Compare commits

..

4 Commits

Author SHA1 Message Date
w0rp
97b410bbba Merge pull request #342 from Exteris/bugfix/gfortran-6.3.1
Update line marker pattern for new gfortran
2017-02-21 20:38:57 +00:00
Chris Paul
78b05d30a5 escape dot in maker regex (#357)
* escape dot in maker regex

* Create test_typecheck_handler

* Rename test_typecheck_handler to test_typecheck_handler.vader
2017-02-21 20:33:16 +00:00
w0rp
6f6c156995 Fix Pug linting 2017-02-11 20:47:11 +00:00
w0rp
62a45f8ae4 Don't export the custom-checks script in archives 2017-02-09 19:34:31 +00:00
165 changed files with 1099 additions and 5352 deletions

3
.gitattributes vendored
View File

@@ -1,9 +1,8 @@
.* export-ignore
/CONTRIBUTING.md export-ignore
/Dockerfile export-ignore
/ISSUE_TEMPLATE.md export-ignore
/Makefile export-ignore
/README.md export-ignore
/custom-checks export-ignore
/img export-ignore
/test export-ignore
/custom-checks export-ignore

View File

@@ -5,11 +5,6 @@
3. [Creating Pull Requests](#pull-requests)
1. [Adding a New Linter](#adding-a-new-linter)
2. [Adding New Options](#adding-new-options)
4. [Writing Documentation](#writing-documentation)
1. [Documenting New Linters](#documenting-new-linters)
2. [Editing the Online Documentation](#editing-online-documentation)
3. [Documenting Linter Options](#documenting-new-options)
5. [In Case of Busses](#in-case-of-busses)
<a name="guidelines"></a>
@@ -17,12 +12,6 @@
Have fun, and work on whatever floats your boat. Take It Easy :tm:.
Don't forget to **write documentation** for whatever it is you are doing.
See the ["Writing Documentation"](#writing-documentation) section.
Remember to write Vader tests for most of the code you write. You can look at
existing Vader tests in the `test` directory for examples.
When writing code, follow the [Google Vimscript Style
Guide](https://google.github.io/styleguide/vimscriptguide.xml), and run `vint
-s` on your files to check for most of what the guide mentions and more. If you
@@ -101,73 +90,3 @@ global variable names.
Any options for linters should be set to some default value so it is always
easy to see what the default is with `:echo g:ale...`.
<a name="writing-documentation"></a>
# 4. Writing Documentation
If you are adding new linters, changing the API, adding new options, etc., you
_must_ write some documentation describing it in the `doc/ale.txt` file. New
linters _must_ be added to the `README.md` file too, so other users can get a
quick overview of the supported tools.
<a name="documenting-new-linters"></a>
# 4.i Documenting New Linters
If you add a new linter to the project, edit the table in the `README.md` file,
and edit the list of linters at the top of the `doc/ale.txt` file. The linters
should be sorted vertically in lexicographic (alphabetical) order by the
programming language name or filetype, and the tools for each language should
be sorted in lexicographic order horizontally. Sorting in this manner is a fair
manner of presenting all of the information in an easy to scan way, without
giving some unfair preference to any particular tool or language.
<a name="editing-online-documentation"></a>
# 4.ii Editing the Online Documentation
The "online documentation" file used for this project lives in `doc/ale.txt`.
This is the file used for generating `:help` text inside Vim itself. There are
some guidlines to follow for this file.
1. Keep all text within a column size of 79 characters, inclusive.
2. Open a section with 79 `=` or `-` characters, for headings and subheadings.
3. Sections should have a _single_ blank line before or after.
4. Between descriptions of variables/functions/commands, use _two_ blank lines.
5. Up-indent the description of a variable/function/command by two spaces.
6. Place tags at the ends of lines, with the final characters on column 79.
All of the tags should line up perfectly on the same column as you scan
down through the document.
7. Keep the table of contents balanced so the longest tag link ends on column
79, and so all links line up perfectly on their first character, on the
left.
<a name="documenting-linter-options"></a>
# 4.iii Documenting Linter Options
For documenting new linter options, please add a new sub-section under the
"Linter Specific Options" section describing all of the global options added
for each linter, and what the default values of the options are. All global
options for linters should be set to some default value. This will allow users
to look up the default value easily by typing `:echo g:ale_...`.
<a name="in-case-of-busses"></a>
# 5. In Case of Busses
Should the principal author of the ALE project and all collaborators with the
required access needed to properly administrate the project on GitHub or any
other website either perish or disappear, whether by tragic traffic accident
or government adduction, etc., action should be taken to ensure that the
project continues. If no one is left to administer the project where it is
hosted, please fork the project and nominate someone capable to administer it.
Preferably, in such an event, a single fork of the project will replace the
original, and life will go on, except the life of whoever vanished, because
then they will probably be dead.
Should w0rp suddenly disappear, then he was probably killed in a traffic
accident, or the government finally decided to kill him and make it look like
suicide. In the latter event, please subvert said government and restore
order to the universe, and ensure peace for mankind.

View File

@@ -1,10 +0,0 @@
For bugs, paste output from your clipboard after running :ALEInfoToClipboard
here. If that doesn't work for some reason, try running :ALEInfo and copying
the output from that here instead. If everything is broken, run around in
circles and scream.
If you are experiencing a bug where ALE is not correctly parsing the output of
commands, set g:ale_history_log_output to 1, and run ALE again, and then
:ALEInfo should include the full output of each command which ran.
Whatever the case, describe the your issue here.

View File

@@ -2,20 +2,11 @@ SHELL := /usr/bin/env bash
IMAGE ?= w0rp/ale
CURRENT_IMAGE_ID = 107e4efc4267
DOCKER_FLAGS = --rm -v $(PWD):/testplugin -v $(PWD)/test:/home "$(IMAGE)"
tests = test/*.vader test/*/*.vader test/*/*/*.vader test/*/*/*/*.vader
test-setup:
docker images -q w0rp/ale | grep ^$(CURRENT_IMAGE_ID) > /dev/null || \
docker pull $(IMAGE)
vader: test-setup
@:; \
vims=$$(docker run --rm $(IMAGE) ls /vim-build/bin | grep -E '^n?vim'); \
if [ -z "$$vims" ]; then echo "No Vims found!"; exit 1; fi; \
for vim in $$vims; do \
docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! $(tests)'; \
done
test: test-setup
@:; \
vims=$$(docker run --rm $(IMAGE) ls /vim-build/bin | grep -E '^n?vim'); \
@@ -27,7 +18,7 @@ test: test-setup
echo "Running tests for $$vim"; \
echo '========================================'; \
echo; \
docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! $(tests)' || EXIT=$$?; \
docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! test/*' || EXIT=$$?; \
done; \
echo; \
echo '========================================'; \

132
README.md
View File

@@ -23,8 +23,7 @@ In other words, this plugin allows you to lint while you type.
1. [Installation with Pathogen](#installation-with-pathogen)
2. [Installation with Vundle](#installation-with-vundle)
3. [Manual Installation](#manual-installation)
4. [Contributing](#contributing)
5. [FAQ](#faq)
4. [FAQ](#faq)
1. [How do I disable particular linters?](#faq-disable-linters)
2. [How can I keep the sign gutter open?](#faq-disable-linters)
3. [How can I change the signs ALE uses?](#faq-change-signs)
@@ -34,8 +33,6 @@ In other words, this plugin allows you to lint while you type.
7. [How can I navigate between errors quickly?](#faq-navigation)
8. [How can I run linters only when I save files?](#faq-lint-on-save)
9. [How can I use the quickfix list instead of the loclist?](#faq-quickfix)
10. [How can I check JSX files with both stylelint and eslint?](#faq-jsx-stylelint-eslint)
11. [Will this plugin eat all of my laptop battery power?](#faq-my-battery-is-sad)
<a name="supported-languages"></a>
@@ -53,14 +50,12 @@ name. That seems to be the fairest way to arrange this table.
| Language | Tools |
| -------- | ----- |
| Ansible | [ansible-lint](https://github.com/willthames/ansible-lint) |
| AsciiDoc | [proselint](http://proselint.com/)|
| Bash | [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/) |
| Bourne Shell | [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/) |
| C | [cppcheck](http://cppcheck.sourceforge.net), [gcc](https://gcc.gnu.org/), [clang](http://clang.llvm.org/)|
| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/), [cppcheck] (http://cppcheck.sourceforge.net), [gcc](https://gcc.gnu.org/)|
| C++ (filetype cpp) | [cppcheck] (http://cppcheck.sourceforge.net), [gcc](https://gcc.gnu.org/)|
| C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) |
| Chef | [foodcritic](http://www.foodcritic.io/) |
| CMake | [cmakelint](https://github.com/richq/cmake-lint) |
| CoffeeScript | [coffee](http://coffeescript.org/), [coffeelint](https://www.npmjs.com/package/coffeelint) |
| CSS | [csslint](http://csslint.net/), [stylelint](https://github.com/stylelint/stylelint) |
| Cython (pyrex filetype) | [cython](http://cython.org/) |
@@ -71,46 +66,40 @@ name. That seems to be the fairest way to arrange this table.
| Erlang | [erlc](http://erlang.org/doc/man/erlc.html) |
| Fortran | [gcc](https://gcc.gnu.org/) |
| Go | [gofmt -e](https://golang.org/cmd/gofmt/), [go vet](https://golang.org/cmd/vet/), [golint](https://godoc.org/github.com/golang/lint), [go build](https://golang.org/cmd/go/) |
| Haml | [haml-lint](https://github.com/brigade/haml-lint)
| Haskell | [ghc](https://www.haskell.org/ghc/), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools) |
| HTML | [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/) |
| Java | [javac](http://www.oracle.com/technetwork/java/javase/downloads/index.html) |
| JavaScript | [eslint](http://eslint.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/), [flow](https://flowtype.org/), [standard](http://standardjs.com/)
| Haskell | [ghc](https://www.haskell.org/ghc/), [hlint](https://hackage.haskell.org/package/hlint) |
| HTML | [HTMLHint](http://htmlhint.com/), [tidy](http://www.html-tidy.org/) |
| JavaScript | [eslint](http://eslint.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/), [flow](https://flowtype.org/) |
| JSON | [jsonlint](http://zaa.ch/jsonlint/) |
| LaTeX | [chktex](http://www.nongnu.org/chktex/), [lacheck](https://www.ctan.org/pkg/lacheck) |
| Lua | [luacheck](https://github.com/mpeterv/luacheck) |
| Markdown | [mdl](https://github.com/mivok/markdownlint), [proselint](http://proselint.com/)|
| MATLAB | [mlint](https://www.mathworks.com/help/matlab/ref/mlint.html) |
| Nim | [nim](https://nim-lang.org/docs/nimc.html) |
| nix | [nix-instantiate](http://nixos.org/nix/manual/#sec-nix-instantiate) |
| nroff | [proselint](http://proselint.com/)|
| OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-ocaml-merlin` for configuration instructions
| Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic) |
| PHP | [hack](http://hacklang.org/), [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org) |
| Pod | [proselint](http://proselint.com/)|
| PHP | [hack](http://hacklang.org/), [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer) |
| Pug | [pug-lint](https://github.com/pugjs/pug-lint) |
| Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) |
| Python | [flake8](http://flake8.pycqa.org/en/latest/), [mypy](http://mypy-lang.org/), [pylint](https://www.pylint.org/) |
| reStructuredText | [proselint](http://proselint.com/)|
| Ruby | [rubocop](https://github.com/bbatsov/rubocop) |
| Rust | [rustc](https://www.rust-lang.org/), cargo (see `:help ale-integration-rust` for configuration instructions) |
| SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) |
| SCSS | [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint), [stylelint](https://github.com/stylelint/stylelint) |
| Scala | [scalac](http://scala-lang.org) |
| Slim | [slim-lint](https://github.com/sds/slim-lint)
| SML | [smlnj](http://www.smlnj.org/) |
| Swift | [swiftlint](https://swift.org/) |
| Tex | [proselint](http://proselint.com/) |
| Texinfo | [proselint](http://proselint.com/)|
| Text^ | [proselint](http://proselint.com/) |
| Text^^ | [proselint](http://proselint.com/) |
| TypeScript | [tslint](https://github.com/palantir/tslint), typecheck |
| Verilog | [iverilog](https://github.com/steveicarus/iverilog), [verilator](http://www.veripool.org/projects/verilator/wiki/Intro) |
| Vim | [vint](https://github.com/Kuniwak/vint) |
| Vim help^ | [proselint](http://proselint.com/)|
| XHTML | [proselint](http://proselint.com/)|
| YAML | [yamllint](https://yamllint.readthedocs.io/) |
* *^ No linters for text or Vim help filetypes are enabled by default.*
* *^^ No text linters are enabled by default.*
If you would like to see support for more languages and tools, please
[create an issue](https://github.com/w0rp/ale/issues)
or [create a pull request](https://github.com/w0rp/ale/pulls).
If your tool can read from stdin or you have code to suggest which is good,
support can be happily added for it.
<a name="usage"></a>
@@ -200,27 +189,13 @@ silent! helptags ALL
Because the author of this plugin is a weird nerd, this is his preferred
installation method.
<a name="contributing"></a>
## 4. Contributing
If you would like to see support for more languages and tools, please
[create an issue](https://github.com/w0rp/ale/issues)
or [create a pull request](https://github.com/w0rp/ale/pulls).
If your tool can read from stdin or you have code to suggest which is good,
support can be happily added for it.
If you are interested in the general direction of the project, check out the
[wiki home page](https://github.com/w0rp/ale/wiki). The wiki includes a
Roadmap for the future, and more.
<a name="faq"></a>
## 5. FAQ
## 4. FAQ
<a name="faq-disable-linters"></a>
### 5.i. How do I disable particular linters?
### 4.i. How do I disable particular linters?
By default, all available tools for all supported languages will be run.
If you want to only select a subset of the tools, simply create a
@@ -244,7 +219,7 @@ in each directory corresponds to the name of a particular linter.
<a name="faq-keep-signs"></a>
### 5.ii. How can I keep the sign gutter open?
### 4.ii. How can I keep the sign gutter open?
You can keep the sign gutter open at all times by setting the
`g:ale_sign_column_always` to 1
@@ -255,7 +230,7 @@ let g:ale_sign_column_always = 1
<a name="faq-change-signs"></a>
### 5.iii. How can I change the signs ALE uses?
### 4.iii. How can I change the signs ALE uses?
Use these options to specify what text should be used for signs:
@@ -275,7 +250,7 @@ highlight clear ALEWarningSign
<a name="faq-statusline"></a>
### 5.iv. How can I show errors or warnings in my statusline?
### 4.iv. How can I show errors or warnings in my statusline?
You can use `ALEGetStatusLine()` to integrate ALE into vim statusline.
To enable it, you should have in your `statusline` settings
@@ -302,7 +277,7 @@ let g:ale_statusline_format = ['⨉ %d', '⚠ %d', '⬥ ok']
<a name="faq-echo-format"></a>
### 5.v. How can I change the format for echo messages?
### 4.v. How can I change the format for echo messages?
There are 3 global options that allow customizing the echoed message.
@@ -327,7 +302,7 @@ Will give you:
<a name="faq-autocmd"></a>
### 5.vi. How can I execute some code when ALE stops linting?
### 4.vi. How can I execute some code when ALE stops linting?
ALE runs its own [autocmd](http://vimdoc.sourceforge.net/htmldoc/autocmd.html)
event whenever has a linter has been successfully executed and processed. This
@@ -342,7 +317,7 @@ augroup END
<a name="faq-navigation"></a>
### 5.vii. How can I navigate between errors quickly?
### 4.vii. How can I navigate between errors quickly?
ALE offers some commands with `<Plug>` keybinds for moving between warnings and
errors quickly. You can map the keys Ctrl+j and Ctrl+k to moving between errors
@@ -358,7 +333,7 @@ For more information, consult the online documentation with
<a name="faq-lint-on-save"></a>
### 5.viii. How can I run linters only when I save files?
### 4.viii. How can I run linters only when I save files?
ALE offers an option `g:ale_lint_on_save` for enabling running the linters
when files are saved. If you wish to run linters when files are saved, not
@@ -376,7 +351,7 @@ let g:ale_lint_on_enter = 0
<a name="faq-quickfix"></a>
### 5.ix. How can I use the quickfix list instead of the loclist?
### 4.ix. How can I use the quickfix list instead of the loclist?
The quickfix list can be enabled by turning the `g:ale_set_quickfix`
option on. If you wish to also disable the loclist, you can disable
@@ -400,62 +375,3 @@ let g:ale_open_list = 1
" some other plugin which sets quickfix errors, etc.
let g:ale_keep_list_window_open = 1
```
<a name="faq-jsx-stylelint-eslint"></a>
### 5.x. How can I check JSX files with both stylelint and eslint?
If you configure ALE options correctly in your vimrc file, and install
the right tools, you can check JSX files with stylelint and eslint.
First, install eslint and install stylelint with
[https://github.com/styled-components/stylelint-processor-styled-components](stylelint-processor-styled-components).
Supposing you have installed both tools correctly, configure your .jsx files so
`jsx` is included in the filetype. You can use an `autocmd` for this.
```vim
augroup FiletypeGroup
autocmd!
au BufNewFile,BufRead *.jsx set filetype=javascript.jsx
augroup END
```
Supposing the filetype has been set correctly, you can set the following
options in your vimrc file:
```vim
let g:ale_linters = {'jsx': ['stylelint', 'eslint']}
let g:ale_linter_aliases = {'jsx': 'css'}
```
ALE will alias the `jsx` filetype so it uses the `css` filetype linters, and
use the original Array of selected linters for `jsx` from the `g:ale_linters`
object. All available linters will be used for the filetype `javascript`, and
no linter will be run twice for the same file.
<a name="faq-my-battery-is-sad"></a>
### 5.xi. Will this plugin eat all of my laptop battery power?
ALE takes advantage of the power of various tools to check your code. This of
course means that CPU time will be used to continuously check your code. If you
are concerned about the CPU time ALE will spend, which will of course imply
some cost to battery life, you can adjust your settings to make your CPU do
less work.
First, consider increasing the delay before which ALE will run any linters
while you type. ALE uses a timeout which is cancelled and reset every time you
type, and this delay can be increased so linters are run less often. See
`:help g:ale_lint_delay` for more information.
If you don't wish to run linters while you type, you can disable that
behaviour. Set `g:ale_lint_on_text_changed` to `0`, and consider setting
`g:ale_lint_on_save` to `1` to enable linting when you save files. You won't
get as frequent error checking, but ALE shouldn't block your ability to edit a
document after you save a file, so the asynchronous nature of the plugin will
still be an advantage.
If you are still concerned, you can turn the automatic linting off altogether,
including the option `g:ale_lint_on_enter`, and you can run ALE manually with
`:call ale#Lint()`.

View File

@@ -1,37 +0,0 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Follow-up checks for the plugin: warn about conflicting plugins.
" A flag for ensuring that this is not run more than one time.
if exists('g:loaded_ale_after')
finish
endif
" Set the flag so this file is not run more than one time.
let g:loaded_ale_after = 1
" Check if the flag is available and set to 0 to disable checking for and
" emitting conflicting plugin warnings.
if exists('g:ale_emit_conflict_warnings') && !g:ale_emit_conflict_warnings
finish
endif
" Conflicting Plugins Checks
function! s:GetConflictingPluginWarning(plugin_name) abort
return 'ALE conflicts with ' . a:plugin_name
\ . '. Uninstall it, or disable this warning with '
\ . '`let g:ale_emit_conflict_warnings = 0` in your vimrc file, '
\ . '*before* plugins are loaded.'
endfunction
if exists('g:loaded_syntastic_plugin')
throw s:GetConflictingPluginWarning('Syntastic')
endif
if exists('g:loaded_neomake')
throw s:GetConflictingPluginWarning('Neomake')
endif
if exists('g:loaded_validator_plugin')
throw s:GetConflictingPluginWarning('Validator')
endif

View File

@@ -4,6 +4,6 @@
call ale#linter#Define('ansible', {
\ 'name': 'ansible',
\ 'executable': 'ansible',
\ 'command': 'ansible-lint -p %t',
\ 'command': g:ale#util#stdin_wrapper . ' .yml ansible-lint -p',
\ 'callback': 'ale#handlers#HandlePEP8Format',
\})

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for AsciiDoc files
call ale#linter#Define('asciidoc', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -9,18 +9,12 @@ if !exists('g:ale_c_clang_options')
let g:ale_c_clang_options = '-std=c11 -Wall'
endif
function! ale_linters#c#clang#GetCommand(buffer) abort
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
return 'clang -S -x c -fsyntax-only '
\ . '-iquote ' . fnameescape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ' ' . g:ale_c_clang_options . ' -'
endfunction
call ale#linter#Define('c', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
\ 'executable': 'clang',
\ 'command_callback': 'ale_linters#c#clang#GetCommand',
\ 'command': 'clang -S -x c -fsyntax-only '
\ . g:ale_c_clang_options
\ . ' -',
\ 'callback': 'ale#handlers#HandleGCCFormat',
\})

View File

@@ -2,14 +2,15 @@
" Description: cppcheck linter for c files
" Set this option to change the cppcheck options
let g:ale_c_cppcheck_options = get(g:, 'ale_c_cppcheck_options', '--enable=style')
if !exists('g:ale_c_cppcheck_options')
let g:ale_c_cppcheck_options = '--enable=style'
endif
call ale#linter#Define('c', {
\ 'name': 'cppcheck',
\ 'output_stream': 'both',
\ 'executable': 'cppcheck',
\ 'command': 'cppcheck -q --language=c '
\ . g:ale_c_cppcheck_options
\ . ' %t',
\ 'command': g:ale#util#stdin_wrapper . ' .c cppcheck -q --language=c '
\ . g:ale_c_cppcheck_options,
\ 'callback': 'ale#handlers#HandleCppCheckFormat',
\})

View File

@@ -10,11 +10,9 @@ if !exists('g:ale_c_gcc_options')
endif
function! ale_linters#c#gcc#GetCommand(buffer) abort
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
return 'gcc -S -x c -fsyntax-only '
\ . '-iquote ' . fnameescape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ' ' . g:ale_c_gcc_options . ' -'
\ . g:ale_c_gcc_options . ' -'
endfunction
call ale#linter#Define('c', {

View File

@@ -17,12 +17,15 @@ function! ale_linters#chef#foodcritic#Handle(buffer, lines) abort
let l:text = l:match[1]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[3] + 0,
\ 'vcol': 0,
\ 'col': 0,
\ 'text': l:text,
\ 'type': 'W',
\ 'nr': -1,
\})
endfor
@@ -32,7 +35,7 @@ endfunction
call ale#linter#Define('chef', {
\ 'name': 'foodcritic',
\ 'executable': 'foodcritic',
\ 'command': 'foodcritic %t',
\ 'command': g:ale#util#stdin_wrapper . ' .rb foodcritic',
\ 'callback': 'ale_linters#chef#foodcritic#Handle',
\})

View File

@@ -1,24 +0,0 @@
" Author: Kenneth Benzie <k.benzie83@gmail.com>
" Description: cmakelint for cmake files
let g:ale_cmake_cmakelint_executable =
\ get(g:, 'ale_cmake_cmakelint_executable', 'cmakelint')
let g:ale_cmake_cmakelint_options =
\ get(g:, 'ale_cmake_cmakelint_options', '')
function! ale_linters#cmake#cmakelint#Executable(buffer) abort
return g:ale_cmake_cmakelint_executable
endfunction
function! ale_linters#cmake#cmakelint#Command(buffer) abort
return ale_linters#cmake#cmakelint#Executable(a:buffer)
\ . ' ' . g:ale_cmake_cmakelint_options . ' %t'
endfunction
call ale#linter#Define('cmake', {
\ 'name': 'cmakelint',
\ 'executable_callback': 'ale_linters#cmake#cmakelint#Executable',
\ 'command_callback': 'ale_linters#cmake#cmakelint#Command',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -1,23 +1,10 @@
" Author: KabbAmine - https://github.com/KabbAmine
" Description: Coffee for checking coffee files
function! ale_linters#coffee#coffee#GetExecutable(buffer) abort
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/coffee',
\ 'coffee'
\)
endfunction
function! ale_linters#coffee#coffee#GetCommand(buffer) abort
return ale_linters#coffee#coffee#GetExecutable(a:buffer)
\ . ' -cp -s'
endfunction
call ale#linter#Define('coffee', {
\ 'name': 'coffee',
\ 'executable_callback': 'ale_linters#coffee#coffee#GetExecutable',
\ 'command_callback': 'ale_linters#coffee#coffee#GetCommand',
\ 'executable': 'coffee',
\ 'command': 'coffee -cp -s',
\ 'output_stream': 'stderr',
\ 'callback': 'ale#handlers#HandleGCCFormat',
\})

View File

@@ -1,19 +1,6 @@
" Author: Prashanth Chandra https://github.com/prashcr
" Description: coffeelint linter for coffeescript files
function! ale_linters#coffee#coffeelint#GetExecutable(buffer) abort
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/coffeelint',
\ 'coffeelint'
\)
endfunction
function! ale_linters#coffee#coffeelint#GetCommand(buffer) abort
return ale_linters#coffee#coffeelint#GetExecutable(a:buffer)
\ . ' --stdin --reporter csv'
endfunction
function! ale_linters#coffee#coffeelint#Handle(buffer, lines) abort
" Matches patterns like the following:
"
@@ -21,7 +8,7 @@ function! ale_linters#coffee#coffeelint#Handle(buffer, lines) abort
" stdin,14,,error,Throwing strings is forbidden
"
" Note that we currently ignore lineNumberEnd for multiline errors
let l:pattern = 'stdin,\(\d\+\),\(\d*\),\(.\{-1,}\),\(.\+\)'
let l:pattern = 'stdin,\(\d\+\),\(\d*\),\(.\+\),\(.\+\)'
let l:output = []
for l:line in a:lines
@@ -32,14 +19,19 @@ function! ale_linters#coffee#coffeelint#Handle(buffer, lines) abort
endif
let l:line = l:match[1] + 0
let l:column = 1
let l:type = l:match[3] ==# 'error' ? 'E' : 'W'
let l:text = l:match[4]
" vcol is needed to indicate that the column is a character
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor
@@ -48,7 +40,7 @@ endfunction
call ale#linter#Define('coffee', {
\ 'name': 'coffeelint',
\ 'executable_callback': 'ale_linters#coffee#coffeelint#GetExecutable',
\ 'command_callback': 'ale_linters#coffee#coffeelint#GetCommand',
\ 'executable': 'coffeelint',
\ 'command': 'coffeelint --stdin --reporter csv',
\ 'callback': 'ale_linters#coffee#coffeelint#Handle',
\})

View File

@@ -1,23 +0,0 @@
" Author: Tomota Nakamura <https://github.com/tomotanakamura>
" Description: clang linter for cpp files
" Set this option to change the Clang options for warnings for CPP.
if !exists('g:ale_cpp_clang_options')
let g:ale_cpp_clang_options = '-std=c++14 -Wall'
endif
function! ale_linters#cpp#clang#GetCommand(buffer) abort
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
return 'clang++ -S -x c++ -fsyntax-only '
\ . '-iquote ' . fnameescape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ' ' . g:ale_cpp_clang_options . ' -'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
\ 'executable': 'clang++',
\ 'command_callback': 'ale_linters#cpp#clang#GetCommand',
\ 'callback': 'ale#handlers#HandleGCCFormat',
\})

View File

@@ -1,18 +0,0 @@
" Author: vdeurzen <tim@kompiler.org>, w0rp <devw0rp@gmail.com>
" Description: clang-tidy linter for cpp files
" Set this option to change the clang-tidy options for warnings for C.
let g:ale_cpp_clangtidy_options =
\ get(g:, 'ale_cpp_clangtidy_options', '-std=c++14 -Wall')
function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort
return 'clang-tidy %t -- ' . g:ale_cpp_clangtidy_options
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clangtidy',
\ 'output_stream': 'stdout',
\ 'executable': 'clang-tidy',
\ 'command_callback': 'ale_linters#cpp#clangtidy#GetCommand',
\ 'callback': 'ale#handlers#HandleGCCFormat',
\})

View File

@@ -2,14 +2,15 @@
" Description: cppcheck linter for cpp files
" Set this option to change the cppcheck options
let g:ale_cpp_cppcheck_options = get(g:, 'ale_cpp_cppcheck_options', '--enable=style')
if !exists('g:ale_cpp_cppcheck_options')
let g:ale_cpp_cppcheck_options = '--enable=style'
endif
call ale#linter#Define('cpp', {
\ 'name': 'cppcheck',
\ 'output_stream': 'both',
\ 'executable': 'cppcheck',
\ 'command': 'cppcheck -q --language=c++ '
\ . g:ale_cpp_cppcheck_options
\ . ' %t',
\ 'command': g:ale#util#stdin_wrapper . ' .cpp cppcheck -q --language=c++ '
\ . g:ale_cpp_cppcheck_options,
\ 'callback': 'ale#handlers#HandleCppCheckFormat',
\})

View File

@@ -16,11 +16,9 @@ if !exists('g:ale_cpp_gcc_options')
endif
function! ale_linters#cpp#gcc#GetCommand(buffer) abort
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
return 'gcc -S -x c++ -fsyntax-only '
\ . '-iquote ' . fnameescape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ' ' . g:ale_cpp_gcc_options . ' -'
\ . g:ale_cpp_gcc_options . ' -'
endfunction
call ale#linter#Define('cpp', {

View File

@@ -1,8 +1,6 @@
let g:ale_cs_mcs_options = get(g:, 'ale_cs_mcs_options', '')
function! ale_linters#cs#mcs#GetCommand(buffer) abort
return 'mcs -unsafe --parse ' . g:ale_cs_mcs_options . ' %t'
endfunction
if !exists('g:ale_cs_mcs_options')
let g:ale_cs_mcs_options = ''
endif
function! ale_linters#cs#mcs#Handle(buffer, lines) abort
" Look for lines like the following.
@@ -21,9 +19,11 @@ function! ale_linters#cs#mcs#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[3] . ': ' . l:match[4],
\ 'type': l:match[3] =~# '^error' ? 'E' : 'W',
\ 'nr': -1,
\})
endfor
@@ -31,9 +31,10 @@ function! ale_linters#cs#mcs#Handle(buffer, lines) abort
endfunction
call ale#linter#Define('cs',{
\ 'name': 'mcs',
\ 'output_stream': 'stderr',
\ 'executable': 'mcs',
\ 'command_callback': 'ale_linters#cs#mcs#GetCommand',
\ 'callback': 'ale_linters#cs#mcs#Handle',
\})
\ 'name': 'mcs',
\ 'output_stream': 'stderr',
\ 'executable': 'mcs',
\ 'command': g:ale#util#stdin_wrapper . ' .cs mcs -unsafe --parse' . g:ale_cs_mcs_options,
\ 'callback': 'ale_linters#cs#mcs#Handle',
\ })

View File

@@ -4,6 +4,6 @@
call ale#linter#Define('css', {
\ 'name': 'csslint',
\ 'executable': 'csslint',
\ 'command': 'csslint --format=compact %t',
\ 'command': g:ale#util#stdin_wrapper . ' .css csslint --format=compact',
\ 'callback': 'ale#handlers#HandleCSSLintFormat',
\})

View File

@@ -3,9 +3,6 @@
let g:ale_css_stylelint_executable =
\ get(g:, 'ale_css_stylelint_executable', 'stylelint')
let g:ale_css_stylelint_options =
\ get(g:, 'ale_css_stylelint_options', '')
let g:ale_css_stylelint_use_global =
\ get(g:, 'ale_css_stylelint_use_global', 0)
@@ -23,7 +20,6 @@ endfunction
function! ale_linters#css#stylelint#GetCommand(buffer) abort
return ale_linters#css#stylelint#GetExecutable(a:buffer)
\ . ' ' . g:ale_css_stylelint_options
\ . ' --stdin-filename %s'
endfunction

View File

@@ -46,7 +46,9 @@ function! ale_linters#d#dmd#DMDCommand(buffer, dub_output) abort
endif
endfor
return 'dmd '. join(l:import_list) . ' -o- -vcolumns -c %t'
return g:ale#util#stdin_wrapper . ' .d dmd '
\ . join(l:import_list)
\ . ' -o- -vcolumns -c'
endfunction
function! ale_linters#d#dmd#Handle(buffer, lines) abort
@@ -68,12 +70,15 @@ function! ale_linters#d#dmd#Handle(buffer, lines) abort
let l:type = l:match[3]
let l:text = l:match[4]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': bufnr('%'),
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type ==# 'Warning' ? 'W' : 'E',
\ 'nr': -1,
\})
endfor

View File

@@ -23,9 +23,11 @@ function! ale_linters#dockerfile#hadolint#Handle(buffer, lines) abort
let l:type = 'W'
let l:text = l:match[3]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:lnum,
\ 'vcol': 0,
\ 'col': 0,
\ 'type': l:type,
\ 'text': l:text,

View File

@@ -3,8 +3,8 @@
function! ale_linters#elixir#credo#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" lib/filename.ex:19:7: F: Pipe chain should start with a raw value.
let l:pattern = '\v:(\d+):?(\d+)?: (.): (.+)$'
" stdin:19: F: Pipe chain should start with a raw value.
let l:pattern = '\v^stdin:(\d+):?(\d+)?: (.): (.+)$'
let l:output = []
for l:line in a:lines
@@ -23,12 +23,15 @@ function! ale_linters#elixir#credo#Handle(buffer, lines) abort
let l:type = 'W'
endif
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'type': l:type,
\ 'text': l:text,
\ 'nr': -1,
\})
endfor
@@ -38,5 +41,5 @@ endfunction
call ale#linter#Define('elixir', {
\ 'name': 'credo',
\ 'executable': 'mix',
\ 'command': 'mix credo suggest --format=flycheck --read-from-stdin %s',
\ 'command': 'mix credo suggest --format=flycheck --read-from-stdin',
\ 'callback': 'ale_linters#elixir#credo#Handle' })

View File

@@ -22,10 +22,11 @@ function! ale_linters#elm#make#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:error.region.start.line,
\ 'vcol': 0,
\ 'col': l:error.region.start.column,
\ 'type': (l:error.type ==? 'error') ? 'E' : 'W',
\ 'text': l:error.overview,
\ 'detail': l:error.overview . "\n\n" . l:error.details
\ 'nr': -1,
\})
endif
endfor
@@ -51,8 +52,9 @@ function! ale_linters#elm#make#GetCommand(buffer) abort
" which is why this is hard coded here.
" Source: https://github.com/elm-lang/elm-make/blob/master/src/Flags.hs
let l:elm_cmd = 'elm-make --report=json --output='.shellescape('/dev/null')
let l:stdin_wrapper = g:ale#util#stdin_wrapper . ' .elm'
return l:dir_set_cmd . ' ' . l:elm_cmd . ' %t'
return l:dir_set_cmd . ' ' . l:stdin_wrapper . ' ' . l:elm_cmd
endfunction
call ale#linter#Define('elm', {

View File

@@ -3,9 +3,7 @@
let g:ale_erlang_erlc_options = get(g:, 'ale_erlang_erlc_options', '')
function! ale_linters#erlang#erlc#GetCommand(buffer) abort
let l:output_file = tempname()
call ale#engine#ManageFile(a:buffer, l:output_file)
return 'erlc -o ' . fnameescape(l:output_file) . ' ' . g:ale_erlang_erlc_options . ' %t'
return g:ale#util#stdin_wrapper . ' .erl erlc ' . g:ale_erlang_erlc_options
endfunction
function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
@@ -45,9 +43,11 @@ function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': 0,
\ 'vcol': 0,
\ 'col': 0,
\ 'type': 'E',
\ 'text': l:match_parse_transform[0],
\ 'nr': -1,
\})
continue
@@ -73,12 +73,15 @@ function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
let l:type = 'E'
endif
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': 0,
\ 'type': l:type,
\ 'text': l:text,
\ 'nr': -1,
\})
endfor

View File

@@ -41,7 +41,9 @@ function! ale_linters#fortran#gcc#Handle(buffer, lines) abort
let l:last_loclist_obj = {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'nr': -1,
\}
" Start looking for the message and error type.

View File

@@ -101,12 +101,6 @@ function! ale_linters#go#gobuild#CopyFiles(buffer, golist_output) abort
endfunction
function! ale_linters#go#gobuild#GetCommand(buffer, copy_output) abort
" If for some reason we don't get any output from the last command, stop
" here.
if empty(a:copy_output)
return ''
endif
let l:tempdir = a:copy_output[0]
let l:importpath = s:PackageImportPath(a:buffer)
@@ -196,9 +190,11 @@ function! ale_linters#go#gobuild#Handler(buffer, lines) abort
call add(l:output, {
\ 'bufnr': l:buffer,
\ 'lnum': l:match[2] + 0,
\ 'vcol': 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[4],
\ 'type': 'E',
\ 'nr': -1,
\})
endfor

View File

@@ -5,6 +5,6 @@ call ale#linter#Define('go', {
\ 'name': 'gofmt',
\ 'output_stream': 'stderr',
\ 'executable': 'gofmt',
\ 'command': 'gofmt -e %t',
\ 'command': g:ale#util#stdin_wrapper . ' .go gofmt -e',
\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
\})

View File

@@ -4,6 +4,6 @@
call ale#linter#Define('go', {
\ 'name': 'golint',
\ 'executable': 'golint',
\ 'command': 'golint %t',
\ 'command': g:ale#util#stdin_wrapper . ' .go golint',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -5,6 +5,6 @@ call ale#linter#Define('go', {
\ 'name': 'go vet',
\ 'output_stream': 'stderr',
\ 'executable': 'go',
\ 'command': 'go vet %t',
\ 'command': g:ale#util#stdin_wrapper . ' .go go vet',
\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
\})

View File

@@ -1,33 +0,0 @@
" Author: Patrick Lewis - https://github.com/patricklewis
" Description: haml-lint for Haml files
function! ale_linters#haml#hamllint#Handle(buffer, lines) abort
" Matches patterns like the following:
" <path>:51 [W] RuboCop: Use the new Ruby 1.9 hash syntax.
let l:pattern = '\v^.*:(\d+) \[([EW])\] (.+)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'type': l:match[2],
\ 'text': l:match[3]
\})
endfor
return l:output
endfunction
call ale#linter#Define('haml', {
\ 'name': 'hamllint',
\ 'executable': 'haml-lint',
\ 'command': 'haml-lint %t',
\ 'callback': 'ale_linters#haml#hamllint#Handle'
\})

View File

@@ -1,18 +1,73 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: ghc for Haskell files
if exists('g:loaded_ale_linters_haskell_ghc')
finish
endif
let g:loaded_ale_linters_haskell_ghc = 1
function! ale_linters#haskell#ghc#Handle(buffer, lines) abort
" Look for lines like the following.
"
" /dev/stdin:28:26: Not in scope: `>>>>>'
let l:pattern = '^[^:]\+:\(\d\+\):\(\d\+\): \(.\+\)$'
let l:output = []
" For some reason the output coming out of the GHC through the wrapper
" script breaks the lines up in strange ways. So we have to join some
" lines back together again.
let l:corrected_lines = []
for l:line in a:lines
if len(matchlist(l:line, l:pattern)) > 0
call add(l:corrected_lines, l:line)
if l:line !~# ': error:$'
call add(l:corrected_lines, '')
endif
elseif l:line ==# ''
call add(l:corrected_lines, l:line)
else
if len(l:corrected_lines) > 0
let l:line = substitute(l:line, '\v\s+', ' ', '')
let l:corrected_lines[-1] .= l:line
endif
endif
endfor
for l:line in l:corrected_lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[3],
\ 'type': 'E',
\ 'nr': -1,
\})
endfor
return l:output
endfunction
call ale#linter#Define('haskell', {
\ 'name': 'ghc',
\ 'output_stream': 'stderr',
\ 'executable': 'ghc',
\ 'command': 'ghc -fno-code -v0 %t',
\ 'callback': 'ale#handlers#HandleGhcFormat',
\ 'command': g:ale#util#stdin_wrapper . ' .hs ghc -fno-code -v0',
\ 'callback': 'ale_linters#haskell#ghc#Handle',
\})
call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc',
\ 'output_stream': 'stderr',
\ 'executable': 'stack',
\ 'command': 'stack ghc -- -fno-code -v0 %t',
\ 'callback': 'ale#handlers#HandleGhcFormat',
\ 'command': g:ale#util#stdin_wrapper . ' .hs stack ghc -- -fno-code -v0',
\ 'callback': 'ale_linters#haskell#ghc#Handle',
\})

View File

@@ -1,9 +0,0 @@
" Author: rob-b
" Description: hdevtools for Haskell files
call ale#linter#Define('haskell', {
\ 'name': 'hdevtools',
\ 'executable': 'hdevtools',
\ 'command': 'hdevtools check -g -Wall -p %s %t',
\ 'callback': 'ale#handlers#HandleGhcFormat',
\})

View File

@@ -7,9 +7,11 @@ function! ale_linters#haskell#hlint#Handle(buffer, lines) abort
let l:output = []
for l:error in l:errors
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:error.startLine + 0,
\ 'vcol': 0,
\ 'col': l:error.startColumn + 0,
\ 'text': l:error.severity . ': ' . l:error.hint . '. Found: ' . l:error.from . ' Why not: ' . l:error.to,
\ 'type': l:error.severity ==# 'Error' ? 'E' : 'W',

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for Vim help files
call ale#linter#Define('help', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -23,9 +23,9 @@ function! ale_linters#html#htmlhint#GetExecutable(buffer) abort
endfunction
function! ale_linters#html#htmlhint#GetCommand(buffer) abort
return ale_linters#html#htmlhint#GetExecutable(a:buffer)
return g:ale#util#stdin_wrapper . ' .html '
\ . ale_linters#html#htmlhint#GetExecutable(a:buffer)
\ . ' ' . g:ale_html_htmlhint_options
\ . ' %t'
endfunction
call ale#linter#Define('html', {

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for HTML files
call ale#linter#Define('html', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -49,12 +49,15 @@ function! ale_linters#html#tidy#Handle(buffer, lines) abort
let l:type = l:match[3] ==# 'Error' ? 'E' : 'W'
let l:text = l:match[4]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:col,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor

View File

@@ -1,51 +0,0 @@
" Author: farenjihn <farenjihn@gmail.com>, w0rp <devw0rp@gmail.com>
" Description: Lints java files using javac
let g:ale_java_javac_options = get(g:, 'ale_java_javac_options', '')
let g:ale_java_javac_classpath = get(g:, 'ale_java_javac_classpath', '')
function! ale_linters#java#javac#GetCommand(buffer) abort
let l:cp_option = !empty(g:ale_java_javac_classpath)
\ ? '-cp ' . g:ale_java_javac_classpath
\ : ''
return 'javac -Xlint '
\ . l:cp_option
\ . ' ' . g:ale_java_javac_options
\ . ' %t'
endfunction
function! ale_linters#java#javac#Handle(buffer, lines) abort
" Look for lines like the following.
"
" Main.java:13: warning: [deprecation] donaught() in Testclass has been deprecated
" Main.java:16: error: ';' expected
let l:pattern = '^.*\:\(\d\+\):\ \(.*\):\(.*\)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'text': l:match[2] . ':' . l:match[3],
\ 'type': l:match[2] ==# 'error' ? 'E' : 'W',
\})
endfor
return l:output
endfunction
call ale#linter#Define('java', {
\ 'name': 'javac',
\ 'output_stream': 'stderr',
\ 'executable': 'javac',
\ 'command_callback': 'ale_linters#java#javac#GetCommand',
\ 'callback': 'ale_linters#java#javac#Handle',
\})

View File

@@ -29,21 +29,6 @@ function! ale_linters#javascript#eslint#GetCommand(buffer) abort
endfunction
function! ale_linters#javascript#eslint#Handle(buffer, lines) abort
let l:config_error_pattern = '\v^ESLint couldn''t find a configuration file'
\ . '|^Cannot read config file'
" Look for a message in the first few lines which indicates that
" a configuration file couldn't be found.
for l:line in a:lines[:10]
if len(matchlist(l:line, l:config_error_pattern)) > 0
return [{
\ 'lnum': 1,
\ 'text': 'eslint configuration error (type :ALEDetail for more information)',
\ 'detail': join(a:lines, "\n"),
\}]
endif
endfor
" Matches patterns line the following:
"
" /path/to/some-filename.js:47:14: Missing trailing comma. [Warning/comma-dangle]
@@ -76,12 +61,15 @@ function! ale_linters#javascript#eslint#Handle(buffer, lines) abort
let l:text .= ' [' . l:match[4] . ']'
endif
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
\ 'type': l:type ==# 'Warning' ? 'W' : 'E',
\ 'nr': -1,
\})
endfor

View File

@@ -8,73 +8,68 @@ let g:ale_javascript_flow_use_global =
\ get(g:, 'ale_javascript_flow_use_global', 0)
function! ale_linters#javascript#flow#GetExecutable(buffer) abort
if g:ale_javascript_flow_use_global
return g:ale_javascript_flow_executable
endif
if g:ale_javascript_flow_use_global
return g:ale_javascript_flow_executable
endif
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/flow',
\ g:ale_javascript_flow_executable
\)
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/flow',
\ g:ale_javascript_flow_executable
\)
endfunction
function! ale_linters#javascript#flow#GetCommand(buffer) abort
let l:flow_config = ale#util#FindNearestFile(a:buffer, '.flowconfig')
if empty(l:flow_config)
" Don't run Flow if we can't find a .flowconfig file.
return ''
endif
return ale_linters#javascript#flow#GetExecutable(a:buffer)
\ . ' check-contents --respect-pragma --json --from ale %s'
return ale_linters#javascript#flow#GetExecutable(a:buffer)
\ . ' check-contents --respect-pragma --json --from ale %s'
endfunction
function! ale_linters#javascript#flow#Handle(buffer, lines) abort
let l:str = join(a:lines, '')
let l:str = join(a:lines, '')
if l:str ==# ''
return []
endif
let l:flow_output = json_decode(l:str)
if l:str ==# ''
return []
endif
let l:flow_output = json_decode(l:str)
if has_key(l:flow_output, 'errors')
let l:output = []
for l:error in get(l:flow_output, 'errors', [])
" Each error is broken up into parts
let l:text = ''
let l:line = 0
let l:col = 0
for l:message in l:error.message
" Comments have no line of column information
if has_key(l:message, 'loc') && l:line ==# 0
let l:line = l:message.loc.start.line + 0
let l:col = l:message.loc.start.column + 0
endif
if l:text ==# ''
let l:text = l:message.descr . ':'
else
let l:text = l:text . ' ' . l:message.descr
endif
endfor
if has_key(l:error, 'operation')
let l:text = l:text . ' See also: ' . l:error.operation.descr
for l:error in l:flow_output.errors
" Each error is broken up into parts
let l:text = ''
let l:line = 0
let l:col = 0
for l:message in l:error.message
" Comments have no line of column information
if has_key(l:message, 'loc') && l:line ==# 0
let l:line = l:message.loc.start.line + 0
let l:col = l:message.loc.start.column + 0
endif
if l:text ==# ''
let l:text = l:message.descr . ':'
else
let l:text = l:text . ' ' . l:message.descr
endif
endfor
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'col': l:col,
\ 'text': l:text,
\ 'type': l:error.level ==# 'error' ? 'E' : 'W',
\})
if has_key(l:error, 'operation')
let l:text = l:text . ' See also: ' . l:error.operation.descr
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:col,
\ 'text': l:text,
\ 'type': l:error.level ==# 'error' ? 'E' : 'W',
\})
endfor
return l:output
else
return []
endif
endfunction
call ale#linter#Define('javascript', {

View File

@@ -1,68 +0,0 @@
" Author: Ahmed El Gabri <@ahmedelgabri>
" Description: standardjs for JavaScript files
let g:ale_javascript_standard_executable =
\ get(g:, 'ale_javascript_standard_executable', 'standard')
let g:ale_javascript_standard_options =
\ get(g:, 'ale_javascript_standard_options', '')
let g:ale_javascript_standard_use_global =
\ get(g:, 'ale_javascript_standard_use_global', 0)
function! ale_linters#javascript#standard#GetExecutable(buffer) abort
if g:ale_javascript_standard_use_global
return g:ale_javascript_standard_executable
endif
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/standard',
\ g:ale_javascript_standard_executable
\)
endfunction
function! ale_linters#javascript#standard#GetCommand(buffer) abort
return ale_linters#javascript#standard#GetExecutable(a:buffer)
\ . ' ' . g:ale_javascript_standard_options
\ . ' --stdin %s'
endfunction
function! ale_linters#javascript#standard#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" /path/to/some-filename.js:47:14: Strings must use singlequote.
" /path/to/some-filename.js:56:41: Expected indentation of 2 spaces but found 4.
" /path/to/some-filename.js:13:3: Parsing error: Unexpected token
let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
let l:type = 'Error'
let l:text = l:match[3]
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
\ 'type': 'E',
\})
endfor
return l:output
endfunction
call ale#linter#Define('javascript', {
\ 'name': 'standard',
\ 'executable_callback': 'ale_linters#javascript#standard#GetExecutable',
\ 'command_callback': 'ale_linters#javascript#standard#GetCommand',
\ 'callback': 'ale_linters#javascript#standard#Handle',
\})

View File

@@ -1,41 +0,0 @@
" Author: Daniel Lupu <lupu.daniel.f@gmail.com>
" Description: xo for JavaScript files
let g:ale_javascript_xo_executable =
\ get(g:, 'ale_javascript_xo_executable', 'xo')
let g:ale_javascript_xo_options =
\ get(g:, 'ale_javascript_xo_options', '')
let g:ale_javascript_xo_use_global =
\ get(g:, 'ale_javascript_xo_use_global', 0)
function! ale_linters#javascript#xo#GetExecutable(buffer) abort
if g:ale_javascript_xo_use_global
return g:ale_javascript_xo_executable
endif
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/xo',
\ g:ale_javascript_xo_executable
\)
endfunction
function! ale_linters#javascript#xo#GetCommand(buffer) abort
return ale_linters#javascript#xo#GetExecutable(a:buffer)
\ . ' ' . g:ale_javascript_xo_options
\ . ' --reporter unix --stdin --stdin-filename %s'
endfunction
function! ale_linters#javascript#xo#Handle(buffer, lines) abort
" xo uses eslint and the output format is the same
return ale_linters#javascript#eslint#Handle(a:buffer, a:lines)
endfunction
call ale#linter#Define('javascript', {
\ 'name': 'xo',
\ 'executable_callback': 'ale_linters#javascript#xo#GetExecutable',
\ 'command_callback': 'ale_linters#javascript#xo#GetCommand',
\ 'callback': 'ale_linters#javascript#xo#Handle',
\})

View File

@@ -14,12 +14,15 @@ function! ale_linters#json#jsonlint#Handle(buffer, lines) abort
continue
endif
" vcol is needed to indicate that the column is a character
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[3],
\ 'type': 'E',
\ 'nr': -1,
\})
endfor

View File

@@ -19,12 +19,15 @@ function! ale_linters#lua#luacheck#Handle(buffer, lines) abort
continue
endif
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[4],
\ 'type': l:match[3],
\ 'nr': -1,
\})
endfor

View File

@@ -16,9 +16,11 @@ function! ale_linters#markdown#mdl#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': 0,
\ 'text': l:match[2],
\ 'type': 'W',
\ 'nr': -1,
\})
endfor

View File

@@ -1,9 +1,9 @@
" Author: poohzrn https://github.com/poohzrn
" Description: proselint for Markdown files
" Description: proselint for markdown files
call ale#linter#Define('markdown', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'command': g:ale#util#stdin_wrapper . ' .md proselint',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -4,16 +4,6 @@
let g:ale_matlab_mlint_executable =
\ get(g:, 'ale_matlab_mlint_executable', 'mlint')
function! ale_linters#matlab#mlint#GetExecutable(buffer) abort
return g:ale_matlab_mlint_executable
endfunction
function! ale_linters#matlab#mlint#GetCommand(buffer) abort
let l:executable = ale_linters#matlab#mlint#GetExecutable(a:buffer)
return l:executable . ' -id %t'
endfunction
function! ale_linters#matlab#mlint#Handle(buffer, lines) abort
" Matches patterns like the following:
"
@@ -40,12 +30,15 @@ function! ale_linters#matlab#mlint#Handle(buffer, lines) abort
continue
endif
" vcol is needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:lnum,
\ 'vcol': 0,
\ 'col': l:col,
\ 'text': l:text,
\ 'type': 'W',
\ 'nr': -1,
\})
endfor
@@ -54,8 +47,9 @@ endfunction
call ale#linter#Define('matlab', {
\ 'name': 'mlint',
\ 'executable_callback': 'ale_linters#matlab#mlint#GetExecutable',
\ 'command_callback': 'ale_linters#matlab#mlint#GetCommand',
\ 'executable': 'mlint',
\ 'command': g:ale#util#stdin_wrapper .
\ ' .m ' . g:ale_matlab_mlint_executable . ' -id',
\ 'output_stream': 'stderr',
\ 'callback': 'ale_linters#matlab#mlint#Handle',
\})

View File

@@ -1,65 +0,0 @@
" Author: Baabelfish
" Description: Typechecking for nim files
function! ale_linters#nim#nimcheck#Handle(buffer, lines) abort
let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p:t')
let l:pattern = '^\(.\+\.nim\)(\(\d\+\), \(\d\+\)) \(.\+\)'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
" Only show errors of the current buffer
" NOTE: Checking filename only is OK because nim enforces unique
" module names.
let l:temp_buffer_filename = fnamemodify(l:match[1], ':p:t')
if l:buffer_filename !=# '' && l:temp_buffer_filename !=# l:buffer_filename
continue
endif
let l:line = l:match[2] + 0
let l:column = l:match[3] + 0
let l:text = l:match[4]
let l:type = 'W'
" Extract error type from message of type 'Error: Some error message'
let l:textmatch = matchlist(l:match[4], '^\(.\{-}\): .\+$')
if len(l:textmatch) > 0
let l:errortype = l:textmatch[1]
if l:errortype ==# 'Error'
let l:type = 'E'
endif
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\})
endfor
return l:output
endfunction
function! ale_linters#nim#nimcheck#GetCommand(buffer)
return 'nim check --path:' . fnameescape(fnamemodify(bufname(a:buffer), ':p:h')) . ' --verbosity:0 --colors:off --listFullPaths %t'
endfunction
call ale#linter#Define('nim', {
\ 'name': 'nimcheck',
\ 'executable': 'nim',
\ 'output_stream': 'both',
\ 'command_callback': 'ale_linters#nim#nimcheck#GetCommand',
\ 'callback': 'ale_linters#nim#nimcheck#Handle'
\})

View File

@@ -1,34 +0,0 @@
" Author: Alistair Bill <@alibabzo>
" Description: nix-instantiate linter for nix files
function! ale_linters#nix#nix#Handle(buffer, lines) abort
let l:pattern = '^\(.\+\): \(.\+\), at .*:\(\d\+\):\(\d\+\)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[3] + 0,
\ 'col': l:match[4] + 0,
\ 'text': l:match[1] . ': ' . l:match[2],
\ 'type': l:match[1] =~# '^error' ? 'E' : 'W',
\})
endfor
return l:output
endfunction
call ale#linter#Define('nix', {
\ 'name': 'nix',
\ 'output_stream': 'stderr',
\ 'executable': 'nix-instantiate',
\ 'command': 'nix-instantiate --parse -',
\ 'callback': 'ale_linters#nix#nix#Handle',
\})

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for nroff files
call ale#linter#Define('nroff', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -1,22 +1,6 @@
" Author: Vincent Lequertier <https://github.com/SkySymbol>
" Description: This file adds support for checking perl syntax
let g:ale_perl_perl_executable =
\ get(g:, 'ale_perl_perl_executable', 'perl')
let g:ale_perl_perl_options =
\ get(g:, 'ale_perl_perl_options', '-X -c -Mwarnings -Ilib')
function! ale_linters#perl#perl#GetExecutable(buffer) abort
return g:ale_perl_perl_executable
endfunction
function! ale_linters#perl#perl#GetCommand(buffer) abort
return ale_linters#perl#perl#GetExecutable(a:buffer)
\ . ' ' . g:ale_perl_perl_options
\ . ' %t'
endfunction
function! ale_linters#perl#perl#Handle(buffer, lines) abort
let l:pattern = '\(.\+\) at \(.\+\) line \(\d\+\)'
let l:output = []
@@ -29,14 +13,19 @@ function! ale_linters#perl#perl#Handle(buffer, lines) abort
endif
let l:line = l:match[3]
let l:column = 1
let l:text = l:match[1]
let l:type = 'E'
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor
@@ -45,8 +34,8 @@ endfunction
call ale#linter#Define('perl', {
\ 'name': 'perl',
\ 'executable_callback': 'ale_linters#perl#perl#GetExecutable',
\ 'executable': 'perl',
\ 'output_stream': 'both',
\ 'command_callback': 'ale_linters#perl#perl#GetCommand',
\ 'command': 'perl -X -c -Mwarnings -Ilib',
\ 'callback': 'ale_linters#perl#perl#Handle',
\})

View File

@@ -13,14 +13,19 @@ function! ale_linters#perl#perlcritic#Handle(buffer, lines) abort
endif
let l:line = l:match[3]
let l:column = 1
let l:text = l:match[1]
let l:type = 'E'
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor

View File

@@ -19,6 +19,7 @@ function! ale_linters#php#hack#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[2] + 0,
\ 'vcol': 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[5],
\ 'type': 'E',

View File

@@ -1,12 +1,11 @@
" Author: Spencer Wood <https://github.com/scwood>, Adriaan Zonnenberg <amz@adriaan.xyz>
" Author: Spencer Wood <https://github.com/scwood>
" Description: This file adds support for checking PHP with php-cli
function! ale_linters#php#php#Handle(buffer, lines) abort
" Matches patterns like the following:
"
" PHP Parse error: syntax error, unexpected ';', expecting ']' in - on line 15
let l:pattern = '\vPHP %(Fatal|Parse) error:\s+(.+unexpected ''(.+)%(expecting.+)@<!''.*|.+) in - on line (\d+)'
" Parse error: parse error in - on line 7
let l:pattern = 'Parse error:\s\+\(.\+\) on line \(\d\+\)'
let l:output = []
for l:line in a:lines
@@ -16,12 +15,15 @@ function! ale_linters#php#php#Handle(buffer, lines) abort
continue
endif
" vcol is needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[3] + 0,
\ 'col': empty(l:match[2]) ? 0 : stridx(getline(l:match[3]), l:match[2]) + 1,
\ 'lnum': l:match[2] + 0,
\ 'vcol': 0,
\ 'col': 1,
\ 'text': l:match[1],
\ 'type': 'E',
\ 'nr': -1,
\})
endfor

View File

@@ -29,12 +29,15 @@ function! ale_linters#php#phpcs#Handle(buffer, lines) abort
let l:text = l:match[4]
let l:type = l:match[3]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
\ 'type': l:type ==# 'error' ? 'E' : 'W',
\ 'nr': -1,
\})
endfor

View File

@@ -1,38 +0,0 @@
" Author: medains <https://github.com/medains>
" Description: phpmd for PHP files
" Set to change the ruleset
let g:ale_php_phpmd_ruleset = get(g:, 'ale_php_phpmd_ruleset', 'cleancode,codesize,controversial,design,naming,unusedcode')
function! ale_linters#php#phpmd#Handle(buffer, lines) abort
" Matches against lines like the following:
"
" /path/to/some-filename.php:18 message
let l:pattern = '^.*:\(\d\+\)\t\(.\+\)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'col': 0,
\ 'text': l:match[2],
\ 'type': 'W',
\})
endfor
return l:output
endfunction
call ale#linter#Define('php', {
\ 'name': 'phpmd',
\ 'executable': 'phpmd',
\ 'command': 'phpmd %s text ' . g:ale_php_phpmd_ruleset . ' --ignore-violations-on-exit %t',
\ 'callback': 'ale_linters#php#phpmd#Handle',
\})

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for Pod files
call ale#linter#Define('pod', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -5,6 +5,6 @@ call ale#linter#Define('pug', {
\ 'name': 'puglint',
\ 'executable': 'pug-lint',
\ 'output_stream': 'stderr',
\ 'command': 'pug-lint -r inline %t',
\ 'command': g:ale#util#stdin_wrapper . ' .pug pug-lint -r inline',
\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
\})

View File

@@ -14,12 +14,15 @@ function! ale_linters#puppet#puppet#Handle(buffer, lines) abort
continue
endif
" vcol is needed to indicate that the column is a character
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[2] + 0,
\ 'vcol': 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[1],
\ 'type': 'E',
\ 'nr': -1,
\})
endfor
@@ -30,6 +33,6 @@ call ale#linter#Define('puppet', {
\ 'name': 'puppet',
\ 'executable': 'puppet',
\ 'output_stream': 'stderr',
\ 'command': 'puppet parser validate --color=false %t',
\ 'command': g:ale#util#stdin_wrapper . ' .pp puppet parser validate --color=false',
\ 'callback': 'ale_linters#puppet#puppet#Handle',
\})

View File

@@ -3,8 +3,6 @@
call ale#linter#Define('puppet', {
\ 'name': 'puppetlint',
\ 'executable': 'puppet-lint',
\ 'command': 'puppet-lint --no-autoloader_layout-check'
\ . ' --log-format "-:%{line}:%{column}: %{kind}: [%{check}] %{message}"'
\ . ' %t',
\ 'command': g:ale#util#stdin_wrapper . ' .pp puppet-lint --no-autoloader_layout-check --log-format "-:%{line}:%{column}: %{kind}: [%{check}] %{message}"',
\ 'callback': 'ale#handlers#HandleGCCFormat',
\})

View File

@@ -5,6 +5,8 @@ call ale#linter#Define('pyrex', {
\ 'name': 'cython',
\ 'output_stream': 'stderr',
\ 'executable': 'cython',
\ 'command': 'cython --warning-extra -o ' . g:ale#util#nul_file . ' %t',
\ 'command': g:ale#util#stdin_wrapper
\ . ' .pyx cython --warning-extra -o '
\ . g:ale#util#nul_file,
\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
\})

View File

@@ -10,9 +10,10 @@ function! g:ale_linters#python#mypy#GetCommand(buffer) abort
\ ? 'MYPYPATH=' . l:automatic_stubs_dir . ' '
\ : ''
return 'mypy --show-column-numbers '
return l:automatic_stubs_command
\ . g:ale#util#stdin_wrapper
\ . ' .py mypy --show-column-numbers '
\ . g:ale_python_mypy_options
\ . ' %t'
endfunction
let s:path_pattern = '[a-zA-Z]\?\\\?:\?[[:alnum:]/\.\-_]\+'
@@ -44,9 +45,11 @@ function! g:ale_linters#python#mypy#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[4],
\ 'type': l:match[3] =~# 'error' ? 'E' : 'W',
\ 'nr': -1,
\})
endfor

View File

@@ -12,10 +12,10 @@ function! ale_linters#python#pylint#GetExecutable(buffer) abort
endfunction
function! ale_linters#python#pylint#GetCommand(buffer) abort
return ale_linters#python#pylint#GetExecutable(a:buffer)
return g:ale#util#stdin_wrapper . ' .py '
\ . ale_linters#python#pylint#GetExecutable(a:buffer)
\ . ' ' . g:ale_python_pylint_options
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n'
\ . ' %t'
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} {msg}" --reports n'
endfunction
call ale#linter#Define('python', {

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for reStructuredText files
call ale#linter#Define('rst', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -19,12 +19,15 @@ function! ale_linters#ruby#rubocop#Handle(buffer, lines) abort
let l:text = l:match[4]
let l:type = l:match[3]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
\ 'type': index(['C', 'E'], l:type) != -1 ? 'E' : 'W',
\ 'nr': -1,
\})
endfor

View File

@@ -3,6 +3,6 @@
call ale#linter#Define('sass', {
\ 'name': 'sasslint',
\ 'executable': 'sass-lint',
\ 'command': 'sass-lint -v -q -f compact %t',
\ 'command': g:ale#util#stdin_wrapper . ' .sass sass-lint -v -q -f compact',
\ 'callback': 'ale#handlers#HandleCSSLintFormat',
\})

View File

@@ -1,3 +1,4 @@
" vim: set et:
" Author: Zoltan Kalmar - https://github.com/kalmiz
" Description: Basic scala support using scalac
@@ -20,17 +21,22 @@ function! ale_linters#scala#scalac#Handle(buffer, lines) abort
let l:text = l:match[3]
let l:type = l:match[2] ==# 'error' ? 'E' : 'W'
let l:col = 0
if l:ln + 1 < len(a:lines)
let l:col = stridx(a:lines[l:ln + 1], '^')
if l:col == -1
let l:col = 0
endif
endif
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:col + 1,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor
@@ -41,6 +47,6 @@ call ale#linter#Define('scala', {
\ 'name': 'scalac',
\ 'executable': 'scalac',
\ 'output_stream': 'stderr',
\ 'command': 'scalac -Ystop-after:parser %t',
\ 'command': g:ale#util#stdin_wrapper . ' .scala scalac -Ystop-after:parser',
\ 'callback': 'ale_linters#scala#scalac#Handle',
\})

View File

@@ -3,6 +3,6 @@
call ale#linter#Define('scss', {
\ 'name': 'sasslint',
\ 'executable': 'sass-lint',
\ 'command': 'sass-lint -v -q -f compact %t',
\ 'command': g:ale#util#stdin_wrapper . ' .scss sass-lint -v -q -f compact',
\ 'callback': 'ale#handlers#HandleCSSLintFormat',
\})

View File

@@ -20,12 +20,15 @@ function! ale_linters#scss#scsslint#Handle(buffer, lines) abort
continue
endif
" vcol is needed to indicate that the column is a character
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[4],
\ 'type': l:match[3] ==# 'E' ? 'E' : 'W',
\ 'nr': -1,
\})
endfor

View File

@@ -30,7 +30,7 @@ function! ale_linters#sh#shell#GetExecutable(buffer) abort
endfunction
function! ale_linters#sh#shell#GetCommand(buffer) abort
return ale_linters#sh#shell#GetExecutable(a:buffer) . ' -n %t'
return ale_linters#sh#shell#GetExecutable(a:buffer) . ' -n'
endfunction
function! ale_linters#sh#shell#Handle(buffer, lines) abort
@@ -38,7 +38,7 @@ function! ale_linters#sh#shell#Handle(buffer, lines) abort
"
" bash: line 13: syntax error near unexpected token `d'
" sh: 11: Syntax error: "(" unexpected
let l:pattern = '\v(line |: ?)(\d+): (.+)$'
let l:pattern = '^[^:]\+: \%(\w\+ \|\)\(\d\+\): \(.\+\)'
let l:output = []
for l:line in a:lines
@@ -48,15 +48,20 @@ function! ale_linters#sh#shell#Handle(buffer, lines) abort
continue
endif
let l:line = l:match[2] + 0
let l:text = l:match[3]
let l:line = l:match[1] + 0
let l:column = 1
let l:text = l:match[2]
let l:type = 'E'
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor

View File

@@ -1,33 +0,0 @@
" Author: Markus Doits - https://github.com/doits
" Description: slim-lint for Slim files, based on hamllint.vim
function! ale_linters#slim#slimlint#Handle(buffer, lines) abort
" Matches patterns like the following:
" <path>:5 [W] LineLength: Line is too long. [150/120]
let l:pattern = '\v^.*:(\d+) \[([EW])\] (.+)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'type': l:match[2],
\ 'text': l:match[3]
\})
endfor
return l:output
endfunction
call ale#linter#Define('slim', {
\ 'name': 'slimlint',
\ 'executable': 'slim-lint',
\ 'command': 'slim-lint %t',
\ 'callback': 'ale_linters#slim#slimlint#Handle'
\})

View File

@@ -1,39 +0,0 @@
" Author: Paulo Alem <paulo.alem@gmail.com>
" Description: Rudimentary SML checking with smlnj compiler
if exists('g:loaded_ale_sml_smlnj_checker')
finish
endif
let g:loaded_ale_sml_smlnj_checker = 1
function! ale_linters#sml#smlnj#Handle(buffer, lines) abort
" Try to match basic sml errors
let l:out = []
let l:pattern = '^.*\:\([0-9\.]\+\)\ \(\w\+\)\:\ \(.*\)'
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
call add(l:out, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'text': l:match[2] . ': ' . l:match[3],
\ 'type': l:match[2] ==# 'error' ? 'E' : 'W',
\})
endfor
return l:out
endfunction
call g:ale#linter#Define('sml', {
\ 'name': 'smlnj',
\ 'executable': 'sml',
\ 'command': 'sml',
\ 'callback': 'ale_linters#sml#smlnj#Handle',
\})

View File

@@ -44,9 +44,11 @@ function! ale_linters#tex#chktex#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[4] . ' (' . (l:match[3]+0) . ')',
\ 'type': 'W',
\ 'nr': -1
\})
endfor

View File

@@ -4,14 +4,6 @@
let g:ale_tex_lacheck_executable =
\ get(g:, 'ale_tex_lacheck_executable', 'lacheck')
function! ale_linters#tex#lacheck#GetExecutable(buffer) abort
return g:ale_tex_lacheck_executable
endfunction
function! ale_linters#tex#lacheck#GetCommand(buffer) abort
return g:ale_tex_lacheck_executable . ' %t'
endfunction
function! ale_linters#tex#lacheck#Handle(buffer, lines) abort
" Mattes lines like:
"
@@ -38,9 +30,11 @@ function! ale_linters#tex#lacheck#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': 0,
\ 'text': l:match[2],
\ 'type': 'W',
\ 'nr': -1
\})
endfor
@@ -49,7 +43,8 @@ endfunction
call ale#linter#Define('tex', {
\ 'name': 'lacheck',
\ 'executable_callback': 'ale_linters#tex#lacheck#GetExecutable',
\ 'command_callback': 'ale_linters#tex#lacheck#GetCommand',
\ 'executable': 'lacheck',
\ 'command': g:ale#util#stdin_wrapper . ' .tex '
\ . g:ale_tex_lacheck_executable,
\ 'callback': 'ale_linters#tex#lacheck#Handle'
\})

View File

@@ -1,9 +1,9 @@
" Author: poohzrn https://github.com/poohzrn
" Description: proselint for TeX files
" Description: proselint for tex files
call ale#linter#Define('tex', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'command': g:ale#util#stdin_wrapper . ' .tex proselint',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for Texinfo files
call ale#linter#Define('texinfo', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -4,6 +4,6 @@
call ale#linter#Define('text', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\ 'command': g:ale#util#stdin_wrapper . ' .txt proselint',
\})

View File

@@ -1,20 +1,6 @@
" Author: Prashanth Chandra https://github.com/prashcr
" Description: tslint for TypeScript files
let g:ale_typescript_tslint_executable =
\ get(g:, 'ale_typescript_tslint_executable', 'tslint')
let g:ale_typescript_tslint_config_path =
\ get(g:, 'ale_typescript_tslint_config_path', '')
function! ale_linters#typescript#tslint#GetExecutable(buffer) abort
return ale#util#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/tslint',
\ g:ale_typescript_tslint_executable
\)
endfunction
function! ale_linters#typescript#tslint#Handle(buffer, lines) abort
" Matches patterns like the following:
"
@@ -37,37 +23,33 @@ function! ale_linters#typescript#tslint#Handle(buffer, lines) abort
let l:type = 'E'
let l:text = l:match[3]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor
return l:output
endfunction
function! ale_linters#typescript#tslint#BuildLintCommand(buffer) abort
let g:ale_typescript_tslint_config_path =
\ empty(g:ale_typescript_tslint_config_path) ?
\ ale#util#FindNearestFile(a:buffer, 'tslint.json')
\ : g:ale_typescript_tslint_config_path
function! ale_linters#typescript#tslint#BuildLintCommand(buffer_n) abort
let l:tsconfig_path = ale#util#FindNearestFile(a:buffer_n, 'tslint.json')
let l:tslint_options = empty(l:tsconfig_path) ? '' : '-c ' . l:tsconfig_path
let l:tslint_options =
\ empty(g:ale_typescript_tslint_config_path) ?
\ ''
\ : '-c ' . fnameescape(g:ale_typescript_tslint_config_path)
let l:ext = '.' . fnamemodify(bufname(a:buffer_n), ':e')
return ale_linters#typescript#tslint#GetExecutable(a:buffer)
\ . ' ' . l:tslint_options
\ . ' %t'
return g:ale#util#stdin_wrapper . ' ' . l:ext . ' tslint ' . l:tslint_options
endfunction
call ale#linter#Define('typescript', {
\ 'name': 'tslint',
\ 'executable_callback': 'ale_linters#typescript#tslint#GetExecutable',
\ 'executable': 'tslint',
\ 'command_callback': 'ale_linters#typescript#tslint#BuildLintCommand',
\ 'callback': 'ale_linters#typescript#tslint#Handle',
\})

View File

@@ -22,12 +22,15 @@ function! ale_linters#typescript#typecheck#Handle(buffer, lines) abort
let l:type = 'E'
let l:text = l:match[3]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:column,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor

View File

@@ -25,8 +25,11 @@ function! ale_linters#verilog#iverilog#Handle(buffer, lines) abort
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': 1,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor
@@ -37,6 +40,6 @@ call ale#linter#Define('verilog', {
\ 'name': 'iverilog',
\ 'output_stream': 'stderr',
\ 'executable': 'iverilog',
\ 'command': 'iverilog -t null -Wall %t',
\ 'command': g:ale#util#stdin_wrapper . ' .v iverilog -t null -Wall',
\ 'callback': 'ale_linters#verilog#iverilog#Handle',
\})

View File

@@ -1,16 +1,6 @@
" Author: Masahiro H https://github.com/mshr-h
" Description: verilator for verilog files
function! ale_linters#verilog#verilator#GetCommand(buffer) abort
let l:filename = tempname() . '_verilator_linted.v'
" Create a special filename, so we can detect it in the handler.
call ale#engine#ManageFile(a:buffer, l:filename)
call writefile(getbufline(a:buffer, 1, '$'), l:filename)
return 'verilator --lint-only -Wall -Wno-DECLFILENAME ' . fnameescape(l:filename)
endfunction
function! ale_linters#verilog#verilator#Handle(buffer, lines) abort
" Look for lines like the following.
"
@@ -35,13 +25,16 @@ function! ale_linters#verilog#verilator#Handle(buffer, lines) abort
let l:text = l:match[4]
let l:file = l:match[2]
if l:file =~# '_verilator_linted.v'
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'text': l:text,
\ 'type': l:type,
\})
if(l:file =~# '_verilator_linted.v')
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': 1,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endif
endfor
@@ -52,7 +45,6 @@ call ale#linter#Define('verilog', {
\ 'name': 'verilator',
\ 'output_stream': 'stderr',
\ 'executable': 'verilator',
\ 'command_callback': 'ale_linters#verilog#verilator#GetCommand',
\ 'command': g:ale#util#stdin_wrapper . ' _verilator_linted.v verilator --lint-only -Wall -Wno-DECLFILENAME',
\ 'callback': 'ale_linters#verilog#verilator#Handle',
\ 'read_buffer': 0,
\})

View File

@@ -6,19 +6,17 @@ let g:ale_vim_vint_show_style_issues =
\ get(g:, 'ale_vim_vint_show_style_issues', 1)
let s:warning_flag = g:ale_vim_vint_show_style_issues ? '-s' : '-w'
let s:vint_version = ale#semver#Parse(system('vint --version'))
let s:has_no_color_support = ale#semver#GreaterOrEqual(s:vint_version, [0, 3, 7])
let s:enable_neovim = has('nvim') ? ' --enable-neovim ' : ''
let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})"'
call ale#linter#Define('vim', {
\ 'name': 'vint',
\ 'executable': 'vint',
\ 'command': 'vint '
\ . s:warning_flag . ' '
\ . (s:has_no_color_support ? '--no-color ' : '')
\ 'command': g:ale#util#stdin_wrapper
\ . ' .vim vint '
\ . s:warning_flag
\ . ' --no-color '
\ . s:enable_neovim
\ . s:format
\ . ' %t',
\ . s:format,
\ 'callback': 'ale#handlers#HandleGCCFormat',
\})

View File

@@ -1,9 +0,0 @@
" Author: Daniel M. Capella https://github.com/polyzen
" Description: proselint for XHTML files
call ale#linter#Define('xhtml', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
\})

View File

@@ -1,21 +1,5 @@
" Author: KabbAmine <amine.kabb@gmail.com>
let g:ale_yaml_yamllint_executable =
\ get(g:, 'ale_yaml_yamllint_executable', 'yamllint')
let g:ale_yaml_yamllint_options =
\ get(g:, 'ale_yaml_yamllint_options', '')
function! ale_linters#yaml#yamllint#GetExecutable(buffer) abort
return g:ale_yaml_yamllint_executable
endfunction
function! ale_linters#yaml#yamllint#GetCommand(buffer) abort
return ale_linters#yaml#yamllint#GetExecutable(a:buffer)
\ . ' ' . g:ale_yaml_yamllint_options
\ . ' -f parsable %t'
endfunction
function! ale_linters#yaml#yamllint#Handle(buffer, lines) abort
" Matches patterns line the following:
" something.yaml:1:1: [warning] missing document start "---" (document-start)
@@ -35,12 +19,15 @@ function! ale_linters#yaml#yamllint#Handle(buffer, lines) abort
let l:type = l:match[3]
let l:text = l:match[4]
" vcol is Needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:line,
\ 'vcol': 0,
\ 'col': l:col,
\ 'text': l:text,
\ 'type': l:type ==# 'error' ? 'E' : 'W',
\ 'nr': -1,
\})
endfor
@@ -49,7 +36,7 @@ endfunction
call ale#linter#Define('yaml', {
\ 'name': 'yamllint',
\ 'executable_callback': 'ale_linters#yaml#yamllint#GetExecutable',
\ 'command_callback': 'ale_linters#yaml#yamllint#GetCommand',
\ 'executable': 'yamllint',
\ 'command': g:ale#util#stdin_wrapper . ' .yml yamllint -f parsable',
\ 'callback': 'ale_linters#yaml#yamllint#Handle',
\})

View File

@@ -3,41 +3,13 @@
" Manages execution of linters when requested by autocommands
let s:lint_timer = -1
let s:should_lint_file_for_buffer = {}
" A function for checking various conditions whereby ALE just shouldn't
" attempt to do anything, say if particular buffer types are open in Vim.
function! ale#ShouldDoNothing() abort
" Do nothing for blacklisted files
" OR if ALE is running in the sandbox
return index(g:ale_filetype_blacklist, &filetype) >= 0
\ || (exists('*getcmdwintype') && !empty(getcmdwintype()))
\ || ale#util#InSandbox()
endfunction
" (delay, [linting_flag])
function! ale#Queue(delay, ...) abort
if len(a:0) > 1
throw 'too many arguments!'
endif
" Default linting_flag to ''
let l:linting_flag = get(a:000, 0, '')
if l:linting_flag !=# '' && l:linting_flag !=# 'lint_file'
throw "linting_flag must be either '' or 'lint_file'"
endif
if ale#ShouldDoNothing()
function! ale#Queue(delay) abort
" Do nothing for blacklisted files.
if index(g:ale_filetype_blacklist, &filetype) >= 0
return
endif
" Remember that we want to check files for this buffer.
" We will remember this until we finally run the linters, via any event.
if l:linting_flag ==# 'lint_file'
let s:should_lint_file_for_buffer[bufnr('%')] = 1
endif
if s:lint_timer != -1
call timer_stop(s:lint_timer)
let s:lint_timer = -1
@@ -57,19 +29,13 @@ function! ale#Queue(delay, ...) abort
endfunction
function! ale#Lint(...) abort
if ale#ShouldDoNothing()
" Do nothing for blacklisted files.
if index(g:ale_filetype_blacklist, &filetype) >= 0
return
endif
let l:buffer = bufnr('%')
let l:linters = ale#linter#Get(&filetype)
let l:should_lint_file = 0
" Check if we previously requested checking the file.
if has_key(s:should_lint_file_for_buffer, l:buffer)
unlet s:should_lint_file_for_buffer[l:buffer]
let l:should_lint_file = 1
endif
" Initialise the buffer information if needed.
call ale#engine#InitBufferInfo(l:buffer)
@@ -77,22 +43,19 @@ function! ale#Lint(...) abort
" Clear the new loclist again, so we will work with all new items.
let g:ale_buffer_info[l:buffer].new_loclist = []
if l:should_lint_file
" Clear loclist items for files if we are checking files again.
let g:ale_buffer_info[l:buffer].lint_file_loclist = []
else
" Otherwise, don't run any `lint_file` linters
" We will continue running any linters which are currently checking
" the file, and the items will be mixed together with any new items.
call filter(l:linters, '!v:val.lint_file')
endif
for l:linter in l:linters
" Check if a given linter has a program which can be executed.
if has_key(l:linter, 'executable_callback')
let l:executable = ale#util#GetFunction(l:linter.executable_callback)(l:buffer)
else
let l:executable = l:linter.executable
endif
if !executable(l:executable)
" The linter's program cannot be executed, so skip it.
continue
endif
call ale#engine#Invoke(l:buffer, l:linter)
endfor
endfunction
" Reset flags indicating that files should be checked for all buffers.
function! ale#ResetLintFileMarkers() abort
let s:should_lint_file_for_buffer = {}
endfunction

View File

@@ -3,19 +3,11 @@
function! ale#cleanup#Buffer(buffer) abort
if has_key(g:ale_buffer_info, a:buffer)
call ale#engine#RemoveManagedFiles(a:buffer)
" When buffers are removed, clear all of the jobs.
for l:job in get(g:ale_buffer_info[a:buffer], 'job_list', [])
call ale#engine#ClearJob(l:job)
endfor
" Clear delayed highlights for a buffer being removed.
if g:ale_set_highlights
call ale#highlight#UnqueueHighlights(a:buffer)
call ale#highlight#RemoveHighlights([])
endif
call remove(g:ale_buffer_info, a:buffer)
endif
endfunction

View File

@@ -18,29 +18,6 @@ function! s:GetMessage(linter, type, text) abort
return printf(l:msg, l:text)
endfunction
function! s:EchoWithShortMess(setting, message) abort
" We need to remember the setting for shormess and reset it again.
let l:shortmess_options = getbufvar('%', '&shortmess')
try
" Turn shortmess on or off.
if a:setting ==# 'on'
setlocal shortmess+=T
" echomsg is neede for the message to get truncated and appear in
" the message history.
exec "norm! :echomsg a:message\n"
elseif a:setting ==# 'off'
setlocal shortmess-=T
" Regular echo is needed for printing newline characters.
echo a:message
else
throw 'Invalid setting: ' . string(a:setting)
endif
finally
call setbufvar('%', '&shortmess', l:shortmess_options)
endtry
endfunction
function! ale#cursor#TruncatedEcho(message) abort
let l:message = a:message
" Change tabs to spaces.
@@ -48,23 +25,17 @@ function! ale#cursor#TruncatedEcho(message) abort
" Remove any newlines in the message.
let l:message = substitute(l:message, "\n", '', 'g')
call s:EchoWithShortMess('on', l:message)
endfunction
" We need to turn T for truncated messages on for shortmess,
" and then then we need to reset the option back to what it was.
let l:shortmess_options = getbufvar('%', '&shortmess')
function! s:FindItemAtCursor() abort
let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
let l:pos = getcurpos()
let l:index = ale#util#BinarySearch(l:info.loclist, l:pos[1], l:pos[2])
let l:loc = l:index >= 0 ? l:info.loclist[l:index] : {}
return [l:info, l:loc]
endfunction
function! s:StopCursorTimer() abort
if s:cursor_timer != -1
call timer_stop(s:cursor_timer)
let s:cursor_timer = -1
endif
try
" Echo the message truncated to fit without creating a prompt.
setlocal shortmess+=T
exec "norm! :echomsg message\n"
finally
call setbufvar('%', '&shortmess', l:shortmess_options)
endtry
endfunction
function! ale#cursor#EchoCursorWarning(...) abort
@@ -73,17 +44,28 @@ function! ale#cursor#EchoCursorWarning(...) abort
return
endif
let [l:info, l:loc] = s:FindItemAtCursor()
let l:buffer = bufnr('%')
if !empty(l:loc)
if !has_key(g:ale_buffer_info, l:buffer)
return
endif
let l:pos = getcurpos()
let l:loclist = g:ale_buffer_info[l:buffer].loclist
let l:index = ale#util#BinarySearch(l:loclist, l:pos[1], l:pos[2])
if l:index >= 0
let l:loc = l:loclist[l:index]
let l:msg = s:GetMessage(l:loc.linter_name, l:loc.type, l:loc.text)
call ale#cursor#TruncatedEcho(l:msg)
let l:info.echoed = 1
elseif get(l:info, 'echoed')
let g:ale_buffer_info[l:buffer].echoed = 1
else
" We'll only clear the echoed message when moving off errors once,
" so we don't continually clear the echo line.
echo
let l:info.echoed = 0
if get(g:ale_buffer_info[l:buffer], 'echoed')
echo
let g:ale_buffer_info[l:buffer].echoed = 0
endif
endif
endfunction
@@ -91,11 +73,15 @@ let s:cursor_timer = -1
let s:last_pos = [0, 0, 0]
function! ale#cursor#EchoCursorWarningWithDelay() abort
if ale#ShouldDoNothing()
" Do nothing for blacklisted files.
if index(g:ale_filetype_blacklist, &filetype) >= 0
return
endif
call s:StopCursorTimer()
if s:cursor_timer != -1
call timer_stop(s:cursor_timer)
let s:cursor_timer = -1
endif
let l:pos = getcurpos()[0:2]
@@ -108,23 +94,3 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort
let s:cursor_timer = timer_start(10, function('ale#cursor#EchoCursorWarning'))
endif
endfunction
function! ale#cursor#ShowCursorDetail() abort
" Only echo the warnings in normal mode, otherwise we will get problems.
if mode() !=# 'n'
return
endif
call s:StopCursorTimer()
let [l:info, l:loc] = s:FindItemAtCursor()
if !empty(l:loc)
let l:message = get(l:loc, 'detail', l:loc.text)
call s:EchoWithShortMess('off', l:message)
" Set the echo marker, so we can clear it by moving the cursor.
let l:info.echoed = 1
endif
endfunction

View File

@@ -1,142 +0,0 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: This file implements debugging information for ALE
let s:global_variable_list = [
\ 'ale_echo_cursor',
\ 'ale_echo_msg_error_str',
\ 'ale_echo_msg_format',
\ 'ale_echo_msg_warning_str',
\ 'ale_enabled',
\ 'ale_keep_list_window_open',
\ 'ale_lint_delay',
\ 'ale_lint_on_enter',
\ 'ale_lint_on_save',
\ 'ale_lint_on_text_changed',
\ 'ale_linter_aliases',
\ 'ale_linters',
\ 'ale_open_list',
\ 'ale_set_highlights',
\ 'ale_set_loclist',
\ 'ale_set_quickfix',
\ 'ale_set_signs',
\ 'ale_sign_column_always',
\ 'ale_sign_error',
\ 'ale_sign_offset',
\ 'ale_sign_warning',
\ 'ale_statusline_format',
\ 'ale_warn_about_trailing_whitespace',
\]
function! s:GetLinterVariables(filetype, linter_names) abort
let l:variable_list = []
let l:filetype_parts = split(a:filetype, '\.')
for l:key in keys(g:)
" Extract variable names like: 'ale_python_flake8_executable'
let l:match = matchlist(l:key, '\v^ale_([^_]+)_([^_]+)_.+$')
" Include matching variables.
if !empty(l:match)
\&& index(l:filetype_parts, l:match[1]) >= 0
\&& index(a:linter_names, l:match[2]) >= 0
call add(l:variable_list, l:key)
endif
endfor
call sort(l:variable_list)
return l:variable_list
endfunction
function! s:EchoLinterVariables(variable_list) abort
for l:key in a:variable_list
echom 'let g:' . l:key . ' = ' . string(g:[l:key])
endfor
endfunction
function! s:EchoGlobalVariables() abort
for l:key in s:global_variable_list
echom 'let g:' . l:key . ' = ' . string(get(g:, l:key, v:null))
endfor
endfunction
function! s:EchoCommandHistory() abort
let l:buffer = bufnr('%')
if !has_key(g:ale_buffer_info, l:buffer)
return
endif
for l:item in g:ale_buffer_info[l:buffer].history
let l:status_message = l:item.status
" Include the exit code in output if we have it.
if l:item.status ==# 'finished'
let l:status_message .= ' - exit code ' . l:item.exit_code
endif
echom '(' . l:status_message . ') ' . string(l:item.command)
if g:ale_history_log_output && has_key(l:item, 'output')
if empty(l:item.output)
echom ''
echom '<<<NO OUTPUT RETURNED>>>'
echom ''
else
echom ''
echom '<<<OUTPUT STARTS>>>'
for l:line in l:item.output
echom l:line
endfor
echom '<<<OUTPUT ENDS>>>'
echom ''
endif
endif
endfor
endfunction
function! ale#debugging#Info() abort
let l:filetype = &filetype
" We get the list of enabled linters for free by the above function.
let l:enabled_linters = deepcopy(ale#linter#Get(l:filetype))
" But have to build the list of available linters ourselves.
let l:all_linters = []
let l:linter_variable_list = []
for l:part in split(l:filetype, '\.')
let l:aliased_filetype = ale#linter#ResolveFiletype(l:part)
call extend(l:all_linters, ale#linter#GetAll(l:aliased_filetype))
endfor
let l:all_names = map(l:all_linters, 'v:val[''name'']')
let l:enabled_names = map(l:enabled_linters, 'v:val[''name'']')
" Load linter variables to display
" This must be done after linters are loaded.
let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names)
echom ' Current Filetype: ' . l:filetype
echom 'Available Linters: ' . string(l:all_names)
echom ' Enabled Linters: ' . string(l:enabled_names)
echom ' Linter Variables:'
echom ''
call s:EchoLinterVariables(l:variable_list)
echom ' Global Variables:'
echom ''
call s:EchoGlobalVariables()
echom ' Command History:'
echom ''
call s:EchoCommandHistory()
endfunction
function! ale#debugging#InfoToClipboard() abort
redir @+>
silent call ale#debugging#Info()
redir END
echom 'ALEInfo copied to your clipboard'
endfunction

View File

@@ -9,53 +9,33 @@
" output: The array of lines for the output of the job.
let s:job_info_map = {}
function! ale#engine#ParseVim8ProcessID(job_string) abort
return matchstr(a:job_string, '\d\+') + 0
endfunction
function! s:GetJobID(job) abort
if has('nvim')
"In NeoVim, job values are just IDs.
return a:job
endif
" For Vim 8, the job is a different variable type, and we can parse the
" process ID from the string.
return ale#engine#ParseVim8ProcessID(string(a:job))
" In Vim 8, the job is a special variable, and we open a channel for each
" job. We'll use the ID of the channel instead as the job ID.
return ch_info(job_getchannel(a:job)).id
endfunction
function! ale#engine#InitBufferInfo(buffer) abort
if !has_key(g:ale_buffer_info, a:buffer)
" job_list will hold the list of jobs
" loclist holds the loclist items after all jobs have completed.
" lint_file_loclist holds items from the last run including linters
" which use the lint_file option.
" new_loclist holds loclist items while jobs are being run.
" temporary_file_list holds temporary files to be cleaned up
" temporary_directory_list holds temporary directories to be cleaned up
" history holds a list of previously run commands for this buffer
let g:ale_buffer_info[a:buffer] = {
\ 'job_list': [],
\ 'loclist': [],
\ 'lint_file_loclist': [],
\ 'new_loclist': [],
\ 'temporary_file_list': [],
\ 'temporary_directory_list': [],
\ 'history': [],
\}
endif
endfunction
" A map from timer IDs to Vim 8 jobs, for tracking jobs that need to be killed
" with SIGKILL if they don't terminate right away.
let s:job_kill_timers = {}
function! s:KillHandler(timer) abort
call job_stop(remove(s:job_kill_timers, a:timer), 'kill')
endfunction
function! ale#engine#ClearJob(job) abort
let l:job_id = s:GetJobID(a:job)
let l:linter = s:job_info_map[l:job_id].linter
if has('nvim')
call jobstop(a:job)
@@ -66,19 +46,10 @@ function! ale#engine#ClearJob(job) abort
call ch_close_in(job_getchannel(a:job))
endif
" Ask nicely for the job to stop.
call job_stop(a:job)
" If a job doesn't stop immediately, queue a timer which will
" send SIGKILL to the job, if it's alive by the time the timer ticks.
if job_status(a:job) ==# 'run'
let s:job_kill_timers[timer_start(100, function('s:KillHandler'))] = a:job
endif
endif
if has_key(s:job_info_map, l:job_id)
call remove(s:job_info_map, l:job_id)
endif
call remove(s:job_info_map, l:job_id)
endfunction
function! s:StopPreviousJobs(buffer, linter) abort
@@ -141,46 +112,6 @@ function! ale#engine#JoinNeovimOutput(output, data) abort
endif
endfunction
" Register a temporary file to be managed with the ALE engine for
" a current job run.
function! ale#engine#ManageFile(buffer, filename) abort
call add(g:ale_buffer_info[a:buffer].temporary_file_list, a:filename)
endfunction
" Same as the above, but manage an entire directory.
function! ale#engine#ManageDirectory(buffer, directory) abort
call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory)
endfunction
function! ale#engine#RemoveManagedFiles(buffer) abort
if !has_key(g:ale_buffer_info, a:buffer)
return
endif
" We can't delete anything in a sandbox, so wait until we escape from
" it to delete temporary files and directories.
if ale#util#InSandbox()
return
endif
" Delete files with a call akin to a plan `rm` command.
for l:filename in g:ale_buffer_info[a:buffer].temporary_file_list
call delete(l:filename)
endfor
let g:ale_buffer_info[a:buffer].temporary_file_list = []
" Delete directories like `rm -rf`.
" Directories are handled differently from files, so paths that are
" intended to be single files can be set up for automatic deletion without
" accidentally deleting entire directories.
for l:directory in g:ale_buffer_info[a:buffer].temporary_directory_list
call delete(l:directory, 'rf')
endfor
let g:ale_buffer_info[a:buffer].temporary_directory_list = []
endfunction
function! s:HandleExit(job) abort
if a:job ==# 'no process'
" Stop right away when the job is not valid in Vim 8.
@@ -203,110 +134,59 @@ function! s:HandleExit(job) abort
" which just closed.
call s:StopPreviousJobs(l:buffer, l:linter)
" Stop here if we land in the handle for a job completing if we're in
" a sandbox.
if ale#util#InSandbox()
return
endif
if l:next_chain_index < len(get(l:linter, 'command_chain', []))
call s:InvokeChain(l:buffer, l:linter, l:next_chain_index, l:output)
return
endif
" Log the output of the command for ALEInfo if we should.
if g:ale_history_enabled && g:ale_history_log_output
call ale#history#RememberOutput(l:buffer, l:job_id, l:output[:])
endif
let l:linter_loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output)
" Make some adjustments to the loclists to fix common problems, and also
" to set default values for loclist items.
let l:linter_loclist = ale#engine#FixLocList(l:buffer, l:linter, l:linter_loclist)
" Make some adjustments to the loclists to fix common problems.
call s:FixLocList(l:buffer, l:linter_loclist)
for l:item in l:linter_loclist
let l:item.linter_name = l:linter.name
endfor
" Add the loclist items from the linter.
" loclist items for files which are checked go into a different list,
" and are kept between runs.
if l:linter.lint_file
call extend(g:ale_buffer_info[l:buffer].lint_file_loclist, l:linter_loclist)
else
call extend(g:ale_buffer_info[l:buffer].new_loclist, l:linter_loclist)
endif
call extend(g:ale_buffer_info[l:buffer].new_loclist, l:linter_loclist)
if !empty(g:ale_buffer_info[l:buffer].job_list)
" Wait for all jobs to complete before doing anything else.
return
endif
" Automatically remove all managed temporary files and directories
" now that all jobs have completed.
call ale#engine#RemoveManagedFiles(l:buffer)
" Combine the lint_file List and the List for everything else.
let l:combined_list = g:ale_buffer_info[l:buffer].lint_file_loclist
\ + g:ale_buffer_info[l:buffer].new_loclist
" Sort the loclist again.
" We need a sorted list so we can run a binary search against it
" for efficient lookup of the messages in the cursor handler.
call sort(l:combined_list, 'ale#util#LocItemCompare')
call sort(g:ale_buffer_info[l:buffer].new_loclist, 'ale#util#LocItemCompare')
" Now swap the old and new loclists, after we have collected everything
" and sorted the list again.
let g:ale_buffer_info[l:buffer].loclist = l:combined_list
let g:ale_buffer_info[l:buffer].loclist = g:ale_buffer_info[l:buffer].new_loclist
let g:ale_buffer_info[l:buffer].new_loclist = []
call ale#engine#SetResults(l:buffer, g:ale_buffer_info[l:buffer].loclist)
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd User ALELint
endfunction
function! ale#engine#SetResults(buffer, loclist) abort
" Set signs first. This could potentially fix some line numbers.
" The List could be sorted again here by SetSigns.
if g:ale_set_signs
call ale#sign#SetSigns(a:buffer, a:loclist)
if g:ale_set_quickfix || g:ale_set_loclist
call ale#list#SetLists(g:ale_buffer_info[l:buffer].loclist)
endif
if g:ale_set_quickfix || g:ale_set_loclist
call ale#list#SetLists(a:buffer, a:loclist)
if g:ale_set_signs
call ale#sign#SetSigns(l:buffer, g:ale_buffer_info[l:buffer].loclist)
endif
if exists('*ale#statusline#Update')
" Don't load/run if not already loaded.
call ale#statusline#Update(a:buffer, a:loclist)
call ale#statusline#Update(l:buffer, g:ale_buffer_info[l:buffer].loclist)
endif
if g:ale_set_highlights
call ale#highlight#SetHighlights(a:buffer, a:loclist)
endif
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd User ALELint
if g:ale_echo_cursor
" Try and echo the warning now.
" This will only do something meaningful if we're in normal mode.
call ale#cursor#EchoCursorWarning()
endif
" Mark line 200, column 17 with a squiggly line or something
" matchadd('ALEError', '\%200l\%17v')
endfunction
function! s:SetExitCode(job, exit_code) abort
let l:job_id = s:GetJobID(a:job)
if !has_key(s:job_info_map, l:job_id)
return
endif
let l:buffer = s:job_info_map[l:job_id].buffer
call ale#history#SetExitCode(l:buffer, l:job_id, a:exit_code)
endfunction
function! s:HandleExitNeoVim(job, exit_code, event) abort
if g:ale_history_enabled
call s:SetExitCode(a:job, a:exit_code)
endif
function! s:HandleExitNeoVim(job, data, event) abort
call s:HandleExit(a:job)
endfunction
@@ -314,154 +194,34 @@ function! s:HandleExitVim(channel) abort
call s:HandleExit(ch_getjob(a:channel))
endfunction
" Vim returns the exit status with one callback,
" and the channel will close later in another callback.
function! s:HandleExitStatusVim(job, exit_code) abort
call s:SetExitCode(a:job, a:exit_code)
endfunction
function! ale#engine#FixLocList(buffer, linter, loclist) abort
let l:new_loclist = []
function! s:FixLocList(buffer, loclist) abort
" Some errors have line numbers beyond the end of the file,
" so we need to adjust them so they set the error at the last line
" of the file instead.
let l:last_line_number = ale#util#GetLineCount(a:buffer)
for l:old_item in a:loclist
" Copy the loclist item with some default values and corrections.
"
" line and column numbers will be converted to numbers.
" The buffer will default to the buffer being checked.
" The vcol setting will default to 0, a byte index.
" The error type will default to 'E' for errors.
" The error number will default to -1.
"
" The line number and text are the only required keys.
"
" The linter_name will be set on the errors so it can be used in
" output, filtering, etc..
let l:item = {
\ 'text': l:old_item.text,
\ 'lnum': str2nr(l:old_item.lnum),
\ 'col': str2nr(get(l:old_item, 'col', 0)),
\ 'bufnr': get(l:old_item, 'bufnr', a:buffer),
\ 'vcol': get(l:old_item, 'vcol', 0),
\ 'type': get(l:old_item, 'type', 'E'),
\ 'nr': get(l:old_item, 'nr', -1),
\ 'linter_name': a:linter.name,
\}
if has_key(l:old_item, 'detail')
let l:item.detail = l:old_item.detail
endif
for l:item in a:loclist
if l:item.lnum == 0
" When errors appear at line 0, put them at line 1 instead.
let l:item.lnum = 1
elseif l:item.lnum > l:last_line_number
" When errors go beyond the end of the file, put them at the end.
let l:item.lnum = l:last_line_number
endif
call add(l:new_loclist, l:item)
endfor
return l:new_loclist
endfunction
" Given part of a command, replace any % with %%, so that no characters in
" the string will be replaced with filenames, etc.
function! ale#engine#EscapeCommandPart(command_part) abort
return substitute(a:command_part, '%', '%%', 'g')
endfunction
function! s:TemporaryFilename(buffer) abort
let l:filename = fnamemodify(bufname(a:buffer), ':t')
if empty(l:filename)
" If the buffer's filename is empty, create a dummy filename.
let l:ft = getbufvar(a:buffer, '&filetype')
let l:filename = 'file' . ale#filetypes#GuessExtension(l:ft)
endif
" Create a temporary filename, <temp_dir>/<original_basename>
" The file itself will not be created by this function.
return tempname() . (has('win32') ? '\' : '/') . l:filename
endfunction
" Given a command string, replace every...
" %s -> with the current filename
" %t -> with the name of an unused file in a temporary directory
" %% -> with a literal %
function! ale#engine#FormatCommand(buffer, command) abort
let l:temporary_file = ''
function! s:RunJob(command, generic_job_options) abort
let l:buffer = a:generic_job_options.buffer
let l:linter = a:generic_job_options.linter
let l:output_stream = a:generic_job_options.output_stream
let l:next_chain_index = a:generic_job_options.next_chain_index
let l:read_buffer = a:generic_job_options.read_buffer
let l:command = a:command
" First replace all uses of %%, used for literal percent characters,
" with an ugly string.
let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g')
" Replace all %s occurences in the string with the name of the current
" file.
if l:command =~# '%s'
let l:filename = fnamemodify(bufname(a:buffer), ':p')
let l:command = substitute(l:command, '%s', '\=fnameescape(l:filename)', 'g')
endif
if l:command =~# '%t'
" Create a temporary filename, <temp_dir>/<original_basename>
" The file itself will not be created by this function.
let l:temporary_file = s:TemporaryFilename(a:buffer)
let l:command = substitute(l:command, '%t', '\=fnameescape(l:temporary_file)', 'g')
endif
" Finish formatting so %% becomes %.
let l:command = substitute(l:command, '<<PERCENTS>>', '%', 'g')
return [l:temporary_file, l:command]
endfunction
function! s:CreateTemporaryFileForJob(buffer, temporary_file) abort
if empty(a:temporary_file)
" There is no file, so we didn't create anything.
return 0
endif
let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
" Create the temporary directory for the file, unreadable by 'other'
" users.
call mkdir(l:temporary_directory, '', 0750)
" Automatically delete the directory later.
call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
" Write the buffer out to a file.
call writefile(getbufline(a:buffer, 1, '$'), a:temporary_file)
return 1
endfunction
function! s:RunJob(options) abort
let l:command = a:options.command
let l:buffer = a:options.buffer
let l:linter = a:options.linter
let l:output_stream = a:options.output_stream
let l:next_chain_index = a:options.next_chain_index
let l:read_buffer = a:options.read_buffer
let [l:temporary_file, l:command] = ale#engine#FormatCommand(l:buffer, l:command)
if l:read_buffer && empty(l:temporary_file)
" If we are to send the Vim buffer to a command, we'll do it
" in the shell. We'll write out the file to a temporary file,
" and then read it back in, in the shell.
let l:temporary_file = s:TemporaryFilename(l:buffer)
let l:command = l:command . ' < ' . fnameescape(l:temporary_file)
endif
if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
" If a temporary filename has been formatted in to the command, then
" we do not need to send the Vim buffer to the command.
let l:read_buffer = 0
" If there is a '%s' in the command string, replace it with the name
" of the file.
let l:command = printf(l:command, shellescape(fnamemodify(bufname(l:buffer), ':p')))
endif
if has('nvim')
@@ -491,12 +251,6 @@ function! s:RunJob(options) abort
\ 'close_cb': function('s:HandleExitVim'),
\}
if g:ale_history_enabled
" We only need to capture the exit status if we are going to
" save it in the history. Otherwise, we don't care.
let l:job_options.exit_cb = function('s:HandleExitStatusVim')
endif
if l:output_stream ==# 'stderr'
" Read from stderr instead of stdout.
let l:job_options.err_cb = function('s:GatherOutputVim')
@@ -508,50 +262,63 @@ function! s:RunJob(options) abort
let l:job_options.out_cb = function('s:GatherOutputVim')
endif
" The command will be executed in a subshell. This fixes a number of
" issues, including reading the PATH variables correctly, %PATHEXT%
" expansion on Windows, etc.
"
" NeoVim handles this issue automatically if the command is a String.
let l:command = has('win32')
\ ? 'cmd /c ' . l:command
\ : split(&shell) + split(&shellcmdflag) + [l:command]
if has('win32')
" job_start commands on Windows have to be run with cmd /c,
" othwerwise %PATHTEXT% will not be used to programs ending int
" .cmd, .bat, .exe, etc.
let l:command = 'cmd /c ' . l:command
else
" Execute the command with the shell, to fix escaping issues.
let l:command = split(&shell) + split(&shellcmdflag) + [l:command]
if l:read_buffer
" On Unix machines, we can send the Vim buffer directly.
" This is faster than reading the lines ourselves.
let l:job_options.in_io = 'buffer'
let l:job_options.in_buf = l:buffer
endif
endif
" Vim 8 will read the stdin from the file's buffer.
let l:job = job_start(l:command, l:job_options)
endif
let l:status = 'failed'
let l:job_id = 0
" Only proceed if the job is being run.
if has('nvim') || (l:job !=# 'no process' && job_status(l:job) ==# 'run')
" Add the job to the list of jobs, so we can track them.
call add(g:ale_buffer_info[l:buffer].job_list, l:job)
let l:status = 'started'
let l:job_id = s:GetJobID(l:job)
" Store the ID for the job in the map to read back again.
let s:job_info_map[l:job_id] = {
let s:job_info_map[s:GetJobID(l:job)] = {
\ 'linter': l:linter,
\ 'buffer': l:buffer,
\ 'output': [],
\ 'next_chain_index': l:next_chain_index,
\}
endif
if g:ale_history_enabled
call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
else
let g:ale_buffer_info[l:buffer].history = []
if l:read_buffer
if has('nvim')
" In NeoVim, we have to send the buffer lines ourselves.
let l:input = join(getbufline(l:buffer, 1, '$'), "\n") . "\n"
call jobsend(l:job, l:input)
call jobclose(l:job, 'stdin')
elseif has('win32')
" On some Vim versions, we have to send the buffer data ourselves.
let l:input = join(getbufline(l:buffer, 1, '$'), "\n") . "\n"
let l:channel = job_getchannel(l:job)
if ch_status(l:channel) ==# 'open'
call ch_sendraw(l:channel, l:input)
call ch_close_in(l:channel)
endif
endif
endif
endif
endfunction
" Determine which commands to run for a link in a command chain, or
" just a regular command.
function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
function! s:InvokeChain(buffer, linter, chain_index, input) abort
let l:output_stream = get(a:linter, 'output_stream', 'stdout')
let l:read_buffer = a:linter.read_buffer
let l:chain_index = a:chain_index
let l:input = a:input
@@ -561,6 +328,11 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
" so that many programs can be run in a sequence.
let l:chain_item = a:linter.command_chain[l:chain_index]
" The chain item can override the output_stream option.
if has_key(l:chain_item, 'output_stream')
let l:output_stream = l:chain_item.output_stream
endif
if l:chain_index == 0
" The first callback in the chain takes only a buffer number.
let l:command = ale#util#GetFunction(l:chain_item.callback)(
@@ -576,21 +348,6 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
if !empty(l:command)
" We hit a command to run, so we'll execute that
" The chain item can override the output_stream option.
if has_key(l:chain_item, 'output_stream')
let l:output_stream = l:chain_item.output_stream
endif
" The chain item can override the read_buffer option.
if has_key(l:chain_item, 'read_buffer')
let l:read_buffer = l:chain_item.read_buffer
elseif l:chain_index != len(a:linter.command_chain) - 1
" Don't read the buffer for commands besides the last one
" in the chain by default.
let l:read_buffer = 0
endif
break
endif
@@ -600,6 +357,11 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
let l:input = []
let l:chain_index += 1
endwhile
if empty(l:command)
" Don't run any jobs if the last command is an empty string.
return
endif
elseif has_key(a:linter, 'command_callback')
" If there is a callback for generating a command, call that instead.
let l:command = ale#util#GetFunction(a:linter.command_callback)(a:buffer)
@@ -607,45 +369,21 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
let l:command = a:linter.command
endif
if empty(l:command)
" Don't run any jobs if the command is an empty string.
return {}
endif
let l:is_last_job = l:chain_index >= len(get(a:linter, 'command_chain', [])) - 1
return {
\ 'command': l:command,
call s:RunJob(l:command, {
\ 'buffer': a:buffer,
\ 'linter': a:linter,
\ 'output_stream': l:output_stream,
\ 'next_chain_index': l:chain_index + 1,
\ 'read_buffer': l:read_buffer,
\}
endfunction
function! s:InvokeChain(buffer, linter, chain_index, input) abort
let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input)
if !empty(l:options)
call s:RunJob(l:options)
elseif empty(g:ale_buffer_info[a:buffer].job_list)
" If we cancelled running a command, and we have no jobs in progress,
" then delete the managed temporary files now.
call ale#engine#RemoveManagedFiles(a:buffer)
endif
\ 'read_buffer': l:is_last_job,
\})
endfunction
function! ale#engine#Invoke(buffer, linter) abort
" Stop previous jobs for the same linter.
call s:StopPreviousJobs(a:buffer, a:linter)
let l:executable = has_key(a:linter, 'executable_callback')
\ ? ale#util#GetFunction(a:linter.executable_callback)(a:buffer)
\ : a:linter.executable
" Run this program if it can be executed.
if executable(l:executable)
call s:InvokeChain(a:buffer, a:linter, 0, [])
endif
call s:InvokeChain(a:buffer, a:linter, 0, [])
endfunction
" Given a buffer number, return the warnings and errors for a given buffer.
@@ -664,7 +402,7 @@ endfunction
" The time taken will be a very rough approximation, and more time may be
" permitted than is specified.
function! ale#engine#WaitForJobs(deadline) abort
let l:start_time = ale#util#ClockMilliseconds()
let l:start_time = system('date +%s%3N') + 0
if l:start_time == 0
throw 'Failed to read milliseconds from the clock!'
@@ -684,7 +422,7 @@ function! ale#engine#WaitForJobs(deadline) abort
for l:job in l:job_list
if job_status(l:job) ==# 'run'
let l:now = ale#util#ClockMilliseconds()
let l:now = system('date +%s%3N') + 0
if l:now - l:start_time > a:deadline
" Stop waiting after a timeout, so we don't wait forever.
@@ -709,19 +447,15 @@ function! ale#engine#WaitForJobs(deadline) abort
" for command_chain linters.
let l:has_new_jobs = 0
" Check again to see if any jobs are running.
for l:info in values(g:ale_buffer_info)
for l:job in l:info.job_list
if job_status(l:job) ==# 'run'
let l:has_new_jobs = 1
break
endif
endfor
if !empty(l:info.job_list)
let l:has_new_jobs = 1
endif
endfor
if l:has_new_jobs
" We have to wait more. Offset the timeout by the time taken so far.
let l:now = ale#util#ClockMilliseconds()
let l:now = system('date +%s%3N') + 0
let l:new_deadline = a:deadline - (l:now - l:start_time)
if l:new_deadline <= 0

View File

@@ -1,60 +0,0 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: This file handles guessing file extensions for filetypes, etc.
function! ale#filetypes#LoadExtensionMap() abort
" Output includes:
" '*.erl setf erlang'
redir => l:output
silent exec 'autocmd'
redir end
let l:map = {}
for l:line in split(l:output, "\n")
" Parse filetypes, like so:
"
" *.erl setf erlang
" *.md set filetype=markdown
" *.snippet setlocal filetype=snippets
let l:match = matchlist(l:line, '\v^ *\*(\.[^ ]+).*set(f *| *filetype=|local *filetype=)([^ ]+)')
if !empty(l:match)
let l:map[substitute(l:match[3], '^=', '', '')] = l:match[1]
endif
endfor
return l:map
endfunction
let s:cached_map = {}
function! s:GetCachedExtensionMap() abort
if empty(s:cached_map)
let s:cached_map = ale#filetypes#LoadExtensionMap()
endif
return s:cached_map
endfunction
function! ale#filetypes#GuessExtension(filetype) abort
let l:map = s:GetCachedExtensionMap()
let l:ext = get(l:map, a:filetype, '')
" If we have an exact match, like something for javascript.jsx, use that.
if !empty(l:ext)
return l:ext
endif
" If we don't have an exact match, use the first filetype in the compound
" filetype.
for l:part in split(a:filetype, '\.')
let l:ext = get(l:map, l:part, '')
if !empty(l:ext)
return l:ext
endif
endfor
" Return an empty string if we don't find anything.
return ''
endfunction

View File

@@ -220,58 +220,3 @@ function! ale#handlers#HandleStyleLintFormat(buffer, lines) abort
return l:output
endfunction
function! ale#handlers#HandleGhcFormat(buffer, lines) abort
" Look for lines like the following.
"
"Appoint/Lib.hs:8:1: warning:
"Appoint/Lib.hs:8:1:
let l:pattern = '^[^:]\+:\(\d\+\):\(\d\+\):\(.*\)\?$'
let l:output = []
let l:corrected_lines = []
for l:line in a:lines
if len(matchlist(l:line, l:pattern)) > 0
call add(l:corrected_lines, l:line)
elseif l:line ==# ''
call add(l:corrected_lines, l:line)
else
if len(l:corrected_lines) > 0
let l:line = substitute(l:line, '\v^\s+', ' ', '')
let l:corrected_lines[-1] .= l:line
endif
endif
endfor
for l:line in l:corrected_lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
let l:errors = matchlist(l:match[3], '\(warning:\|error:\)\(.*\)')
if len(l:errors) > 0
let l:type = l:errors[1]
let l:text = l:errors[2]
else
let l:type = ''
let l:text = l:match[3]
endif
let l:type = l:type ==# '' ? 'E' : toupper(l:type[0])
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'vcol': 0,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
\ 'type': l:type,
\ 'nr': -1,
\})
endfor
return l:output
endfunction

View File

@@ -1,109 +0,0 @@
scriptencoding utf8
" Author: w0rp <devw0rp@gmail.com>
" Description: This module implements error/warning highlighting.
if !hlexists('ALEError')
highlight link ALEError SpellBad
endif
if !hlexists('ALEWarning')
highlight link ALEWarning SpellCap
endif
" This map holds highlights to be set when buffers are opened.
" We can only set highlights for whatever the current buffer is, so we will
" wait until the buffer is entered again to show the highlights, unless
" the buffer is in focus when linting completes.
let s:buffer_highlights = {}
function! ale#highlight#UnqueueHighlights(buffer) abort
if has_key(s:buffer_highlights, a:buffer)
call remove(s:buffer_highlights, a:buffer)
endif
endfunction
function! s:GetALEMatches() abort
let l:list = []
for l:match in getmatches()
if l:match['group'] ==# 'ALEError' || l:match['group'] ==# 'ALEWarning'
call add(l:list, l:match)
endif
endfor
return l:list
endfunction
function! s:GetCurrentMatchIDs(loclist) abort
let l:current_id_map = {}
for l:item in a:loclist
if has_key(l:item, 'match_id')
let l:current_id_map[l:item.match_id] = 1
endif
endfor
return l:current_id_map
endfunction
" Given a loclist for current items to highlight, remove all highlights
" except these which have matching loclist item entries.
function! ale#highlight#RemoveHighlights(loclist) abort
let l:current_id_map = s:GetCurrentMatchIDs(a:loclist)
for l:match in s:GetALEMatches()
if !has_key(l:current_id_map, l:match.id)
call matchdelete(l:match.id)
endif
endfor
endfunction
function! ale#highlight#UpdateHighlights() abort
let l:buffer = bufnr('%')
let l:has_new_items = has_key(s:buffer_highlights, l:buffer)
let l:loclist = l:has_new_items ? remove(s:buffer_highlights, l:buffer) : []
if l:has_new_items || !g:ale_enabled
call ale#highlight#RemoveHighlights(l:loclist)
endif
" Remove anything with a current match_id
call filter(l:loclist, '!has_key(v:val, ''match_id'')')
if l:has_new_items
for l:item in l:loclist
let l:col = l:item.col
let l:group = l:item.type ==# 'E' ? 'ALEError' : 'ALEWarning'
let l:line = l:item.lnum
let l:size = 1
" Rememeber the match ID for the item.
" This ID will be used to preserve loclist items which are set
" many times.
let l:item.match_id = matchaddpos(l:group, [[l:line, l:col, l:size]])
endfor
endif
endfunction
augroup ALEHighlightBufferGroup
autocmd!
autocmd BufEnter * call ale#highlight#UpdateHighlights()
augroup END
function! ale#highlight#SetHighlights(buffer, loclist) abort
" Only set set items for the buffer if ALE is enabled.
if g:ale_enabled
" Set a list of items to be set as highlights for a buffer when
" we next open it.
"
" We'll filter the loclist down to items we can set now.
let s:buffer_highlights[a:buffer] = filter(
\ copy(a:loclist),
\ 'v:val.bufnr == a:buffer && v:val.col > 0'
\)
" Update highlights for the current buffer, which may or may not
" be the buffer we just set highlights for.
call ale#highlight#UpdateHighlights()
endif
endfunction

View File

@@ -1,58 +0,0 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Tools for managing command history
"
function! ale#history#Add(buffer, status, job_id, command) abort
if g:ale_max_buffer_history_size <= 0
" Don't save anything if the history isn't a positive number.
let g:ale_buffer_info[a:buffer].history = []
return
endif
let l:history = g:ale_buffer_info[a:buffer].history
" Remove the first item if we hit the max history size.
if len(l:history) >= g:ale_max_buffer_history_size
let l:history = l:history[1:]
endif
call add(l:history, {
\ 'status': a:status,
\ 'job_id': a:job_id,
\ 'command': a:command,
\})
let g:ale_buffer_info[a:buffer].history = l:history
endfunction
function! s:FindHistoryItem(buffer, job_id) abort
" Search backwards to find a matching job ID. IDs might be recycled,
" so finding the last one should be good enough.
for l:obj in reverse(g:ale_buffer_info[a:buffer].history[:])
if l:obj.job_id == a:job_id
return l:obj
endif
endfor
return {}
endfunction
" Set an exit code for a command which finished.
function! ale#history#SetExitCode(buffer, job_id, exit_code) abort
let l:obj = s:FindHistoryItem(a:buffer, a:job_id)
if !empty(l:obj)
" If we find a match, then set the code and status.
let l:obj.exit_code = a:exit_code
let l:obj.status = 'finished'
endif
endfunction
" Set the output for a command which finished.
function! ale#history#RememberOutput(buffer, job_id, output) abort
let l:obj = s:FindHistoryItem(a:buffer, a:job_id)
if !empty(l:obj)
let l:obj.output = a:output
endif
endfunction

View File

@@ -18,14 +18,10 @@ let s:default_ale_linter_aliases = {
" The user defined linter selections will be merged with this Dictionary.
"
" No linters are used for plaintext files by default.
"
" Only cargo is enabled for Rust by default.
let s:default_ale_linters = {
\ 'csh': ['shell'],
\ 'help': [],
\ 'rust': ['cargo'],
\ 'text': [],
\ 'zsh': ['shell'],
\ 'csh': ['shell'],
\ 'text': [],
\}
" Testing/debugging helper to unload all linters.
@@ -37,10 +33,6 @@ function! s:IsCallback(value) abort
return type(a:value) == type('') || type(a:value) == type(function('type'))
endfunction
function! s:IsBoolean(value) abort
return type(a:value) == type(0) && (a:value == 0 || a:value == 1)
endfunction
function! ale#linter#PreProcess(linter) abort
if type(a:linter) != type({})
throw 'The linter object must be a Dictionary'
@@ -103,10 +95,6 @@ function! ale#linter#PreProcess(linter) abort
endif
endif
if has_key(l:link, 'read_buffer') && !s:IsBoolean(l:link.read_buffer)
throw l:err_prefix . 'value for `read_buffer` must be `0` or `1`'
endif
let l:link_index += 1
endfor
elseif has_key(a:linter, 'command_callback')
@@ -126,15 +114,6 @@ function! ale#linter#PreProcess(linter) abort
\ . 'must be defined'
endif
if (
\ has_key(a:linter, 'command')
\ + has_key(a:linter, 'command_chain')
\ + has_key(a:linter, 'command_callback')
\) > 1
throw 'Only one of `command`, `command_callback`, or `command_chain` '
\ . 'should be set'
endif
let l:obj.output_stream = get(a:linter, 'output_stream', 'stdout')
if type(l:obj.output_stream) != type('')
@@ -142,25 +121,6 @@ function! ale#linter#PreProcess(linter) abort
throw "`output_stream` must be 'stdout', 'stderr', or 'both'"
endif
" An option indicating that this linter should only be run against the
" file on disk.
let l:obj.lint_file = get(a:linter, 'lint_file', 0)
if !s:IsBoolean(l:obj.lint_file)
throw '`lint_file` must be `0` or `1`'
endif
" An option indicating that the buffer should be read.
let l:obj.read_buffer = get(a:linter, 'read_buffer', !l:obj.lint_file)
if !s:IsBoolean(l:obj.read_buffer)
throw '`read_buffer` must be `0` or `1`'
endif
if l:obj.lint_file && l:obj.read_buffer
throw 'Only one of `lint_file` or `read_buffer` can be `1`'
endif
return l:obj
endfunction
@@ -196,7 +156,7 @@ function! ale#linter#GetAll(filetype) abort
return s:linters[a:filetype]
endfunction
function! ale#linter#ResolveFiletype(original_filetype) abort
function! s:ResolveFiletype(original_filetype) abort
" Try and get an aliased file type either from the user's Dictionary, or
" our default Dictionary, otherwise use the filetype as-is.
let l:filetype = get(
@@ -217,7 +177,7 @@ function! ale#linter#Get(original_filetypes) abort
" Handle dot-seperated filetypes.
for l:original_filetype in split(a:original_filetypes, '\.')
let l:filetype = ale#linter#ResolveFiletype(l:original_filetype)
let l:filetype = s:ResolveFiletype(l:original_filetype)
" Try and get a list of linters to run, using the original file type,
" not the aliased filetype. We have some linters to limit by default,
@@ -251,3 +211,25 @@ function! ale#linter#Get(original_filetypes) abort
return l:combined_linters
endfunction
function! ale#linter#Info() abort
let l:original_filetypes = &filetype
" We get the list of enabled linters for free by the above function.
let l:enabled_linters = deepcopy(ale#linter#Get(l:original_filetypes))
" But have to build the list of available linters ourselves.
let l:all_linters = []
for l:original_filetype in split(l:original_filetypes, '\.')
let l:filetype = s:ResolveFiletype(l:original_filetype)
let l:filetype_linters = ale#linter#GetAll(l:filetype)
call extend(l:all_linters, l:filetype_linters)
endfor
let l:all_names = map(l:all_linters, 'v:val[''name'']')
let l:enabled_names = map(l:enabled_linters, 'v:val[''name'']')
echom ' Current Filetype: ' . l:original_filetypes
echom 'Available Linters: ' . string(l:all_names)
echom ' Enabled Linters: ' . string(l:enabled_names)
endfunction

View File

@@ -11,20 +11,11 @@ function! ale#list#IsQuickfixOpen() abort
return 0
endfunction
function! ale#list#SetLists(buffer, loclist) abort
function! ale#list#SetLists(loclist) abort
if g:ale_set_quickfix
call setqflist(a:loclist)
elseif g:ale_set_loclist
" If windows support is off, bufwinid() may not exist.
if exists('*bufwinid')
" Set the results on the window for the buffer.
call setloclist(bufwinid(str2nr(a:buffer)), a:loclist)
else
" Set the results in the current window.
" This may not be the same window we ran the linters for, but
" it's better than nothing.
call setloclist(0, a:loclist)
endif
call setloclist(0, a:loclist)
endif
" If we don't auto-open lists, bail out here.
@@ -36,12 +27,10 @@ function! ale#list#SetLists(buffer, loclist) abort
if len(a:loclist) > 0 || g:ale_keep_list_window_open
let l:winnr = winnr()
if !ale#list#IsQuickfixOpen()
if g:ale_set_quickfix
copen
elseif g:ale_set_loclist
lopen
endif
if g:ale_set_quickfix
copen
elseif g:ale_set_loclist
lopen
endif
" If focus changed, restore it (jump to the last window).

View File

@@ -1,6 +1,37 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: This file implements functions for jumping around in a file
" based on ALE's internal loclist.
" based on errors and warnings in the loclist or quickfix list.
function! s:GetCurrentList() abort
if g:ale_set_loclist
return getloclist(winnr())
elseif g:ale_set_quickfix
let l:buffer = bufnr('%')
return filter(getqflist(), 'get(v:val, ''bufnr'', -1) == ' . l:buffer)
endif
return []
endfunction
function! s:GetSortedLoclist() abort
let l:loclist = []
for l:item in s:GetCurrentList()
if l:item.lnum < 1
" Remove items we can't even jump to.
continue
endif
call add(l:loclist, l:item)
endfor
" We must sort the list again, as the loclist could contain items set
" by other plugins.
call sort(l:loclist, 'ale#util#LocItemCompare')
return l:loclist
endfunction
" Search for the nearest line either before or after the current position
" in the loclist. The argument 'wrap' can be passed to enable wrapping
@@ -10,15 +41,18 @@
" List will be returned, otherwise a pair of [line_number, column_number] will
" be returned.
function! ale#loclist_jumping#FindNearest(direction, wrap) abort
let l:pos = getcurpos()
let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
" This list will have already been sorted.
let l:loclist = l:info.loclist
let l:search_item = {'lnum': l:pos[1], 'col': l:pos[2]}
let l:loclist = s:GetSortedLoclist()
if empty(l:loclist)
" We couldn't find anything, so stop here.
return []
endif
let l:search_item = {'lnum': getcurpos()[1], 'col': getcurpos()[2]}
" When searching backwards, so we can find the next smallest match.
if a:direction ==# 'before'
let l:loclist = reverse(copy(l:loclist))
call reverse(l:loclist)
endif
" Look for items before or after the current position.
@@ -47,8 +81,8 @@ function! ale#loclist_jumping#FindNearest(direction, wrap) abort
" If we found nothing, and the wrap option is set to 1, then we should
" wrap around the list of warnings/errors
if a:wrap && !empty(l:loclist)
let l:item = l:loclist[0]
if a:wrap
let l:item = get(l:loclist, 0)
return [l:item.lnum, l:item.col]
endif

View File

@@ -12,6 +12,14 @@ if !hlexists('ALEWarningSign')
highlight link ALEWarningSign todo
endif
if !hlexists('ALEError')
highlight link ALEError SpellBad
endif
if !hlexists('ALEWarning')
highlight link ALEWarning SpellCap
endif
" Signs show up on the left for error markers.
execute 'sign define ALEErrorSign text=' . g:ale_sign_error
\ . ' texthl=ALEErrorSign'
@@ -28,30 +36,26 @@ function! ale#sign#ReadSigns(buffer) abort
return split(l:output, "\n")
endfunction
" Given a list of lines for sign output, return a List of pairs [line, id]
" Given a list of lines for sign output, return a list of sign IDs
function! ale#sign#ParseSigns(line_list) abort
" Matches output like :
" line=4 id=1 name=ALEErrorSign
" строка=1 id=1000001 имя=ALEErrorSign
" 行=1 識別子=1000001 名前=ALEWarningSign
" línea=12 id=1000001 nombre=ALEWarningSign
" riga=1 id=1000001, nome=ALEWarningSign
let l:pattern = '^.*=\(\d\+\).*=\(\d\+\).*=ALE\(Error\|Warning\|Dummy\)Sign'
let l:result = []
let l:pattern = '^.*=\d*\s\+.*=\(\d\+\)\s\+.*=ALE\(Warning\|Error\|Dummy\)Sign'
let l:id_list = []
for l:line in a:line_list
let l:match = matchlist(l:line, l:pattern)
if len(l:match) > 0
call add(l:result, [
\ str2nr(l:match[1]),
\ str2nr(l:match[2]),
\ 'ALE' . l:match[3] . 'Sign',
\])
call add(l:id_list, l:match[1] + 0)
endif
endfor
return l:result
return l:id_list
endfunction
function! ale#sign#FindCurrentSigns(buffer) abort
@@ -60,144 +64,90 @@ function! ale#sign#FindCurrentSigns(buffer) abort
return ale#sign#ParseSigns(l:line_list)
endfunction
" Given a loclist, group the List into with one List per line.
function! s:GroupLoclistItems(loclist) abort
let l:grouped_items = []
let l:last_lnum = -1
" Given a loclist, combine the loclist into a list of signs such that only
" one sign appears per line. Error lines will take precedence.
" The loclist will have been previously sorted.
function! ale#sign#CombineSigns(loclist) abort
let l:signlist = []
for l:obj in a:loclist
" Create a new sub-List when we hit a new line.
if l:obj.lnum != l:last_lnum
call add(l:grouped_items, [])
let l:should_append = 1
if l:obj.lnum < 1
" Skip warnings and errors at line 0, etc.
continue
endif
call add(l:grouped_items[-1], l:obj)
let l:last_lnum = l:obj.lnum
endfor
if len(l:signlist) > 0 && l:signlist[-1].lnum == l:obj.lnum
" We can't add the same line twice, because signs must be
" unique per line.
let l:should_append = 0
return l:grouped_items
endfunction
function! s:IsDummySignSet(current_id_list) abort
for [l:line, l:id, l:name] in a:current_id_list
if l:id == g:ale_sign_offset
return 1
endif
if l:line > 1
return 0
endif
endfor
return 0
endfunction
function! s:SetDummySignIfNeeded(buffer, current_sign_list, new_signs) abort
let l:is_dummy_sign_set = s:IsDummySignSet(a:current_sign_list)
" If we haven't already set a dummy sign, and we have some previous signs
" or always want a dummy sign, then set one, to keep the sign column open.
if !l:is_dummy_sign_set && (a:new_signs || g:ale_sign_column_always)
execute 'sign place ' . g:ale_sign_offset
\ . ' line=1 name=ALEDummySign buffer='
\ . a:buffer
let l:is_dummy_sign_set = 1
endif
return l:is_dummy_sign_set
endfunction
function! s:PlaceNewSigns(buffer, grouped_items) abort
" Add the new signs,
for l:index in range(0, len(a:grouped_items) - 1)
let l:sign_id = l:index + g:ale_sign_offset + 1
let l:sublist = a:grouped_items[l:index]
let l:type = !empty(filter(copy(l:sublist), 'v:val.type ==# ''E'''))
\ ? 'ALEErrorSign'
\ : 'ALEWarningSign'
" Save the sign IDs we are setting back on our loclist objects.
" These IDs will be used to preserve items which are set many times.
for l:obj in l:sublist
let l:obj.sign_id = l:sign_id
endfor
execute 'sign place ' . l:sign_id
\ . ' line=' . l:sublist[0].lnum
\ . ' name=' . l:type
\ . ' buffer=' . a:buffer
endfor
endfunction
" Get items grouped by any current sign IDs they might have.
function! s:GetItemsWithSignIDs(loclist) abort
let l:items_by_sign_id = {}
for l:item in a:loclist
if has_key(l:item, 'sign_id')
if !has_key(l:items_by_sign_id, l:item.sign_id)
let l:items_by_sign_id[l:item.sign_id] = []
if l:signlist[-1].type ==# 'W' && l:obj.type ==# 'E'
" If we had a warning previously, but now have an error,
" we replace the object to set an error instead.
let l:signlist[-1] = l:obj
endif
endif
call add(l:items_by_sign_id[l:item.sign_id], l:item)
if l:should_append
call add(l:signlist, l:obj)
endif
endfor
return l:items_by_sign_id
endfunction
" Given some current signs and a loclist, look for items with sign IDs,
" and change the line numbers for loclist items to match the signs.
function! s:UpdateLineNumbers(current_sign_list, loclist) abort
let l:items_by_sign_id = s:GetItemsWithSignIDs(a:loclist)
" Do nothing if there's nothing to work with.
if empty(l:items_by_sign_id)
return
endif
for [l:line, l:sign_id, l:name] in a:current_sign_list
for l:obj in get(l:items_by_sign_id, l:sign_id, [])
let l:obj.lnum = l:line
endfor
endfor
" Sort items again.
call sort(a:loclist, 'ale#util#LocItemCompare')
return l:signlist
endfunction
" This function will set the signs which show up on the left.
function! ale#sign#SetSigns(buffer, loclist) abort
let l:signlist = ale#sign#CombineSigns(a:loclist)
" Find the current markers
let l:current_sign_list = ale#sign#FindCurrentSigns(a:buffer)
let l:current_id_list = ale#sign#FindCurrentSigns(a:buffer)
let l:dummy_sign_set = 0
call s:UpdateLineNumbers(l:current_sign_list, a:loclist)
let l:grouped_items = s:GroupLoclistItems(a:loclist)
" Set the dummy sign if we need to.
" This keeps the sign gutter open while we remove things, etc.
let l:is_dummy_sign_set = s:SetDummySignIfNeeded(
\ a:buffer,
\ l:current_sign_list,
\ !empty(l:grouped_items),
\)
" Now remove the previous signs. The dummy will hold the column open
" while we add the new signs, if we had signs before.
for [l:line, l:sign_id, l:name] in l:current_sign_list
if l:sign_id != g:ale_sign_offset
exec 'sign unplace ' . l:sign_id . ' buffer=' . a:buffer
" Check if we set the dummy sign already.
for l:current_id in l:current_id_list
if l:current_id == g:ale_sign_offset
let l:dummy_sign_set = 1
endif
endfor
call s:PlaceNewSigns(a:buffer, l:grouped_items)
" If we haven't already set a dummy sign, and we have some previous signs
" or always want a dummy sign, then set one, to keep the sign column open.
if !l:dummy_sign_set && (len(l:signlist) > 0 || g:ale_sign_column_always)
execute 'sign place ' . g:ale_sign_offset
\ . ' line=1 name=ALEDummySign buffer='
\ . a:buffer
let l:dummy_sign_set = 1
endif
" Now remove the previous signs. The dummy will hold the column open
" while we add the new signs, if we had signs before.
for l:current_id in l:current_id_list
if l:current_id != g:ale_sign_offset
exec 'sign unplace ' . l:current_id . ' buffer=' . a:buffer
endif
endfor
" Add the new signs,
for l:index in range(0, len(l:signlist) - 1)
let l:sign = l:signlist[l:index]
let l:type = l:sign['type'] ==# 'W' ? 'ALEWarningSign' : 'ALEErrorSign'
let l:sign_line = 'sign place ' . (l:index + g:ale_sign_offset + 1)
\. ' line=' . l:sign['lnum']
\. ' name=' . l:type
\. ' buffer=' . a:buffer
exec l:sign_line
endfor
" Remove the dummy sign now we've updated the signs, unless we want
" to keep it, which will keep the sign column open even when there are
" no warnings or errors.
if l:is_dummy_sign_set && !g:ale_sign_column_always
if l:dummy_sign_set && !g:ale_sign_column_always
execute 'sign unplace ' . g:ale_sign_offset . ' buffer=' . a:buffer
endif
endfunction

Some files were not shown because too many files have changed in this diff Show More