--- /dev/null
+ coverage:
+ status:
+ project:
+ default:
+ target: 0%
+ patch:
+ default:
+ target: 0%
--- /dev/null
+ [run]
+ plugins = covimerage
+ data_file = .coverage_covimerage
--- /dev/null
+ name: CI
+ on: [push, pull_request]
+
+ jobs:
+ vint:
+ name: Run vint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ - run: pip install vim-vint
+ - run: vint --warning --verbose --enable-neovim ./autoload ./plugin
+
+ unit-test:
+ name: Unit tests
+ strategy:
+ matrix:
+ os: [macos-latest, ubuntu-latest, windows-latest]
+ neovim: [true, false]
+ fail-fast: false
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v2
+ - name: Checkout themis.vim
+ uses: actions/checkout@v2
+ with:
+ repository: thinca/vim-themis
+ path: vim-themis
+ - uses: rhysd/action-setup-vim@v1
+ id: vim
+ with:
+ neovim: ${{ matrix.neovim }}
+ - name: Run unit tests
+ env:
+ THEMIS_VIM: ${{ steps.vim.outputs.executable }}
+ THEMIS_PROFILE: profile.txt
+ run: ./vim-themis/bin/themis ./test/unit
+ - uses: actions/setup-python@v2
+ if: matrix.os != 'windows-latest'
+ - name: Report coverage
+ if: matrix.os != 'windows-latest'
+ run: |
+ # https://github.com/Vimjas/covimerage/issues/95
+ pip install 'click<8.0.0'
+ pip install covimerage
+ covimerage write_coverage profile.txt
+ coverage report
+ coverage xml
+ - name: Upload coverage to codecov
+ if: matrix.os != 'windows-latest'
+ uses: codecov/codecov-action@v1
+ with:
+ file: ./coverage.xml
+
+ integ-test:
+ name: Integration tests
+ strategy:
+ matrix:
+ neovim: [true, false]
+ fail-fast: false
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Checkout themis.vim
+ uses: actions/checkout@v2
+ with:
+ repository: thinca/vim-themis
+ path: vim-themis
+ - uses: rhysd/action-setup-vim@v1
+ id: vim
+ with:
+ neovim: ${{ matrix.neovim }}
+ - name: Checkout vim-lsp
+ uses: actions/checkout@v2
+ with:
+ repository: prabirshrestha/vim-lsp
+ path: test/integ/deps/vim-lsp
+ - name: Checkout ale
+ uses: actions/checkout@v2
+ with:
+ repository: dense-analysis/ale
+ path: test/integ/deps/ale
+ - name: Install rust-analyzer
+ run: |
+ mkdir ~/bin
+ curl --fail -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz -o rust-analyzer.gz
+ gunzip ./rust-analyzer.gz
+ chmod +x ./rust-analyzer
+ mv ./rust-analyzer ~/bin
+ echo "$HOME/bin" >> $GITHUB_PATH
+ - name: Run integration tests
+ env:
+ THEMIS_VIM: ${{ steps.vim.outputs.executable }}
+ run: ./vim-themis/bin/themis ./test/integ
+ - name: Show runtime information
+ if: ${{ failure() }}
+ run: |
+ echo 'integ_messages.txt'
+ [ -f test/integ/integ_messages.txt ] && cat test/integ/integ_messages.txt
+ echo 'lsp-log.txt'
+ [ -f test/integ/lsp-log.txt ] && cat test/integ/lsp-log.txt
--- /dev/null
+ /doc/tags
--- /dev/null
+ The MIT License (MIT)
+
+ Copyright (c) 2021 rhysd
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+ [vim-lsp][] + [ALE][]
+ =====================
+ [![Build Status][ci-badge]][ci]
+ [![Coverage Status][codecov-badge]][codecov]
+
+ [vim-lsp-ale][] is a Vim plugin for bridge between [vim-lsp][] and [ALE][]. Diagnostics results received
+ by vim-lsp are shown in ALE's interface.
+
+ When simply using ALE and vim-lsp, both plugins run LSP servers respectively. Running multiple server processes
+ consume resources and may cause some issues. And showing lint results from multiple plugins is confusing.
+ vim-lsp-ale solves the problem.
+
+ <img alt="screencast" src="https://github.com/rhysd/ss/blob/master/vim-lsp-ale/main.gif?raw=true" width="582" height="316"/>
+
+ ## Installation
+
+ Install [vim-lsp][], [ale][ALE], [vim-lsp-ale][] with your favorite package manager or `:packadd` in your `.vimrc`.
+
+ An example with [vim-plug](https://github.com/junegunn/vim-plug):
+
+ ```viml
+ Plug 'dense-analysis/ale'
+ Plug 'prabirshrestha/vim-lsp'
+ Plug 'rhysd/vim-lsp-ale'
+ ```
+
+ ## Usage
+
+ Register LSP servers you want to use with `lsp#register_server` and set `vim-lsp` linter to `g:ale_linters`
+ for filetypes you want to check with vim-lsp.
+
+ The following example configures `gopls` to check Go sources.
+
+ ```vim
+ " LSP configurations for vim-lsp
+ if executable('gopls')
+ autocmd User lsp_setup call lsp#register_server({
+ \ 'name': 'gopls',
+ \ 'cmd': ['gopls'],
+ \ 'allowlist': ['go', 'gomod'],
+ \ })
+ endif
+
+ " Set 'vim-lsp' linter
+ let g:ale_linters = {
+ \ 'go': ['golint'], " vim-lsp is implicitly active
+ \ }
+ ```
+
+ This plugin configures vim-lsp and ALE automatically. You don't need to setup various variables.
+
+ When opening a source code including some lint errors, vim-lsp will receive the errors from language server
+ and ALE will report the errors in the buffer.
+
+ ALE supports also many external programs. All errors can be seen in one place. The above example enables
+ vim-lsp and golint.
+
+ For more details, see [the documentation](./doc/vim-lsp-ale.txt).
+
+ ## Testing
+
+ There are unit tests and integration tests. CI runs on GitHub Actions.
+ See [test/README.md](./test/README.md) for more details.
+
+ ## License
+
+ Licensed under [the MIT license](./LICENSE).
+
+ [vim-lsp]: https://github.com/prabirshrestha/vim-lsp
+ [ALE]: https://github.com/dense-analysis/ale
+ [vim-lsp-ale]: https://github.com/rhysd/vim-lsp-ale
+ [ci-badge]: https://github.com/rhysd/vim-lsp-ale/workflows/CI/badge.svg?branch=master&event=push
+ [ci]: https://github.com/rhysd/vim-lsp-ale/actions?query=workflow%3ACI+branch%3Amaster
+ [codecov-badge]: https://codecov.io/gh/rhysd/vim-lsp-ale/branch/master/graph/badge.svg
+ [codecov]: https://codecov.io/gh/rhysd/vim-lsp-ale
--- /dev/null
+ " DiagnosticSeverity
+ let s:ERROR = 1
+ let s:WARN = 2
+ let s:INFO = 3
+ let s:HINT = 4
+
+ let s:Dispose = v:null
+
+ function! s:severity_threshold() abort
+ let s = g:lsp_ale_diagnostics_severity
+ if s ==? 'error'
+ return s:ERROR
+ elseif s ==? 'warning' || s ==? 'warn'
+ return s:WARN
+ elseif s ==? 'information' || s ==? 'info'
+ return s:INFO
+ elseif s ==? 'hint'
+ return s:HINT
+ else
+ throw 'vim-lsp-ale: Unexpected severity "' . s . '". Severity must be one of "error", "warning", "information", "hint"'
+ endif
+ endfunction
+
+ function! s:get_loc_type(severity) abort
+ if a:severity == s:ERROR
+ return 'E'
+ elseif a:severity == s:WARN
+ return 'W'
+ elseif a:severity == s:INFO
+ return 'I'
+ elseif a:severity == s:HINT
+ return 'H'
+ else
+ throw 'vim-lsp-ale: Unexpected severity: ' . a:severity
+ endif
+ endfunction
+
+ let s:prev_num_diags = {}
+ function! lsp#ale#_reset_prev_num_diags() abort
+ let s:prev_num_diags = {}
+ endfunction
+
+ function! s:can_skip_diags(server, uri, diags) abort
+ if !has_key(s:prev_num_diags, a:server)
+ let s:prev_num_diags[a:server] = {}
+ endif
+ let prev = s:prev_num_diags[a:server]
+
+ let num_diags = len(a:diags)
+ if num_diags == 0 && get(prev, a:uri, -1) == 0
+ " Some language servers send diagnostics notifications even if the
+ " results are not changed from previous. It's hard to check the
+ " notifications are perfectly the same as previous. Here only checks
+ " emptiness and skip if both previous ones and current ones are
+ " empty.
+ " I believe programmers usually try to keep no lint errors in the
+ " source code they are writing :)
+ return v:true
+ endif
+
+ let prev[a:uri] = num_diags
+ return v:false
+ endfunction
+
+ function! s:can_skip_all_diags(uri, all_diags) abort
+ for [server, diags] in items(a:all_diags)
+ if !s:can_skip_diags(server, a:uri, diags.params.diagnostics)
+ return v:false
+ endif
+ endfor
+ return v:true
+ endfunction
+
+ function! s:is_active_linter() abort
+ if g:lsp_ale_auto_enable_linter
+ return v:true
+ endif
+ let active_linters = get(b:, 'ale_linters', get(g:ale_linters, &filetype, []))
+ return index(active_linters, 'vim-lsp') >= 0
+ endf
+
+ function! lsp#ale#on_ale_want_results(bufnr) abort
+ " Note: Checking lsp#internal#diagnostics#state#_is_enabled_for_buffer here. If previous lint
+ " errors remain in a buffer, they won't be updated when vim-lsp is disabled for the buffer.
+ if s:Dispose is v:null || !lsp#internal#diagnostics#state#_is_enabled_for_buffer(a:bufnr)
+ return
+ endif
+
+ let uri = lsp#utils#get_buffer_uri(a:bufnr)
+ let all_diags = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri)
+ if empty(all_diags) || s:can_skip_all_diags(uri, all_diags)
+ " Do nothing when no diagnostics results
+ return
+ endif
+
+ if s:is_active_linter()
+ call ale#other_source#StartChecking(a:bufnr, 'vim-lsp')
+ " Avoid the issue that sign and highlight are not set
+ " https://github.com/dense-analysis/ale/issues/3690
+ call timer_start(0, {-> s:notify_diag_to_ale(a:bufnr, all_diags) })
+ endif
+ endfunction
+
+ function! s:notify_diag_to_ale(bufnr, diags) abort
+ try
+ let threshold = s:severity_threshold()
+ let results = []
+ for [server, diag] in items(a:diags)
+ " Note: Do not filter `diag` destructively since the object is also used by vim-lsp
+ let locs = lsp#ui#vim#utils#diagnostics_to_loc_list({'response': diag})
+ let idx = 0
+ for loc in locs
+ let severity = get(diag.params.diagnostics[idx], 'severity', s:ERROR)
+ if severity > threshold
+ continue
+ endif
+ let loc.text = '[' . server . '] ' . loc.text
+ let loc.type = s:get_loc_type(severity)
+ let results += [loc]
+ let idx += 1
+ endfor
+ endfor
+ catch
+ " Since ale#other_source#StartChecking() was already called, ale#other_source#ShowResults()
+ " needs to be called to notify ALE that checking was done.
+ call ale#other_source#ShowResults(a:bufnr, 'vim-lsp', [])
+ let msg = v:exception . ' at ' . v:throwpoint
+ if msg !~# '^vim-lsp-ale: '
+ " Avoid E608 on rethrowing exceptions from Vim script runtime
+ let msg = 'vim-lsp-ale: Error while notifying results to ALE: ' . msg
+ endif
+ throw msg
+ endtry
+ call ale#other_source#ShowResults(a:bufnr, 'vim-lsp', results)
+ endfunction
+
+ function! s:notify_diag_to_ale_for_buf(bufnr) abort
+ if !s:is_active_linter()
+ return
+ endif
+
+ let uri = lsp#utils#get_buffer_uri(a:bufnr)
+ let diags = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri)
+ call s:notify_diag_to_ale(a:bufnr, diags)
+ endfunction
+
+ function! s:on_diagnostics(res) abort
+ let uri = a:res.response.params.uri
+ if s:can_skip_diags(a:res.server, uri, a:res.response.params.diagnostics)
+ return
+ endif
+
+ let path = lsp#utils#uri_to_path(uri)
+ let bufnr = bufnr('^' . path . '$')
+ if bufnr == -1
+ " This branch is reachable when vim-lsp receives some notifications
+ " but the buffer for them was already deleted. This can happen since
+ " notifications are asynchronous
+ return
+ endif
+
+ call ale#other_source#StartChecking(bufnr, 'vim-lsp')
+ " Use timer_start to ensure calling s:notify_diag_to_ale after all
+ " subscribers handled the publishDiagnostics event.
+ " lsp_setup is hooked before vim-lsp sets various internal hooks. So this
+ " function is called before the response is not handled by vim-lsp yet.
+ call timer_start(0, {-> s:notify_diag_to_ale_for_buf(bufnr) })
+ endfunction
+
+ function! s:is_diagnostics_response(item) abort
+ if !has_key(a:item, 'server') || !has_key(a:item, 'response')
+ return v:false
+ endif
+ let res = a:item.response
+ if !has_key(res, 'method')
+ return v:false
+ endif
+ return res.method ==# 'textDocument/publishDiagnostics'
+ endfunction
+
+ function! lsp#ale#enable() abort
+ if s:Dispose isnot v:null
+ return
+ endif
+
+ let s:Dispose = lsp#callbag#pipe(
+ \ lsp#stream(),
+ \ lsp#callbag#filter(funcref('s:is_diagnostics_response')),
+ \ lsp#callbag#subscribe({ 'next': funcref('s:on_diagnostics') }),
+ \ )
+ endfunction
+
+ function! lsp#ale#disable() abort
+ if s:Dispose is v:null
+ return
+ endif
+ call s:Dispose()
+ let s:Dispose = v:null
+ endfunction
+
+ function! lsp#ale#enabled() abort
+ return s:Dispose isnot v:null
+ endfunction
--- /dev/null
+ *vim-lsp-ale.txt* Bridge between vim-lsp and ALE
+
+ Author: rhysd <https://rhysd.github.io>
+
+ CONTENTS *vim-lsp-ale-contents*
+
+ Introduction |vim-lsp-ale-introduction|
+ Install |vim-lsp-ale-install|
+ Usage |vim-lsp-ale-usage|
+ Variables |vim-lsp-ale-variables|
+ Functions |vim-lsp-ale-functions|
+ Issues |vim-lsp-ale-issues|
+ License |vim-lsp-ale-license|
+
+
+ ==============================================================================
+ INTRODUCTION *vim-lsp-ale-introduction*
+
+ *vim-lsp-ale* is a plugin for bridge between |vim-lsp| and |ale|.
+
+ When using ALE and vim-lsp, both plugins run language server process
+ respectively. It's resource consuming and may cause some issues due to
+ multiple server processes running at the same time.
+
+ |vim-lsp-ale| solves the problem by getting diagnostics results from vim-lsp
+ and by sending them to ALE. It means vim-lsp can be handled as one of linters
+ supported by ALE.
+
+ It's also possible to disable LSP features in ALE and use both ALE and
+ vim-lsp's |:LspDocumentDiagnostics| together. But managing linter results
+ with multiple plugins is complicated and confusing since behaviors of each
+ plugins are not persistent.
+
+ Screencast: https://github.com/rhysd/ss/blob/master/vim-lsp-ale/main.gif
+
+ ==============================================================================
+ INSTALL *vim-lsp-ale-install*
+
+ Install |ale|, |vim-lsp| and |vim-lsp-ale| with your favorite plugin manager
+ or using |:packadd|.
+ The following is an example using vim-plug.
+
+ >
+ Plug 'dense-analysis/ale'
+ Plug 'prabirshrestha/vim-lsp'
+ Plug 'rhysd/vim-lsp-ale'
+ <
+ Repositories:
+
+ - ALE: https://github.com/dense-analysis/ale/
+ - vim-lsp: https://github.com/prabirshrestha/vim-lsp
+ - vim-lsp-ale: https://github.com/rhysd/vim-lsp-ale
+
+
+ ==============================================================================
+ USAGE *vim-lsp-ale-usage*
+
+ Register LSP servers you want to use with |lsp#register_server()| and set
+ "vim-lsp" linter to |g:ale_linters| for filetypes you want to check with
+ vim-lsp.
+
+ The following example configures gopls and golint to check Go sources. ALE
+ will automatically reports diagnostics results from gopls and lint errrors
+ from golint when you're writing Go source code.
+ >
+ if executable('gopls')
+ autocmd User lsp_setup call lsp#register_server({
+ \ 'name': 'gopls',
+ \ 'cmd': ['gopls'],
+ \ 'allowlist': ['go', 'gomod'],
+ \ })
+ endif
+ let g:ale_linters = {
+ \ 'go': ['vim-lsp', 'golint'],
+ \ }
+ <
+ gopls: https://github.com/golang/tools/tree/master/gopls
+
+ vim-lsp-ale configures vim-lsp and ALE automatically. You don't need to setup
+ various variables for them.
+
+ vim-lsp-ale automatically does:
+
+ - disable showing diagnostics results from vim-lsp since ALE will show the
+ results
+ - disable LSP support of ALE since vim-lsp handles all LSP requests/responses
+
+ If you don't want them to be done automatically, see
+ |g:lsp_ale_auto_config_vim_lsp| and |g:lsp_ale_auto_config_ale|
+
+
+ ==============================================================================
+ VARIABLES *vim-lsp-ale-variables*
+
+ Behavior of vim-lsp-ale can be customized with some global variables.
+
+ ------------------------------------------------------------------------------
+ *g:lsp_ale_auto_config_vim_lsp* (Default: |v:true|)
+
+ When |v:true| is set, vim-lsp-ale automatically sets several variables
+ for configuring vim-lsp not to show diagnostics results in vim-lsp side.
+
+ At the time of writing, setting |v:true| is the same as:
+ >
+ let g:lsp_diagnostics_enabled = 1
+ let g:lsp_diagnostics_echo_cursor = 0
+ let g:lsp_diagnostics_float_cursor = 0
+ let g:lsp_diagnostics_highlights_enabled = 0
+ let g:lsp_diagnostics_signs_enabled = 0
+ let g:lsp_diagnostics_virtual_text_enabled = 0
+ <
+ When |v:false| is set, vim-lsp-ale does not set any variables to configure
+ vim-lsp so that you can configure them by yourself.
+
+ ------------------------------------------------------------------------------
+ *g:lsp_ale_auto_config_ale* (Default: |v:true|)
+
+ When |v:true| is set, vim-lsp-ale automatically sets several variables
+ for configuring ALE not to start LSP server process.
+
+ At the time of writing, setting |v:true| is the same as:
+ >
+ let g:ale_disable_lsp = 1
+ <
+ When |v:false| is set, vim-lsp-ale does not set any variables to configure
+ ALE so that you can configure them by yourself.
+
+ ------------------------------------------------------------------------------
+ *g:lsp_ale_auto_enable_linter* (Default: |v:true|)
+
+ When |v:true| is set, vim-lsp-ale automatically enables itself as a linter for
+ all filetypes. It does not modify |g:ale_linters|.
+
+ When |v:false| is set, vim-lsp-ale is only active when configured as a linter
+ for a filetype:
+ >
+ let g:ale_linters = {
+ \ 'go': ['vim-lsp'],
+ \ 'lua': ['vim-lsp'],
+ \ 'python': ['vim-lsp'],
+ \ }
+ <
+ ------------------------------------------------------------------------------
+ *g:lsp_ale_diagnostics_severity* (Default: "information")
+
+ Severity level of reported diagnostics results. Possible values are "error",
+ "warning", "information", "hint". Diagnostics results will be filtered by the
+ severity set to this variable.
+ For example, when "warning" is set, "error"/"warning" results are shown
+ and "information"/"hint" results are not shown.
+
+
+ ==============================================================================
+ FUNCTIONS *vim-lsp-ale-functions*
+
+ ------------------------------------------------------------------------------
+ lsp#ale#enable() *lsp#ale#enable*
+
+ Enables bridge between vim-lsp and ALE. This function is automatically called
+ when |lsp_setup| autocmd event is triggered by vim-lsp. So basically you don't
+ need to call this function.
+
+ ------------------------------------------------------------------------------
+ lsp#ale#disable() *lsp#ale#disable*
+
+ Disables bridge between vim-lsp and ALE. After this function is called,
+ diagnostics results will no longer be sent to ALE until |lsp#ale#enable| is
+ called again.
+
+ ------------------------------------------------------------------------------
+ lsp#ale#enabled() *lsp#ale#enabled*
+
+ Returns whether bridge between vim-lsp and ALE is enabled.
+
+
+ ==============================================================================
+ ISSUES *vim-lsp-ale-issues*
+
+ When you find some issues or you have some feature requests to vim-lsp-ale,
+ visit GitHub repository page and make a new issue:
+
+ https://github.com/rhysd/vim-lsp-ale/issues/new
+
+
+ ==============================================================================
+ LICENSE *vim-lsp-ale-license*
+
+ vim-lsp-ale is distributed under the MIT license.
+ >
+ The MIT License (MIT)
+
+ Copyright (c) 2021 rhysd
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ <
+
+ ==============================================================================
+ vim:tw=78:ts=8:ft=help:norl:et:fen:fdl=0:
--- /dev/null
+ if (exists('g:loaded_lsp_ale') && g:loaded_lsp_ale) || &cp
+ finish
+ endif
+ let g:loaded_lsp_ale = 1
+
+ let g:lsp_ale_diagnostics_severity = get(g:, 'lsp_ale_diagnostics_severity', 'information')
+ let g:lsp_ale_auto_enable_linter = get(g:, 'lsp_ale_auto_enable_linter', v:true)
+
+ if get(g:, 'lsp_ale_auto_config_vim_lsp', v:true)
+ " Enable diagnostics and disable all functionalities to show error
+ " messages by vim-lsp
+ let g:lsp_diagnostics_enabled = 1
+ let g:lsp_diagnostics_echo_cursor = 0
+ let g:lsp_diagnostics_float_cursor = 0
+ let g:lsp_diagnostics_highlights_enabled = 0
+ let g:lsp_diagnostics_signs_enabled = 0
+ let g:lsp_diagnostics_virtual_text_enabled = 0
+ endif
+ if get(g:, 'lsp_ale_auto_config_ale', v:true)
+ " Disable ALE's LSP integration
+ let g:ale_disable_lsp = 1
+ endif
+
+ augroup plugin-lsp-ale
+ autocmd!
+ autocmd User lsp_setup call lsp#ale#enable()
+ autocmd User ALEWantResults call lsp#ale#on_ale_want_results(g:ale_want_results_buffer)
+ augroup END
--- /dev/null
+ Tests
+ =====
+
+ ## Directory structure
+
+ - [`test/unit`](./unit): Unit tests
+ - [`test/unit/test.vimspec`](./unit/test.vimspec): Unit test cases
+ - [`test/unit/runtime`](./unit/runtime): Runtime directory loaded on running unit tests. They mocks several external APIs like `ale#*` or `lsp#*`
+ - [`test/integ`](./integ): Integration tests
+ - [`test/integ/test.vimspec`](./integ/test.vimspec): Integration test cases
+ - [`test/integ/deps`](./integ/deps): Dependant plugins
+
+ ## Unit tests
+
+ Unit tests confirm vim-lsp-ale works as intended.
+
+ ### Prerequisites
+
+ Unit tests can be run with no dependency because they mock every external API.
+
+ [vim-themis](https://github.com/thinca/vim-themis) is used as test runner.
+
+ By default, it runs tests with `vim` command. When running tests with Neovim, set `THEMIS_VIM=nvim` environment variable.
+
+ ### Run unit tests
+
+ ```sh
+ cd path/to/vim-lsp-ale
+ git clone https://github.com/thinca/vim-themis.git
+
+ # Run tests with Vim
+ ./vim-themis/bin/themis ./test/unit/
+
+ # Run tests with NeoVim
+ THEMIS_VIM=nvim ./vim-themis/bin/themis ./test/unit/
+ ```
+
+ ### Measure unit test coverage
+
+ [covimerage](https://github.com/Vimjas/covimerage) is used to measure test coverage. Install it by `pip install covimerage`.
+
+ Set a file path to `THEMIS_PROFILE` environment variable and run unit tests. Vim will store the profile data to the file.
+ `covimerage` command will convert the profile data into coverage data for `coverage` command provided by Python.
+
+ ```sh
+ cd path/to/vim-lsp-ale
+ git clone https://github.com/thinca/vim-themis.git
+
+ # Run test case with $THEMIS_PROFILE environment variable
+ THEMIS_PROFILE=profile.txt ./vim-themis/bin/themis ./test/unit
+
+ # Store coverage data at .coverage_covimerage converted from the profile data
+ covimerage write_coverage profile.txt
+
+ # Show coverage report by `coverage` command
+ coverage report
+ ```
+
+ ## Integration tests
+
+ Integration tests confirm integrity among vim-lsp, ALE, vim-lsp-ale and a language server.
+
+ ### Prerequisites
+
+ Integration tests require all dependencies are installed in [deps](./integ/deps) directory.
+
+ ```sh
+ cd path/to/vim-lsp-ale
+ git clone https://github.com/prabirshrestha/vim-lsp.git test/integ/deps/vim-lsp
+ git clone https://github.com/dense-analysis/ale.git test/integ/deps/ale
+ ```
+
+ [rust-analyzer](https://rust-analyzer.github.io/) is used as language server to run integration tests.
+ Download the binary following [the instruction](https://rust-analyzer.github.io/manual.html#rust-analyzer-language-server-binary)
+ and put the binary in `$PATH` directory.
+
+ And [vim-themis](https://github.com/thinca/vim-themis) is used as test runner.
+
+ Note that integration tests were not confirmed on Windows.
+
+ ### Run integration tests
+
+ ```sh
+ cd path/to/vim-lsp-ale
+ git clone https://github.com/thinca/vim-themis.git
+ ./vim-themis/bin/themis ./test/integ/
+ ```
+
+ ### Log files
+
+ When some integration tests fail, the following log files would be useful to investigate the failure.
+
+ - `test/integ/integ_messages.txt`: Messages in `:message` area while running the tests
+ - `test/integ/lsp-log.txt`: Log information of vim-lsp. It records communications between vim-lsp and a language server
+
+ ## CI
+
+ Tests are run continuously on GitHub Actions.
+
+ https://github.com/rhysd/vim-lsp-ale/actions?query=workflow%3ACI
+
+ - Unit tests are run on Linux, macOS and Windows with Vim and Neovim
+ - Integration tests are run on Linux with Vim and Neovim
--- /dev/null
+ /deps/vim-lsp
+ /deps/ale
+ /lsp-log.txt
+ /integ_messages.txt
--- /dev/null
+ call themis#option('exclude', 'test/README.md')
+
+ let s:dir = expand('<sfile>:p:h')
+ let s:sep = has('win32') ? '\' : '/'
+ let s:vim_lsp_dir = join([s:dir, 'deps', 'vim-lsp'], s:sep)
+ let s:ale_dir = join([s:dir, 'deps', 'ale'], s:sep)
+
+ if !isdirectory(s:vim_lsp_dir)
+ throw 'vim-lsp is not cloned at ' . s:vim_lsp_dir
+ endif
+
+ if !isdirectory(s:ale_dir)
+ throw 'ALE is not cloned at ' . s:ale_dir
+ endif
+
+ function! IntegTestRootDir() abort
+ return s:dir
+ endfunction
+
+ execute 'set rtp+=' . s:vim_lsp_dir
+ execute 'set rtp+=' . s:ale_dir
+ filetype plugin indent on
+
+ let g:lsp_log_file = 'lsp-log.txt'
+ autocmd User lsp_setup call lsp#register_server({
+ \ 'name': 'rust-analyzer',
+ \ 'cmd': { server_info -> ['rust-analyzer'] },
+ \ 'allowlist': ['rust'],
+ \ })
+
+ let g:ale_linters = { 'rust': ['vim-lsp'] }
+
+ runtime plugin/lsp_ale.vim
+ runtime plugin/lsp.vim
+ runtime plugin/ale.vim
+
+ " This is called automatically at VimEnter, but our tests load vim-lsp
+ " after the event. So manually call it here
+ call lsp#enable()
+
+ let s:helper = themis#helper('assert')
+ call themis#helper('command').with(s:helper)
+
+ " vim: set ft=vim:
--- /dev/null
--- /dev/null
--- /dev/null
+ /target
+ **/*.rs.bk
--- /dev/null
+ # This file is automatically @generated by Cargo.
+ # It is not intended for manual editing.
+ [[package]]
+ name = "project"
+ version = "0.1.0"
--- /dev/null
+ [package]
+ name = "project"
+ version = "0.1.0"
+ authors = ["rhysd <lin90162@yahoo.co.jp>"]
+ edition = "2018"
+
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+ [dependencies]
--- /dev/null
+ pub fn do_something() {
+ let this_variable_is_unused = 42;
+ }
--- /dev/null
+ let s:SEP = has('win32') ? '\' : '/'
+
+ function! s:get_debug_info(bufnr) abort
+ let uri = lsp#utils#get_buffer_uri(a:bufnr)
+ let all_diags = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri)
+ return "\nall diags: " . string(all_diags)
+ \ . "\nlocation list: " . string(ale#engine#GetLoclist(a:bufnr))
+ \ . "\nserver_status: " . lsp#get_server_status()
+ \ . "\ncurrent lines: " . string(getline(1, '$'))
+ endfunction
+
+ Describe rust-analyzer
+ Before all
+ if !executable('rust-analyzer')
+ throw 'rust-analyzer command is not found. It must be installed for running integration tests'
+ endif
+
+ let dir = IntegTestRootDir()
+ execute 'cd' dir
+ let file = join([dir, 'project', 'src', 'lib.rs'], s:SEP)
+
+ " Note: It might be better to write lib.rs here and delete in `After all` hook rather than
+ " modifying a file committed to repository directly.
+ let lib_rs_contents = readfile(file)
+ End
+
+ After all
+ " Restore contents of lib.rs since it was modified by test case
+ call writefile(lib_rs_contents, file)
+
+ redir! > integ_messages.txt
+ if exists(':LspStatus')
+ LspStatus
+ else
+ echom 'No :LspStatus command is defined'
+ endif
+ message
+ redir END
+ End
+
+ Before each
+ execute 'edit!' file
+ End
+
+ After each
+ bwipeout!
+ End
+
+ It shows diagnostics results with ALE through vim-lsp
+ Assert lsp#ale#enabled()
+
+ let bufnr = bufnr('')
+
+ let elapsed = 0 " in seconds
+ let timeout = 120 " in seconds
+ let counts = ale#statusline#Count(bufnr)
+ while elapsed <= timeout
+ if counts.total > 0
+ break
+ endif
+ sleep 1
+ let elapsed += 1
+ let counts = ale#statusline#Count(bufnr)
+ endwhile
+
+ let info = s:get_debug_info(bufnr)
+ Assert True(counts.total > 0, 'No error found after ' . elapsed . ' seconds' . info)
+
+ let loclist = ale#engine#GetLoclist(bufnr)
+ Assert NotEmpty(loclist, 'Location list from ALE is empty after ' . elapsed . ' seconds. ' . info)
+
+ let item = loclist[0]
+ let item_str = string(item)
+ Assert Equals(item.linter_name, 'vim-lsp', item_str . info)
+ Assert True(item.from_other_source, item_str . info)
+ Assert Match(item.filename, 'lib\.rs$', item_str . info)
+ Assert Match(item.text, 'this_variable_is_unused', item_str . info)
+
+ " Fix the problem
+ normal! ggjdd
+ write
+
+ let elapsed = 0 " in seconds
+ let counts = ale#statusline#Count(bufnr)
+ while elapsed <= timeout
+ if counts.total == 0
+ break
+ endif
+ sleep 1
+ let elapsed += 1
+ let counts = ale#statusline#Count(bufnr)
+ endwhile
+
+ let info = s:get_debug_info(bufnr)
+ Assert True(counts.total == 0, 'Error found after ' . elapsed . ' seconds' . info)
+
+ " Check the error was removed from location list since it'd been fixed
+ let loclist = ale#engine#GetLoclist(bufnr)
+ Assert Empty(loclist, 'Location list from ALE is not empty after ' . elapsed . ' seconds. ' . info)
+ End
+ End
+
+ " vim: set ft=vim:
--- /dev/null
+ call themis#option('exclude', 'test/README.md')
+
+ let s:dir = expand('<sfile>:p:h')
+ let s:sep = has('win32') ? '\' : '/'
+ let s:runtime_dir = join([s:dir, 'runtime'], s:sep)
+
+ execute 'set rtp+=' . s:runtime_dir
+
+ let s:helper = themis#helper('assert')
+ call themis#helper('command').with(s:helper)
+
+ if $THEMIS_PROFILE !=# ''
+ execute 'profile' 'start' $THEMIS_PROFILE
+ profile! file ./autoload/lsp/ale.vim
+ profile! file ./plugin/lsp_ale.vim
+ endif
+
+ " vim: set ft=vim:
--- /dev/null
+ function! ale#other_source#StartChecking(bufnr, name) abort
+ let s:start_checking_called = [a:bufnr, a:name]
+ endfunction
+
+ function! ale#other_source#ShowResults(bufnr, name, results) abort
+ let s:show_results_called = [a:bufnr, a:name, a:results]
+ endfunction
+
+ function! ale#other_source#last_start_checking() abort
+ return s:start_checking_called
+ endfunction
+
+ function! ale#other_source#last_show_results() abort
+ return s:show_results_called
+ endfunction
+
+ function! WaitUntil(func, ...) abort
+ let timeout = get(a:, 1, 1) " 1sec by default
+ let total = 0
+ while !a:func()
+ sleep 100m
+ let total += 0.1
+ if total >= timeout
+ " Note: v:true/v:false are not supported by themis.vim
+ " https://github.com/thinca/vim-themis/pull/56
+ return 0
+ endif
+ endwhile
+ return 1
+ endfunction
+
+ function! ale#other_source#wait_until_show_results() abort
+ let timeout = 1
+ let total = 0
+ while s:show_results_called is v:null
+ let total += 0.1
+ if total > timeout
+ throw 'ale#other_source#ShowResults() was not called while 1 second'
+ endif
+ sleep 100m
+ endwhile
+ endfunction
+
+ function! ale#other_source#check_show_no_result() abort
+ let timeout = 1
+ let total = 0
+ while s:show_results_called is v:null
+ let total += 0.1
+ if total > timeout
+ return
+ endif
+ sleep 100m
+ endwhile
+ throw 'ale#other_source#ShowResults() was called within 1 second: ' . string(s:show_results_called)
+ endfunction
+
+ function! ale#other_source#reset() abort
+ let s:start_checking_called = v:null
+ let s:show_results_called = v:null
+ endfunction
+
+ call ale#other_source#reset()
--- /dev/null
+ function! lsp#stream() abort
+ endfunction
--- /dev/null
+ function! lsp#callbag#pipe(source, filter, sink) abort
+ let s:Filter = a:filter
+ let s:Next = a:sink.next
+ return {-> extend(s:, {'disposed': v:true})}
+ endfunction
+
+ function! lsp#callbag#filter(pred) abort
+ return a:pred
+ endfunction
+
+ function! lsp#callbag#subscribe(sink) abort
+ return a:sink
+ endfunction
+
+ " Functions for tests
+
+ function! lsp#callbag#piped() abort
+ return s:Filter isnot v:null && s:Next isnot v:null
+ endfunction
+
+ function! lsp#callbag#disposed() abort
+ return s:disposed
+ endfunction
+
+ function! lsp#callbag#reset() abort
+ let s:Filter = v:null
+ let s:Next = v:null
+ let s:disposed = v:false
+ endfunction
+
+ function! lsp#callbag#mock_receive(res) abort
+ if s:Filter(a:res)
+ call s:Next(a:res)
+ endif
+ endfunction
--- /dev/null
+ function! lsp#internal#diagnostics#state#_is_enabled_for_buffer(bufnr) abort
+ return a:bufnr == g:lsp_ale_test_mock_bufnr
+ endfunction
+
+ function! lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri) abort
+ return g:lsp_ale_test_mock_diags
+ endfunction
--- /dev/null
+ function! lsp#ui#vim#utils#diagnostics_to_loc_list(res) abort
+ if len(s:loclists) == 0
+ return []
+ endif
+
+ let ret = s:loclists[0]
+ let s:loclists = s:loclists[1:]
+ return ret
+ endfunction
+
+ function! lsp#ui#vim#utils#mock_diagnostics_to_loc_list(loclists) abort
+ let s:loclists = copy(a:loclists)
+ endfunction
+
+ function! lsp#ui#vim#utils#reset() abort
+ let s:loclists = []
+ endfunction
+
+ call lsp#ui#vim#utils#reset()
--- /dev/null
+ function! lsp#utils#get_buffer_uri(bufnr) abort
+ return 'file://' . s:bufname
+ endfunction
+
+ function! lsp#utils#uri_to_path(uri) abort
+ return s:bufname
+ endfunction
+
+ function! lsp#utils#mock_buf_name(name) abort
+ let s:bufname = a:name
+ endfunction
--- /dev/null
+ function! s:test_diags() abort
+ return {
+ \ 'gopls': {
+ \ 'method': 'textDocument/publishDiagnostics',
+ \ 'jsonrpc': '2.0',
+ \ 'params': {
+ \ 'uri': 'file:///path/to/dummy.txt',
+ \ 'diagnostics': [
+ \ {
+ \ 'source': 'compiler',
+ \ 'range': {
+ \ 'end': {'character': 4, 'line': 4},
+ \ 'start': {'character': 1, 'line': 4}
+ \ },
+ \ 'message': 'error message 1',
+ \ 'severity': 1
+ \ },
+ \ {
+ \ 'source': 'compiler',
+ \ 'range': {
+ \ 'end': {'character': 4, 'line': 4},
+ \ 'start': {'character': 1, 'line': 4}
+ \ },
+ \ 'message': 'warning message 1',
+ \ 'severity': 2
+ \ }
+ \ ]
+ \ }
+ \ }
+ \}
+ endfunction
+
+ function! s:test_locs() abort
+ return [[
+ \ {
+ \ 'lnum': 5,
+ \ 'col': 2,
+ \ 'filename': '/path/to/dummy.txt',
+ \ 'text': 'compiler:Error:error message 1'
+ \ },
+ \ {
+ \ 'lnum': 5,
+ \ 'col': 2,
+ \ 'filename': '/path/to/dummy.txt',
+ \ 'text': 'compiler:Warning:warning message 1'
+ \ }
+ \ ]]
+ endfunction
+
+ function! s:test_diags_all_severities() abort
+ let diags = s:test_diags()
+ let diag = diags.gopls.params.diagnostics[0]
+ let diags.gopls.params.diagnostics = []
+ for [sev, name] in [[1, 'error'], [2, 'warning'], [3, 'info'], [4, 'hint']]
+ let d = copy(diag)
+ let d.severity = sev
+ let d.message = name . ' message'
+ let diags.gopls.params.diagnostics += [d]
+ endfor
+ return diags
+ endfunction
+
+ function! s:test_locs_all_severities() abort
+ let loc = s:test_locs()[0][0]
+ let locs = []
+ for [sev, name] in [[1, 'Error'], [2, 'Warning'], [3, 'Info'], [4, 'Hint']]
+ let l = copy(loc)
+ let l.text = 'compiler:' . name . ':' . tolower(name) . ' message'
+ let locs += [l]
+ endfor
+ return [locs]
+ endfunction
+
+ function! s:test_expected_locs_all_severities() abort
+ let loc = s:test_locs()[0][0]
+ let locs = []
+ for [sev, name, type] in [[1, 'Error', 'E'], [2, 'Warning', 'W'], [3, 'Info', 'I'], [4, 'Hint', 'H']]
+ let l = copy(loc)
+ let l.type = type
+ let l.text = '[gopls] compiler:' . name . ':' . tolower(name) . ' message'
+ let locs += [l]
+ endfor
+ return locs
+ endfunction
+
+ function! s:modify_loc_item(item, type) abort
+ let a:item.type = a:type
+ let a:item.text = '[gopls] ' . a:item.text
+ return a:item
+ endfunction
+
+ Describe vim-lsp-ale
+ Before all
+ " Set bufffer name to 'foo'
+ file /path/to/dummy.txt
+ End
+
+ Before each
+ call lsp#ale#_reset_prev_num_diags()
+ call lsp#callbag#reset()
+ call ale#other_source#reset()
+ call lsp#ui#vim#utils#reset()
+ call lsp#utils#mock_buf_name('/path/to/dummy.txt')
+ let g:lsp_ale_test_mock_diags = {}
+ let g:lsp_ale_test_mock_bufnr = bufnr('')
+ doautocmd User lsp_setup
+ let g:ale_want_results_buffer = bufnr('')
+ End
+
+ After each
+ call lsp#ale#disable()
+ End
+
+ It enables vim-lsp's diagnostics and disables to output diagnostics
+ Assert True(g:lsp_diagnostics_enabled)
+ Assert False(g:lsp_diagnostics_highlights_enabled)
+ Assert False(g:lsp_diagnostics_signs_enabled)
+ Assert False(g:lsp_diagnostics_echo_cursor)
+ Assert False(g:lsp_diagnostics_virtual_text_enabled)
+ End
+
+ It disables ALE's LSP support
+ Assert True(g:ale_disable_lsp)
+ End
+
+ It defines plugin-lsp-ale autocmd group
+ Assert True(exists('g:loaded_lsp_ale'))
+
+ redir => autocmds
+ autocmd plugin-lsp-ale
+ redir END
+
+ Assert True(stridx(autocmds, 'lsp_setup') >= 0, autocmds)
+ Assert True(stridx(autocmds, 'ALEWantResults') >= 0, autocmds)
+ End
+
+ It subscribes notification stream on lsp_setup autocmd event
+ Assert True(lsp#callbag#piped())
+ End
+
+ It stops subscribing stream when lsp#ale#disable() is called
+ call lsp#ale#enable()
+ Assert True(lsp#ale#enabled())
+ call lsp#ale#disable()
+ Assert False(lsp#ale#enabled())
+ Assert True(lsp#callbag#disposed())
+ call lsp#ale#disable()
+ Assert False(lsp#ale#enabled())
+ End
+
+ Context ALEWantResults
+ It does not notify results when vim-lsp-ale is disabled
+ call lsp#ale#disable()
+ doautocmd User ALEWantResults
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+
+ It does not notify results when the buffer disables LSP
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+
+ let g:lsp_ale_test_mock_bufnr = -1
+ doautocmd User ALEWantResults
+
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+
+ It does not notify on no diagnostics error
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ Assert Equals(ale#other_source#last_show_results(), v:null)
+
+ let g:lsp_ale_test_mock_diags = {}
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+
+ call ale#other_source#check_show_no_result()
+
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ Assert Equals(ale#other_source#last_show_results(), v:null)
+ End
+
+ It notifies location list items converted from diagnostics results
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ Assert Equals(ale#other_source#last_show_results(), v:null)
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let g:lsp_ale_test_mock_diags = s:test_diags()
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+
+ let [show_bufnr, show_name, loclist] = ale#other_source#last_show_results()
+ Assert Equals(show_bufnr, bufnr)
+ Assert Equals(show_name, 'vim-lsp')
+
+ let expected_locs = s:test_locs()[0]
+ call s:modify_loc_item(expected_locs[0], 'E')
+ call s:modify_loc_item(expected_locs[1], 'W')
+
+ Assert Equals(loclist, expected_locs)
+ End
+
+ It filters diagnostics results by severity
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ Assert Equals(ale#other_source#last_show_results(), v:null)
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let actual_diags = s:test_diags()
+ " Set 'hint' severity. Default threshold is 'info'
+ let actual_diags.gopls.params.diagnostics[1].severity = 4
+ let g:lsp_ale_test_mock_diags = actual_diags
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+
+ let [show_bufnr, show_name, loclist] = ale#other_source#last_show_results()
+ Assert Equals(show_bufnr, bufnr)
+ Assert Equals(show_name, 'vim-lsp')
+
+ Assert Equals(len(loclist), 1, string(loclist))
+ End
+
+ It skips notifying results to ALE when no error continues to happen
+ let bufnr = bufnr('')
+
+ " Prepare empty results
+ let diags = s:test_diags()
+ let diags.gopls.params.diagnostics = []
+ let g:lsp_ale_test_mock_diags = diags
+
+ " First notification
+ doautocmd User ALEWantResults
+
+ call ale#other_source#wait_until_show_results()
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', []])
+
+ call ale#other_source#reset()
+
+ " Second notification
+ doautocmd User ALEWantResults
+
+ call ale#other_source#check_show_no_result()
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+ End
+
+ Context textDocument/publishDiagnostics notification
+ It notifies diagnostics results to ALE
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ Assert Equals(ale#other_source#last_show_results(), v:null)
+
+ let bufnr = bufnr('')
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let g:lsp_ale_test_mock_diags = s:test_diags()
+
+ let response = { 'response': s:test_diags()['gopls'], 'server': 'gopls' }
+ call lsp#callbag#mock_receive(response)
+
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+
+ let [show_bufnr, show_name, loclist] = ale#other_source#last_show_results()
+ Assert Equals(show_bufnr, bufnr)
+ Assert Equals(show_name, 'vim-lsp')
+
+ let expected_locs = s:test_locs()[0]
+ call s:modify_loc_item(expected_locs[0], 'E')
+ call s:modify_loc_item(expected_locs[1], 'W')
+
+ Assert Equals(loclist, expected_locs)
+ End
+
+ It does nothing when receiving notification other than textDocument/publishDiagnostics
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let g:lsp_ale_test_mock_diags = s:test_diags()
+ let response = {
+ \ 'server': 'gopls',
+ \ 'response': {
+ \ 'method': 'something/doSomethihg',
+ \ 'jsonrpc': '2.0',
+ \ 'params': {},
+ \ }
+ \ }
+ call lsp#callbag#mock_receive(response)
+
+ call ale#other_source#check_show_no_result()
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+
+ It does nothing when method or server is missing in the notification
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let g:lsp_ale_test_mock_diags = s:test_diags()
+
+ for response in [
+ \ {
+ \ 'response': {
+ \ 'method': 'something/doSomethihg',
+ \ 'jsonrpc': '2.0',
+ \ 'params': {},
+ \ }
+ \ },
+ \ {
+ \ 'server': 'gopls',
+ \ 'response': {
+ \ 'jsonrpc': '2.0',
+ \ 'params': {},
+ \ }
+ \ }
+ \ ]
+ call lsp#callbag#mock_receive(response)
+ endfor
+
+ call ale#other_source#check_show_no_result()
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+
+ It does nothing when received notification is for buffer which doesn't exist
+ let bufnr = bufnr('')
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let g:lsp_ale_test_mock_diags = s:test_diags()
+ call lsp#utils#mock_buf_name('/path/to/somewhere/else.txt')
+
+ let response = { 'response': s:test_diags()['gopls'], 'server': 'gopls' }
+ call lsp#callbag#mock_receive(response)
+
+ call ale#other_source#check_show_no_result()
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+
+ It notifies empty list when notification says no lint error was found
+ let bufnr = bufnr('')
+ let response = { 'response': s:test_diags()['gopls'], 'server': 'gopls' }
+ let response.response.params.diagnostics = []
+
+ call lsp#callbag#mock_receive(response)
+
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', []])
+ End
+
+ It skips sending results to ALE when no error continues to happen
+ let bufnr = bufnr('')
+ let diags = s:test_diags()
+ let diags.gopls.params.diagnostics = []
+
+ " First notification
+ let response = { 'response': diags.gopls, 'server': 'gopls' }
+ call lsp#callbag#mock_receive(response)
+
+ call ale#other_source#wait_until_show_results()
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', []])
+
+ call ale#other_source#reset()
+
+ " Second notification
+ call lsp#callbag#mock_receive(response)
+
+ call ale#other_source#check_show_no_result()
+ Assert Equals(ale#other_source#last_start_checking(), v:null)
+ End
+ End
+
+ Describe g:lsp_ale_diagnostics_severity
+ Before
+ let saved_diagnostics_severity = g:lsp_ale_diagnostics_severity
+ End
+
+ After
+ let g:lsp_ale_diagnostics_severity = saved_diagnostics_severity
+ End
+
+ It filters results by severity 'error'
+ let g:lsp_ale_diagnostics_severity = 'error'
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs_all_severities())
+ let g:lsp_ale_test_mock_diags = s:test_diags_all_severities()
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ let expected = s:test_expected_locs_all_severities()
+ call filter(expected, {_, l -> l.type ==# 'E'})
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', expected])
+ End
+
+ It filters results by severity 'warning'
+ let g:lsp_ale_diagnostics_severity = 'warning'
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs_all_severities())
+ let g:lsp_ale_test_mock_diags = s:test_diags_all_severities()
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ let expected = s:test_expected_locs_all_severities()
+ call filter(expected, {_, l -> l.type =~# 'E\|W'})
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', expected])
+ End
+
+ It filters results by severity 'information'
+ let g:lsp_ale_diagnostics_severity = 'information'
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs_all_severities())
+ let g:lsp_ale_test_mock_diags = s:test_diags_all_severities()
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ let expected = s:test_expected_locs_all_severities()
+ call filter(expected, {_, l -> l.type =~# 'E\|W\|I'})
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', expected])
+ End
+
+ It filters results by severity 'hint'
+ let g:lsp_ale_diagnostics_severity = 'hint'
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs_all_severities())
+ let g:lsp_ale_test_mock_diags = s:test_diags_all_severities()
+
+ let bufnr = bufnr('')
+ doautocmd User ALEWantResults
+ call ale#other_source#wait_until_show_results()
+
+ Assert Equals(ale#other_source#last_start_checking(), [bufnr, 'vim-lsp'])
+ let expected = s:test_expected_locs_all_severities()
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', expected])
+ End
+
+ It throws an error when invalid value is set
+ let g:lsp_ale_diagnostics_severity = 'invalid!'
+ let bufnr = bufnr('')
+
+ call lsp#ui#vim#utils#mock_diagnostics_to_loc_list(s:test_locs())
+ let g:lsp_ale_test_mock_diags = s:test_diags()
+
+ call lsp#callbag#mock_receive({ 'response': s:test_diags().gopls, 'server': 'gopls' })
+
+ if has('nvim')
+ Throws /^vim-lsp-ale: Unexpected severity/ ale#other_source#wait_until_show_results()
+ else
+ " XXX: No way to catch exception thrown while sleeping. Indirectly
+ " check the error was handled correctly by checking the result is
+ " set to empty.
+ call ale#other_source#wait_until_show_results()
+ Assert Equals(ale#other_source#last_show_results(), [bufnr, 'vim-lsp', []])
+ endif
+ End
+ End
+
+ End
+
+ " vim: set ft=vim: