diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..5411395
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,40 @@
+---
+# Disabling building for AppVeyor. We are just testing things.
+build: false
+clone_depth: 10
+# Use the directory C:\testplugin so test directories will mostly work.
+clone_folder: C:\testplugin
+
+# Cache the vim and vader directories between builds.
+cache:
+ - C:\vim -> .appveyor.yml
+ - C:\vader -> .appveyor.yml
+
+init:
+ # Stop git from changing newlines
+ - git config --global core.autocrlf input
+
+install:
+ # Download and unpack Vim
+ - ps: >-
+ if (!(Test-Path -Path C:\vim)){
+ Add-Type -A System.IO.Compression.FileSystem
+ Invoke-WebRequest ftp://ftp.vim.org/pub/vim/pc/vim80-586w32.zip `
+ -OutFile C:\vim.zip
+ [IO.Compression.ZipFile]::ExtractToDirectory('C:\vim.zip', 'C:\vim')
+ Invoke-WebRequest ftp://ftp.vim.org/pub/vim/pc/vim80-586rt.zip `
+ -OutFile C:\rt.zip
+ [IO.Compression.ZipFile]::ExtractToDirectory('C:\rt.zip', 'C:\vim')
+ }
+ # Clone Vader and check out the commit we want
+ - ps: >-
+ if (!(Test-Path -Path C:\vader)){
+ git clone https://github.com/junegunn/vader.vim C:\vader 2> $null
+ cd C:\vader
+ git checkout -qf c6243dd81c98350df4dec608fa972df98fa2a3af 2> $null
+ }
+
+test_script:
+ - cd C:\testplugin
+ - 'C:\vim\vim\vim80\vim.exe -u test\vimrc "+Vader!
+ test/*.vader test/*/*.vader test/*/*/*.vader test/*/*/*.vader"'
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d9b95d6
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# Top-most EditorConfig file
+root = true
+
+# Match and apply these rules for all file
+# types you open in your code editor
+[*]
+# Unix-style newlines
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index adcb251..0000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,11 +0,0 @@
-module.exports = {
- parserOptions: {
- ecmaVersion: 6,
- sourceType: "module",
- },
- rules: {
- semi: 'error',
- 'space-infix-ops': 'warn',
- radix: 'error',
- }
-}
diff --git a/.gitattributes b/.gitattributes
index 060e8ad..05b1f3f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,9 +1,12 @@
.* export-ignore
+/CODE_OF_CONDUCT.md export-ignore
/CONTRIBUTING.md export-ignore
/Dockerfile export-ignore
/ISSUE_TEMPLATE.md export-ignore
/Makefile export-ignore
+/PULL_REQUEST_TEMPLATE.md export-ignore
/README.md export-ignore
-/custom-checks export-ignore
/img export-ignore
+/run-tests export-ignore
+/run-tests.bat export-ignore
/test export-ignore
diff --git a/.gitignore b/.gitignore
index 3be1b02..2a99180 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
/init.vim
/doc/tags
.*
+!.editorconfig
*.obj
+tags
diff --git a/.travis.yml b/.travis.yml
index d4e6cf3..2423732 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,9 +2,6 @@
sudo: required
services:
- docker
-branches:
- only:
- - master
language: python
script: |
- make test
+ ./run-tests
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..587bb37
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,3 @@
+Codes of conduct are totally unnecessary and dumb.
+
+Just don't be a jerk and have fun.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e40ed5d..2612956 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,17 +3,17 @@
1. [Guidelines](#guidelines)
2. [Creating Issues](#issues)
3. [Creating Pull Requests](#pull-requests)
- 1. [Adding a New Linter](#adding-a-new-linter)
- 2. [Adding New Options](#adding-new-options)
+ 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)
+ 1. [Documenting New Linters](#documenting-new-linters)
+ 2. [Editing the Online Documentation](#editing-online-documentation)
+ 3. [Documenting Linter Options](#documenting-linter-options)
5. [In Case of Busses](#in-case-of-busses)
-# 1. Guidelines
+## 1. Guidelines
Have fun, and work on whatever floats your boat. Take It Easy :tm:.
@@ -31,7 +31,7 @@ will check your code while you type.
-# 2. Creating Issues
+## 2. Creating Issues
Before creating any issues, please look through the current list of issues and
pull requests, and ensure that the issue hasn't already been reported. If an
@@ -52,7 +52,7 @@ can understand you.
-# 3. Creating Pull Requests
+## 3. Creating Pull Requests
For code you write, make sure to credit yourself at the top of files you add,
and probably those you modify. You can write some comments at the top of your
@@ -71,13 +71,13 @@ If you want to credit multiple authors, you can comma separate them.
-# 3.i. Adding a New Linter
+### 3.i. Adding a New Linter
If you add a new linter, look for existing handlers first in the
-[handlers.vim](autoload/ale/handlers.vim) file. One of the handlers there may
+[handlers](autoload/ale/handlers) directory. One of the handlers there may
already be able to handle your lines of output. If you find that your new
linter replicates an existing error handler, consider pulling it up into the
-[handlers.vim](autoload/ale/handlers.vim) file, and use the generic handler in
+[handlers](autoload/ale/handlers) directory, and use the generic handler in
both places.
When you add a linter, make sure the language for the linter and the linter
@@ -87,7 +87,7 @@ alphabetically in the table and list.
-# 3.ii. Adding New Options
+### 3.ii. Adding New Options
If you add new options to the plugin, make sure to document those new options
in the [README.md](README.md) file, and also in the [help file](doc/ale.txt).
@@ -104,7 +104,7 @@ easy to see what the default is with `:echo g:ale...`.
-# 4. Writing Documentation
+## 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
@@ -113,7 +113,7 @@ quick overview of the supported tools.
-# 4.i Documenting New Linters
+### 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
@@ -125,11 +125,11 @@ giving some unfair preference to any particular tool or language.
-# 4.ii Editing the Online Documentation
+### 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.
+some guidelines 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.
@@ -145,7 +145,7 @@ some guidlines to follow for this file.
-# 4.iii Documenting Linter Options
+### 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
@@ -155,12 +155,12 @@ to look up the default value easily by typing `:echo g:ale_...`.
-# 5. In Case of Busses
+## 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
+or government abduction, 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
diff --git a/Dockerfile b/Dockerfile
index 8c28d95..eba9a1f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,20 +1,18 @@
FROM tweekmonster/vim-testbed:latest
-RUN install_vim -tag v8.0.0000 -build \
- -tag v8.0.0027 -build
+RUN install_vim -tag v8.0.0027 -build \
+ -tag neovim:v0.1.7 -build
ENV PACKAGES="\
+ bash \
git \
- python=2.7.12-r0 \
- py-pip=8.1.2-r0 \
- nodejs \
+ python \
+ py-pip \
"
RUN apk --update add $PACKAGES && \
rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
RUN pip install vim-vint==0.3.9
-RUN npm install -g eslint@3.7.1
-
RUN git clone https://github.com/junegunn/vader.vim vader && \
cd vader && git checkout c6243dd81c98350df4dec608fa972df98fa2a3af
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 730e6fa..0276a65 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -1,10 +1,28 @@
-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.
+## Information
-Whatever the case, describe the your issue here.
+**VIM version**
+
+PASTE JUST THE FIRST TWO LINES OF `:version` HERE.
+
+Operating System: WHAT OS WERE YOU USING?
+
+### :ALEInfo
+
+PASTE OUTPUT OF `:ALEInfo` HERE. YOU CAN TRY `:ALEInfoToClipboard`.
+
+## What went wrong
+
+WRITE WHAT WENT WRONG HERE.
+
+## Reproducing the bug
+
+Steps for repeating the bug:
+
+1. Write a list of steps.
+2. Otherwise nobody will fix the bug.
diff --git a/LICENSE b/LICENSE
index a8162f2..739ccae 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016, w0rp
+Copyright (c) 2016-2018, w0rp
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a37014f..0000000
--- a/Makefile
+++ /dev/null
@@ -1,53 +0,0 @@
-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'); \
- if [ -z "$$vims" ]; then echo "No Vims found!"; exit 1; fi; \
- EXIT=0; \
- for vim in $$vims; do \
- echo; \
- echo '========================================'; \
- echo "Running tests for $$vim"; \
- echo '========================================'; \
- echo; \
- docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! $(tests)' || EXIT=$$?; \
- done; \
- echo; \
- echo '========================================'; \
- echo 'Running Vint to lint our code'; \
- echo '========================================'; \
- echo 'Vint warnings/errors follow:'; \
- echo; \
- set -o pipefail; \
- docker run -a stdout $(DOCKER_FLAGS) vint -s /testplugin | sed s:^/testplugin/:: || EXIT=$$?; \
- set +o pipefail; \
- echo; \
- echo '========================================'; \
- echo 'Running custom checks'; \
- echo '========================================'; \
- echo 'Custom warnings/errors follow:'; \
- echo; \
- set -o pipefail; \
- docker run -a stdout $(DOCKER_FLAGS) /testplugin/custom-checks /testplugin | sed s:^/testplugin/:: || EXIT=$$?; \
- set +o pipefail; \
- echo; \
- exit $$EXIT;
-
-.DEFAULT_GOAL := test
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..9411653
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,21 @@
+
diff --git a/README.md b/README.md
index baefd42..d5335e2 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# Asynchronous Lint Engine [](https://travis-ci.org/w0rp/ale)
+# Asynchronous Lint Engine [](https://travis-ci.org/w0rp/ale) [](https://ci.appveyor.com/project/w0rp/ale)
+

@@ -15,27 +16,38 @@ back to a filesystem.
In other words, this plugin allows you to lint while you type.
+In addition to linting support, ALE offers some support for fixing code with
+formatting tools, and some Language Server Protocol and `tsserver` features.
+
## Table of Contents
1. [Supported Languages and Tools](#supported-languages)
2. [Usage](#usage)
+ 1. [Linting](#usage-linting)
+ 2. [Fixing](#usage-fixing)
+ 3. [Completion](#usage-completion)
+ 4. [Go To Definition](#usage-go-to-definition)
3. [Installation](#installation)
- 1. [Installation with Pathogen](#installation-with-pathogen)
- 2. [Installation with Vundle](#installation-with-vundle)
- 3. [Manual Installation](#manual-installation)
+ 1. [Installation with Vim package management](#standard-installation)
+ 2. [Installation with Pathogen](#installation-with-pathogen)
+ 3. [Installation with Vundle](#installation-with-vundle)
4. [Contributing](#contributing)
5. [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)
- 4. [How can I show errors or warnings in my statusline?](#faq-statusline)
- 5. [How can I change the format for echo messages?](#faq-echo-format)
- 6. [How can I execute some code when ALE stops linting?](#faq-autocmd)
- 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)
+ 1. [How do I disable particular linters?](#faq-disable-linters)
+ 2. [How can I keep the sign gutter open?](#faq-keep-signs)
+ 3. [How can I change the signs ALE uses?](#faq-change-signs)
+ 4. [How can I change or disable the highlights ALE uses?](#faq-change-highlights)
+ 5. [How can I show errors or warnings in my statusline?](#faq-statusline)
+ 6. [How can I show errors or warnings in my lightline?](#faq-lightline)
+ 7. [How can I change the format for echo messages?](#faq-echo-format)
+ 8. [How can I execute some code when ALE starts or stops linting?](#faq-autocmd)
+ 9. [How can I navigate between errors quickly?](#faq-navigation)
+ 10. [How can I run linters only when I save files?](#faq-lint-on-save)
+ 11. [How can I use the quickfix list instead of the loclist?](#faq-quickfix)
+ 12. [How can I check JSX files with both stylelint and eslint?](#faq-jsx-stylelint-eslint)
+ 13. [Will this plugin eat all of my laptop battery power?](#faq-my-battery-is-sad)
+ 14. [How can I configure my C or C++ project?](#faq-c-configuration)
+ 15. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration)
@@ -48,74 +60,122 @@ tools will be run in combination, so they can be complementary.
Keep the table rows sorted alphabetically by the language name,
and the tools in the tools column sorted alphabetically by the tool
name. That seems to be the fairest way to arrange this table.
+
+Remember to also update doc/ale.txt, which has a similar list with different
+formatting.
-->
+**Notes:**
+
+* *^ No linters for text or Vim help filetypes are enabled by default.*
+* *!! These linters check only files on disk. See `:help ale-lint-file-linters`*
+
| Language | Tools |
| -------- | ----- |
+| ASM | [gcc](https://gcc.gnu.org) |
| 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# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) |
+| API Blueprint | [drafter](https://github.com/apiaryio/drafter) |
+| AsciiDoc | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [write-good](https://github.com/btford/write-good) |
+| Awk | [gawk](https://www.gnu.org/software/gawk/)|
+| Bash | shell [-n flag](https://www.gnu.org/software/bash/manual/bash.html#index-set), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) |
+| Bourne Shell | shell [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/), [shfmt](https://github.com/mvdan/sh) |
+| C | [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint), [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) |
+| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html), [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [flawfinder](https://www.dwheeler.com/flawfinder/), [gcc](https://gcc.gnu.org/) |
+| CUDA | [nvcc](http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) |
+| C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) see:`help ale-cs-mcs` for details, [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) !! see:`help ale-cs-mcsc` for details and configuration|
| Chef | [foodcritic](http://www.foodcritic.io/) |
+| Clojure | [joker](https://github.com/candid82/joker) |
| 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) |
+| Crystal | [crystal](https://crystal-lang.org/) !! |
+| CSS | [csslint](http://csslint.net/), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) |
| Cython (pyrex filetype) | [cython](http://cython.org/) |
| D | [dmd](https://dlang.org/dmd-linux.html) |
-| Dockerfile | [hadolint](https://github.com/lukasmartinelli/hadolint) |
-| Elixir | [credo](https://github.com/rrrene/credo) |
-| Elm | [elm-make](https://github.com/elm-lang/elm-make) |
-| Erlang | [erlc](http://erlang.org/doc/man/erlc.html) |
+| Dafny | [dafny](https://rise4fun.com/Dafny) !! |
+| Dart | [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) !!, [language_server](https://github.com/natebosch/dart_language_server) |
+| Dockerfile | [hadolint](https://github.com/hadolint/hadolint) |
+| Elixir | [credo](https://github.com/rrrene/credo), [dialyxir](https://github.com/jeremyjh/dialyxir), [dogma](https://github.com/lpil/dogma) !!|
+| Elm | [elm-format](https://github.com/avh4/elm-format), [elm-make](https://github.com/elm-lang/elm-make) |
+| Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) |
+| Erlang | [erlc](http://erlang.org/doc/man/erlc.html), [SyntaxErl](https://github.com/ten0s/syntaxerl) |
+| Fish | fish [-n flag](https://linux.die.net/man/1/fish)
| 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/)
-| 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/)|
+| Fountain | [proselint](http://proselint.com/) |
+| FusionScript | [fusion-lint](https://github.com/RyanSquared/fusionscript) |
+| Git Commit Messages | [gitlint](https://github.com/jorisroovers/gitlint) |
+| GLSL | [glslang](https://github.com/KhronosGroup/glslang), [glslls](https://github.com/svenstaro/glsl-language-server) |
+| Go | [gofmt](https://golang.org/cmd/gofmt/), [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports), [go vet](https://golang.org/cmd/vet/) !!, [golint](https://godoc.org/github.com/golang/lint), [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype), [gometalinter](https://github.com/alecthomas/gometalinter) !!, [go build](https://golang.org/cmd/go/) !!, [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple) !!, [staticcheck](https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck) !! |
+| GraphQL | [eslint](http://eslint.org/), [gqlint](https://github.com/happylinks/gqlint), [prettier](https://github.com/prettier/prettier) |
+| Haml | [haml-lint](https://github.com/brigade/haml-lint) |
+| Handlebars | [ember-template-lint](https://github.com/rwjblue/ember-template-lint) |
+| Haskell | [brittany](https://github.com/lspitzner/brittany), [ghc](https://www.haskell.org/ghc/), [stack-ghc](https://haskellstack.org/), [stack-build](https://haskellstack.org/) !!, [ghc-mod](https://github.com/DanielG/ghc-mod), [stack-ghc-mod](https://github.com/DanielG/ghc-mod), [hlint](https://hackage.haskell.org/package/hlint), [hdevtools](https://hackage.haskell.org/package/hdevtools), [hfmt](https://github.com/danstiner/hfmt) |
+| HTML | [alex](https://github.com/wooorm/alex) !!, [HTMLHint](http://htmlhint.com/), [proselint](http://proselint.com/), [tidy](http://www.html-tidy.org/), [write-good](https://github.com/btford/write-good) |
+| Idris | [idris](http://www.idris-lang.org/) |
+| Java | [checkstyle](http://checkstyle.sourceforge.net), [javac](http://www.oracle.com/technetwork/java/javase/downloads/index.html), [google-java-format](https://github.com/google/google-java-format) |
+| JavaScript | [eslint](http://eslint.org/), [flow](https://flowtype.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/), [prettier](https://github.com/prettier/prettier), [prettier-eslint](https://github.com/prettier/prettier-eslint), [prettier-standard](https://github.com/sheerun/prettier-standard), [standard](http://standardjs.com/), [xo](https://github.com/sindresorhus/xo)
+| JSON | [fixjson](https://github.com/rhysd/fixjson), [jsonlint](http://zaa.ch/jsonlint/), [jq](https://stedolan.github.io/jq/), [prettier](https://github.com/prettier/prettier) |
+| Kotlin | [kotlinc](https://kotlinlang.org) !!, [ktlint](https://ktlint.github.io) !! see `:help ale-integration-kotlin` for configuration instructions |
+| LaTeX | [alex](https://github.com/wooorm/alex) !!, [chktex](http://www.nongnu.org/chktex/), [lacheck](https://www.ctan.org/pkg/lacheck), [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
+| Less | [lessc](https://www.npmjs.com/package/less), [prettier](https://github.com/prettier/prettier), [stylelint](https://github.com/stylelint/stylelint) |
+| LLVM | [llc](https://llvm.org/docs/CommandGuide/llc.html) |
+| Lua | [luac](https://www.lua.org/manual/5.1/luac.html), [luacheck](https://github.com/mpeterv/luacheck) |
+| Mail | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale) |
+| Make | [checkmake](https://github.com/mrtazz/checkmake) |
+| Markdown | [alex](https://github.com/wooorm/alex) !!, [mdl](https://github.com/mivok/markdownlint), [prettier](https://github.com/prettier/prettier), [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [remark-lint](https://github.com/wooorm/remark-lint) !!, [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
| MATLAB | [mlint](https://www.mathworks.com/help/matlab/ref/mlint.html) |
-| Nim | [nim](https://nim-lang.org/docs/nimc.html) |
+| Nim | [nim check](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
+| nroff | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)|
+| Objective-C | [clang](http://clang.llvm.org/) |
+| Objective-C++ | [clang](http://clang.llvm.org/) |
+| OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server) |
| 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/), [hackfmt](https://github.com/facebook/flow/tree/master/hack/hackfmt), [langserver](https://github.com/felixfbecker/php-language-server), [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions, [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan), [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer), [php-cs-fixer](http://cs.sensiolabs.org/) |
+| PO | [alex](https://github.com/wooorm/alex) !!, [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html), [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
+| Pod | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
+| Pony | [ponyc](https://github.com/ponylang/ponyc) |
+| proto | [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) |
| 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) |
+| Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](http://github.com/landscapeio/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) |
+| R | [lintr](https://github.com/jimhester/lintr) |
+| ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-reasonml-ols` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [refmt](https://github.com/reasonml/reason-cli) |
+| reStructuredText | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [redpen](http://redpen.cc/), [rstcheck](https://github.com/myint/rstcheck), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good) |
+| Re:VIEW | [redpen](http://redpen.cc/) |
+| RPM spec | [rpmlint](https://github.com/rpm-software-management/rpmlint) (disabled by default; see `:help ale-integration-spec`) |
+| Ruby | [brakeman](http://brakemanscanner.org/) !!, [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) !!, [reek](https://github.com/troessner/reek), [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org) |
+| Rust | cargo !! (see `:help ale-integration-rust` for configuration instructions), [rls](https://github.com/rust-lang-nursery/rls), [rustc](https://www.rust-lang.org/), [rustfmt](https://github.com/rust-lang-nursery/rustfmt) |
| 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)
+| SCSS | [prettier](https://github.com/prettier/prettier), [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), [scalastyle](http://www.scalastyle.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/) |
-| TypeScript | [tslint](https://github.com/palantir/tslint), typecheck |
+| Solidity | [solhint](https://github.com/protofire/solhint), [solium](https://github.com/duaraghav8/Solium) |
+| Stylus | [stylelint](https://github.com/stylelint/stylelint) |
+| SQL | [sqlint](https://github.com/purcell/sqlint) |
+| Swift | [swiftlint](https://github.com/realm/SwiftLint), [swiftformat](https://github.com/nicklockwood/SwiftFormat) |
+| Tcl | [nagelfar](http://nagelfar.sourceforge.net) !! |
+| Terraform | [tflint](https://github.com/wata727/tflint) |
+| Texinfo | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)|
+| Text^ | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale), [write-good](https://github.com/btford/write-good), [redpen](http://redpen.cc/) |
+| Thrift | [thrift](http://thrift.apache.org/) |
+| TypeScript | [eslint](http://eslint.org/), [prettier](https://github.com/prettier/prettier), [tslint](https://github.com/palantir/tslint), tsserver, 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.*
+| Vim help^ | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
+| Vue | [prettier](https://github.com/prettier/prettier) |
+| XHTML | [alex](https://github.com/wooorm/alex) !!, [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) |
+| XML | [xmllint](http://xmlsoft.org/xmllint.html) |
+| YAML | [swaglint](https://github.com/byCedric/swaglint), [yamllint](https://yamllint.readthedocs.io/) |
## 2. Usage
+
+
+### 2.i Linting
+
Once this plugin is installed, while editing your files in supported
languages and tools which have been correctly installed,
this plugin will send the contents of your text buffers to a variety of
@@ -128,19 +188,115 @@ documented in [the Vim help file](doc/ale.txt). For more information on the
options ALE offers, consult `:help ale-options` for global options and `:help
ale-linter-options` for options specified to particular linters.
+
+
+### 2.ii Fixing
+
+ALE can fix files with the `ALEFix` command. Functions need to be configured
+for different filetypes with the `g:ale_fixers` variable. For example, the
+following code can be used to fix JavaScript code with ESLint:
+
+```vim
+" Put this in vimrc or a plugin file of your own.
+" After this is configured, :ALEFix will try and fix your JS code with ESLint.
+let g:ale_fixers = {
+\ 'javascript': ['eslint'],
+\}
+
+" Set this setting in vimrc if you want to fix files automatically on save.
+" This is off by default.
+let g:ale_fix_on_save = 1
+```
+
+The `:ALEFixSuggest` command will suggest some supported tools for fixing code,
+but fixers can be also implemented with functions, including lambda functions
+too. See `:help ale-fix` for detailed information.
+
+
+
+### 2.iii Completion
+
+ALE offers some support for completion via hijacking of omnicompletion while you
+type. All of ALE's completion information must come from Language Server
+Protocol linters, or similar protocols. At the moment, completion is only
+supported for TypeScript code with `tsserver`, when `tsserver` is enabled. You
+can enable completion like so:
+
+```vim
+" Enable completion where available.
+let g:ale_completion_enabled = 1
+```
+
+See `:help ale-completion` for more information.
+
+
+
+### 2.iv Go To Definition
+
+ALE supports jumping to the definition of words under your cursor with the
+`:ALEGoToDefinition` command using any enabled LSP linters and `tsserver`.
+
+See `:help ale-go-to-definition` for more information.
+
## 3. Installation
To install this plugin, you should use one of the following methods.
For Windows users, replace usage of the Unix `~/.vim` directory with
-`%USERPROFILE%\_vim`, or another directory if you have configured
+`%USERPROFILE%\vimfiles`, or another directory if you have configured
Vim differently. On Windows, your `~/.vimrc` file will be similarly
stored in `%USERPROFILE%\_vimrc`.
+
+
+### 3.i. Installation with Vim package management
+
+In Vim 8 and NeoVim, you can install plugins easily without needing to use
+any other tools. Simply clone the plugin into your `pack` directory.
+
+#### Vim 8 on Unix
+
+```bash
+mkdir -p ~/.vim/pack/git-plugins/start
+git clone https://github.com/w0rp/ale.git ~/.vim/pack/git-plugins/start/ale
+```
+
+#### NeoVim on Unix
+
+```bash
+mkdir -p ~/.local/share/nvim/site/pack/git-plugins/start
+git clone https://github.com/w0rp/ale.git ~/.local/share/nvim/site/pack/git-plugins/start/ale
+```
+
+#### Vim 8 on Windows
+
+```bash
+# Run these commands in the "Git for Windows" Bash terminal
+mkdir -p ~/vimfiles/pack/git-plugins/start
+git clone https://github.com/w0rp/ale.git ~/vimfiles/pack/git-plugins/start/ale
+```
+
+#### Generating Vim help files
+
+You can add the following line to your vimrc files to generate documentation
+tags automatically, if you don't have something similar already, so you can use
+the `:help` command to consult ALE's online documentation:
+
+```vim
+" Put these lines at the very end of your vimrc file.
+
+" Load all plugins now.
+" Plugins need to be added to runtimepath before helptags can be generated.
+packloadall
+" Load all of the helptags now, after plugins have been loaded.
+" All messages and errors will be ignored.
+silent! helptags ALL
+```
+
-### 3.i. Installation with Pathogen
+### 3.ii. Installation with Pathogen
To install this module with [Pathogen](https://github.com/tpope/vim-pathogen),
you should clone this repository to your bundle directory, and ensure
@@ -154,7 +310,7 @@ git clone https://github.com/w0rp/ale.git
-### 3.ii. Installation with Vundle
+### 3.iii. Installation with Vundle
You can install this plugin using [Vundle](https://github.com/VundleVim/Vundle.vim)
by using the path on GitHub for this repository.
@@ -165,41 +321,6 @@ Plugin 'w0rp/ale'
See the Vundle documentation for more information.
-
-
-### 3.iii. Manual Installation
-
-For installation without a package manager, you can clone this git repository
-into a bundle directory as with pathogen, and add the repository to your
-runtime path yourself. First clone the repository.
-
-```bash
-cd ~/.vim/bundle
-git clone https://github.com/w0rp/ale.git
-```
-
-Then, modify your `~/.vimrc` file to add this plugin to your runtime path.
-
-```vim
-set nocompatible
-filetype off
-
-let &runtimepath.=',~/.vim/bundle/ale'
-
-filetype plugin on
-```
-
-You can add the following line to generate documentation tags automatically,
-if you don't have something similar already, so you can use the `:help` command
-to consult ALE's online documentation:
-
-```vim
-silent! helptags ALL
-```
-
-Because the author of this plugin is a weird nerd, this is his preferred
-installation method.
-
## 4. Contributing
@@ -214,6 +335,9 @@ 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.
+If you'd liked to discuss the project more directly, check out the `#vim-ale` channel
+on Freenode. Web chat is available [here](https://webchat.freenode.net/?channels=vim-ale).
+
## 5. FAQ
@@ -273,43 +397,91 @@ highlight clear ALEErrorSign
highlight clear ALEWarningSign
```
+
+
+### 5.iv. How can I change or disable the highlights ALE uses?
+
+ALE's highlights problems with highlight groups which link to `SpellBad`,
+`SpellCap`, `error`, and `todo` groups by default. The characters that are
+highlighted depend on the linters being used, and the information provided to
+ALE.
+
+Highlighting can be disabled completely by setting `g:ale_set_highlights` to
+`0`.
+
+```vim
+" Set this in your vimrc file to disabling highlighting
+let g:ale_set_highlights = 0
+```
+
+You can control all of the highlights ALE uses, say if you are using a different
+color scheme which produces ugly highlights. For example:
+
+```vim
+highlight ALEWarning ctermbg=DarkMagenta
+```
+
+See `:help ale-highlights` for more information.
+
-### 5.iv. How can I show errors or warnings in my statusline?
+### 5.v. 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
+[vim-airline](https://github.com/vim-airline/vim-airline) integrates with ALE
+for displaying error information in the status bar. If you want to see the
+status for ALE in a nice format, it is recommended to use vim-airline with ALE.
+The airline extension can be enabled by adding the following to your vimrc:
```vim
-%{ALEGetStatusLine()}
+" Set this. Airline will handle the rest.
+let g:airline#extensions#ale#enabled = 1
```
-When errors are detected a string showing the number of errors will be shown.
-You can customize the output format using the global list `g:ale_statusline_format` where:
+If you don't want to use vim-airline, you can implement your own statusline
+function without adding any other plugins. ALE provides a function for counting
+the number of problems for this purpose, named `ale#statusline#Count`.
-- The 1st element is for errors
-- The 2nd element is for warnings
-- The 3rd element is for when no errors are detected
-
-e.g
+Say you want to display all errors as one figure, and all non-errors as another
+figure. You can do the following:
```vim
-let g:ale_statusline_format = ['⨉ %d', '⚠ %d', '⬥ ok']
+function! LinterStatus() abort
+ let l:counts = ale#statusline#Count(bufnr(''))
+
+ let l:all_errors = l:counts.error + l:counts.style_error
+ let l:all_non_errors = l:counts.total - l:all_errors
+
+ return l:counts.total == 0 ? 'OK' : printf(
+ \ '%dW %dE',
+ \ all_non_errors,
+ \ all_errors
+ \)
+endfunction
+
+set statusline=%{LinterStatus()}
```
-
-
+See `:help ale#statusline#Count()` for more information.
+
+
+
+### 5.vi. How can I show errors or warnings in my lightline?
+
+[lightline](https://github.com/itchyny/lightline.vim) does not have built-in
+support for ALE, nevertheless there is a plugin that adds this functionality: [maximbaz/lightline-ale](https://github.com/maximbaz/lightline-ale).
+
+For more information, check out the sources of that plugin, `:help ale#statusline#Count()` and [lightline documentation](https://github.com/itchyny/lightline.vim#advanced-configuration).
-### 5.v. How can I change the format for echo messages?
+### 5.vii. How can I change the format for echo messages?
There are 3 global options that allow customizing the echoed message.
- `g:ale_echo_msg_format` where:
- * `%s` is the error message itself
- * `%linter%` is the linter name
- * `%severity` is the severity type
+ * `%s` is the error message itself
+ * `%linter%` is the linter name
+ * `%severity` is the severity type
- `g:ale_echo_msg_error_str` is the string used for error severity.
- `g:ale_echo_msg_warning_str` is the string used for warning severity.
@@ -327,22 +499,24 @@ Will give you:
-### 5.vi. How can I execute some code when ALE stops linting?
+### 5.viii. How can I execute some code when ALE starts or 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
-autocmd event can be used to call arbitrary functions after ALE stops linting.
+events whenever has a linter is started and has been successfully executed and
+processed. These events can be used to call arbitrary functions before and after
+ALE stops linting.
```vim
augroup YourGroup
autocmd!
- autocmd User ALELint call YourFunction()
+ autocmd User ALELintPre call YourFunction()
+ autocmd User ALELintPost call YourFunction()
augroup END
```
-### 5.vii. How can I navigate between errors quickly?
+### 5.ix. How can I navigate between errors quickly?
ALE offers some commands with `` keybinds for moving between warnings and
errors quickly. You can map the keys Ctrl+j and Ctrl+k to moving between errors
@@ -358,25 +532,27 @@ For more information, consult the online documentation with
-### 5.viii. How can I run linters only when I save files?
+### 5.x. 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
-as you are editing files, then you can turn the option for linting
-when text is changed off too.
+when files are saved. This option is enabled by default. If you only
+wish to run linters when files are saved, you can turn the other
+options off.
```vim
" Write this in your vimrc file
-let g:ale_lint_on_save = 1
-let g:ale_lint_on_text_changed = 0
+let g:ale_lint_on_text_changed = 'never'
" You can disable this option too
" if you don't want linters to run on opening a file
let g:ale_lint_on_enter = 0
```
+If for whatever reason you don't wish to run linters again when you save
+files, you can set `g:ale_lint_on_save` to `0`.
+
-### 5.ix. How can I use the quickfix list instead of the loclist?
+### 5.xi. 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
@@ -401,15 +577,18 @@ let g:ale_open_list = 1
let g:ale_keep_list_window_open = 1
```
+You can also set `let g:ale_list_vertical = 1` to open the windows vertically
+instead of the default horizontally.
+
-### 5.x. How can I check JSX files with both stylelint and eslint?
+### 5.xii. 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).
+[stylelint-processor-styled-components](https://github.com/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.
@@ -436,7 +615,7 @@ no linter will be run twice for the same file.
-### 5.xi. Will this plugin eat all of my laptop battery power?
+### 5.xiii. 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
@@ -450,12 +629,72 @@ 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
+behaviour. Set `g:ale_lint_on_text_changed` to `never` or `normal`. 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()`.
+`:ALELint`.
+
+
+
+### 5.xiv. How can I configure my C or C++ project?
+
+The structure of C and C++ projects varies wildly from project to project, with
+many different build tools being used for building them, and many different
+formats for project configuration files. ALE can run compilers easily, but
+ALE cannot easily detect which compiler flags to use.
+
+Some tools and build configurations can generate
+[compile_commands.json](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
+files. The `cppcheck`, `clangcheck` and `clangtidy` linters can read these
+files for automatically determining the appropriate compiler flags to use.
+
+For linting with compilers like `gcc` and `clang`, and with other tools, you
+will need to tell ALE which compiler flags to use yourself. You can use
+different options for different projects with the `g:ale_pattern_options`
+setting. Consult the documentation for that setting for more information.
+`b:ale_linters` can be used to select which tools you want to run, say if you
+want to use only `gcc` for one project, and only `clang` for another.
+
+You may also configure buffer-local settings for linters with project-specific
+vimrc files. [local_vimrc](https://github.com/LucHermitte/local_vimrc) can be
+used for executing local vimrc files which can be shared in your project.
+
+
+
+### 5.xv. How can I configure ALE differently for different buffers?
+
+ALE offers various ways to configure which linters or fixers are run, and
+other settings. For the majority of ALE's settings, they can either be
+configured globally with a `g:` variable prefix, or for a specific buffer
+with a `b:` variable prefix. For example, you can configure a Python ftplugin
+file like so.
+
+```vim
+" In ~/.vim/ftplugin/python.vim
+
+" Check Python files with flake8 and pylint.
+let b:ale_linters = ['flake8', 'pylint']
+" Fix Python files with autopep8 and yapf.
+let b:ale_fixers = ['autopep8', 'yapf']
+" Disable warnings about trailing whitespace for Python files.
+let b:ale_warn_about_trailing_whitespace = 0
+```
+
+For configuring files based on regular expression patterns matched against the
+absolute path to a file, you can use `g:ale_pattern_options`.
+
+```vim
+" Do not lint or fix minified files.
+let g:ale_pattern_options = {
+\ '\.min\.js$': {'ale_linters': [], 'ale_fixers': []},
+\ '\.min\.css$': {'ale_linters': [], 'ale_fixers': []},
+\}
+" If you configure g:ale_pattern_options outside of vimrc, you need this.
+let g:ale_pattern_options_enabled = 1
+```
+
+Buffer-local variables for settings always override the global settings.
diff --git a/after/plugin/ale.vim b/after/plugin/ale.vim
index 463b65a..d738dbd 100644
--- a/after/plugin/ale.vim
+++ b/after/plugin/ale.vim
@@ -1,13 +1,22 @@
+" Author: w0rp
+" 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
-if !g:ale_emit_conflict_warnings
+" 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 '
diff --git a/ale_linters/ansible/ansible-lint.vim b/ale_linters/ansible/ansible-lint.vim
deleted file mode 100644
index f3bcf56..0000000
--- a/ale_linters/ansible/ansible-lint.vim
+++ /dev/null
@@ -1,9 +0,0 @@
-" Author: Bjorn Neergaard
-" Description: ansible-lint for ansible-yaml files
-
-call ale#linter#Define('ansible', {
-\ 'name': 'ansible',
-\ 'executable': 'ansible',
-\ 'command': 'ansible-lint -p %t',
-\ 'callback': 'ale#handlers#HandlePEP8Format',
-\})
diff --git a/ale_linters/ansible/ansible_lint.vim b/ale_linters/ansible/ansible_lint.vim
new file mode 100644
index 0000000..0b3b39c
--- /dev/null
+++ b/ale_linters/ansible/ansible_lint.vim
@@ -0,0 +1,49 @@
+" Author: Bjorn Neergaard
+" Description: ansible-lint for ansible-yaml files
+
+function! ale_linters#ansible#ansible_lint#Handle(buffer, lines) abort
+ for l:line in a:lines[:10]
+ if match(l:line, '^Traceback') >= 0
+ return [{
+ \ 'lnum': 1,
+ \ 'text': 'An exception was thrown. See :ALEDetail',
+ \ 'detail': join(a:lines, "\n"),
+ \}]
+ endif
+ endfor
+
+ " Matches patterns line the following:
+ "
+ " test.yml:35: [EANSIBLE0002] Trailing whitespace
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?: \[?([[:alnum:]]+)\]? (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:code = l:match[4]
+
+ if l:code is# 'EANSIBLE0002'
+ \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ " Skip warnings for trailing whitespace if the option is off.
+ continue
+ endif
+
+ if ale#path#IsBufferPath(a:buffer, l:match[1])
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[5],
+ \ 'code': l:code,
+ \ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
+ \})
+ endif
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('ansible', {
+\ 'name': 'ansible',
+\ 'executable': 'ansible',
+\ 'command': 'ansible-lint -p %t',
+\ 'callback': 'ale_linters#ansible#ansible_lint#Handle',
+\})
diff --git a/ale_linters/apiblueprint/drafter.vim b/ale_linters/apiblueprint/drafter.vim
new file mode 100644
index 0000000..9cded35
--- /dev/null
+++ b/ale_linters/apiblueprint/drafter.vim
@@ -0,0 +1,36 @@
+" Author: nametake https://nametake.github.io
+" Description: apiblueprint parser
+
+function! ale_linters#apiblueprint#drafter#HandleErrors(buffer, lines) abort
+ " Matches patterns line the following:
+ "
+ " warning: (3) unable to parse response signature, expected 'response [] [()]'; line 4, column 3k - line 4, column 22
+ " warning: (10) message-body asset is expected to be a pre-formatted code block, separate it by a newline and indent every of its line by 12 spaces or 3 tabs; line 30, column 5 - line 30, column 9; line 31, column 9 - line 31, column 14; line 32, column 9 - line 32, column 14
+ let l:pattern = '\(^.*\): (\d\+) \(.\{-\}\); line \(\d\+\), column \(\d\+\) - line \d\+, column \d\+\(.*; line \d\+, column \d\+ - line \(\d\+\), column \(\d\+\)\)\{-\}$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines[2:], l:pattern)
+ let l:item = {
+ \ 'type': l:match[1] is# 'warning' ? 'W' : 'E',
+ \ 'text': l:match[2],
+ \ 'lnum': l:match[3] + 0,
+ \ 'col': l:match[4] + 0,
+ \}
+ if l:match[5] isnot# ''
+ let l:item.end_lnum = l:match[6] + 0
+ let l:item.end_col = l:match[7] + 0
+ endif
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
+
+call ale#linter#Define('apiblueprint', {
+\ 'name': 'drafter',
+\ 'output_stream': 'stderr',
+\ 'executable': 'drafter',
+\ 'command': 'drafter --use-line-num --validate %t',
+\ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors',
+\})
diff --git a/ale_linters/asciidoc/alex.vim b/ale_linters/asciidoc/alex.vim
new file mode 100644
index 0000000..79b04fc
--- /dev/null
+++ b/ale_linters/asciidoc/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for asciidoc files
+
+call ale#linter#Define('help', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/asciidoc/proselint.vim b/ale_linters/asciidoc/proselint.vim
index 4851191..b636c06 100644
--- a/ale_linters/asciidoc/proselint.vim
+++ b/ale_linters/asciidoc/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('asciidoc', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/asciidoc/redpen.vim b/ale_linters/asciidoc/redpen.vim
new file mode 100644
index 0000000..819e385
--- /dev/null
+++ b/ale_linters/asciidoc/redpen.vim
@@ -0,0 +1,9 @@
+" Author: rhysd https://rhysd.github.io
+" Description: Redpen, a proofreading tool (http://redpen.cc)
+
+call ale#linter#Define('asciidoc', {
+\ 'name': 'redpen',
+\ 'executable': 'redpen',
+\ 'command': 'redpen -f asciidoc -r json %t',
+\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput',
+\})
diff --git a/ale_linters/asciidoc/write-good.vim b/ale_linters/asciidoc/write-good.vim
new file mode 100644
index 0000000..c986cc6
--- /dev/null
+++ b/ale_linters/asciidoc/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for AsciiDoc files
+
+call ale#linter#Define('asciidoc', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/asm/gcc.vim b/ale_linters/asm/gcc.vim
new file mode 100644
index 0000000..4ac876f
--- /dev/null
+++ b/ale_linters/asm/gcc.vim
@@ -0,0 +1,39 @@
+" Author: Lucas Kolstad
+" Description: gcc linter for asm files
+
+call ale#Set('asm_gcc_executable', 'gcc')
+call ale#Set('asm_gcc_options', '-Wall')
+
+function! ale_linters#asm#gcc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'asm_gcc_executable')
+endfunction
+
+function! ale_linters#asm#gcc#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#asm#gcc#GetExecutable(a:buffer))
+ \ . ' -x assembler -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
+ \ . ' ' . ale#Var(a:buffer, 'asm_gcc_options') . ' -'
+endfunction
+
+function! ale_linters#asm#gcc#Handle(buffer, lines) abort
+ let l:pattern = '^.\+:\(\d\+\): \([^:]\+\): \(.\+\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'type': l:match[2] =~? 'error' ? 'E' : 'W',
+ \ 'text': l:match[3],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('asm', {
+\ 'name': 'gcc',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#asm#gcc#GetExecutable',
+\ 'command_callback': 'ale_linters#asm#gcc#GetCommand',
+\ 'callback': 'ale_linters#asm#gcc#Handle',
+\})
diff --git a/ale_linters/awk/gawk.vim b/ale_linters/awk/gawk.vim
new file mode 100644
index 0000000..ac6e915
--- /dev/null
+++ b/ale_linters/awk/gawk.vim
@@ -0,0 +1,26 @@
+" Author: kmarc
+" Description: This file adds support for using GNU awk with sripts.
+
+let g:ale_awk_gawk_executable =
+\ get(g:, 'ale_awk_gawk_executable', 'gawk')
+
+let g:ale_awk_gawk_options =
+\ get(g:, 'ale_awk_gawk_options', '')
+
+function! ale_linters#awk#gawk#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'awk_gawk_executable')
+endfunction
+
+function! ale_linters#awk#gawk#GetCommand(buffer) abort
+ return ale_linters#awk#gawk#GetExecutable(a:buffer)
+ \ . ' ' . ale#Var(a:buffer, 'awk_gawk_options')
+ \ . ' ' . '-f %t --lint /dev/null'
+endfunction
+
+call ale#linter#Define('awk', {
+\ 'name': 'gawk',
+\ 'executable_callback': 'ale_linters#awk#gawk#GetExecutable',
+\ 'command_callback': 'ale_linters#awk#gawk#GetCommand',
+\ 'callback': 'ale#handlers#cpplint#HandleCppLintFormat',
+\ 'output_stream': 'both'
+\})
diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim
index 603e2b7..7680305 100644
--- a/ale_linters/c/clang.vim
+++ b/ale_linters/c/clang.vim
@@ -1,26 +1,29 @@
" Author: Masahiro H https://github.com/mshr-h
" Description: clang linter for c files
-" Set this option to change the Clang options for warnings for C.
-if !exists('g:ale_c_clang_options')
- " let g:ale_c_clang_options = '-Wall'
- " let g:ale_c_clang_options = '-std=c99 -Wall'
- " c11 compatible
- let g:ale_c_clang_options = '-std=c11 -Wall'
-endif
+call ale#Set('c_clang_executable', 'clang')
+call ale#Set('c_clang_options', '-std=c11 -Wall')
+
+function! ale_linters#c#clang#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'c_clang_executable')
+endfunction
function! ale_linters#c#clang#GetCommand(buffer) abort
+ let l:paths = ale#c#FindLocalHeaderPaths(a:buffer)
+
" -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 . ' -'
+ return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer))
+ \ . ' -S -x c -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' '
+ \ . ale#c#IncludeOptions(l:paths)
+ \ . ale#Var(a:buffer, 'c_clang_options') . ' -'
endfunction
call ale#linter#Define('c', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
-\ 'executable': 'clang',
+\ 'executable_callback': 'ale_linters#c#clang#GetExecutable',
\ 'command_callback': 'ale_linters#c#clang#GetCommand',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/c/clangtidy.vim b/ale_linters/c/clangtidy.vim
new file mode 100644
index 0000000..47faa1e
--- /dev/null
+++ b/ale_linters/c/clangtidy.vim
@@ -0,0 +1,64 @@
+" Author: vdeurzen , w0rp ,
+" gagbo , Andrej Radovic
+" Description: clang-tidy linter for c files
+
+call ale#Set('c_clangtidy_executable', 'clang-tidy')
+" Set this option to check the checks clang-tidy will apply.
+" The number of checks that can be applied to C files is limited in contrast to
+" C++
+"
+" Consult the check list in clang-tidy's documentation:
+" http://clang.llvm.org/extra/clang-tidy/checks/list.html
+
+call ale#Set('c_clangtidy_checks', ['*'])
+" Set this option to manually set some options for clang-tidy.
+" This will disable compile_commands.json detection.
+call ale#Set('c_clangtidy_options', '')
+call ale#Set('c_build_dir', '')
+
+function! ale_linters#c#clangtidy#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'c_clangtidy_executable')
+endfunction
+
+function! s:GetBuildDirectory(buffer) abort
+ " Don't include build directory for header files, as compile_commands.json
+ " files don't consider headers to be translation units, and provide no
+ " commands for compiling header files.
+ if expand('#' . a:buffer) =~# '\v\.(h|hpp)$'
+ return ''
+ endif
+
+ let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
+
+ " c_build_dir has the priority if defined
+ if !empty(l:build_dir)
+ return l:build_dir
+ endif
+
+ return ale#c#FindCompileCommands(a:buffer)
+endfunction
+
+function! ale_linters#c#clangtidy#GetCommand(buffer) abort
+ let l:checks = join(ale#Var(a:buffer, 'c_clangtidy_checks'), ',')
+ let l:build_dir = s:GetBuildDirectory(a:buffer)
+
+ " Get the extra options if we couldn't find a build directory.
+ let l:options = empty(l:build_dir)
+ \ ? ale#Var(a:buffer, 'c_clangtidy_options')
+ \ : ''
+
+ return ale#Escape(ale_linters#c#clangtidy#GetExecutable(a:buffer))
+ \ . (!empty(l:checks) ? ' -checks=' . ale#Escape(l:checks) : '')
+ \ . ' %s'
+ \ . (!empty(l:build_dir) ? ' -p ' . ale#Escape(l:build_dir) : '')
+ \ . (!empty(l:options) ? ' -- ' . l:options : '')
+endfunction
+
+call ale#linter#Define('c', {
+\ 'name': 'clangtidy',
+\ 'output_stream': 'stdout',
+\ 'executable_callback': 'ale_linters#c#clangtidy#GetExecutable',
+\ 'command_callback': 'ale_linters#c#clangtidy#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/c/cppcheck.vim b/ale_linters/c/cppcheck.vim
index 754dad7..4db93f7 100644
--- a/ale_linters/c/cppcheck.vim
+++ b/ale_linters/c/cppcheck.vim
@@ -1,15 +1,39 @@
" Author: Bart Libert
" 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')
+call ale#Set('c_cppcheck_executable', 'cppcheck')
+call ale#Set('c_cppcheck_options', '--enable=style')
+
+function! ale_linters#c#cppcheck#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'c_cppcheck_executable')
+endfunction
+
+function! ale_linters#c#cppcheck#GetCommand(buffer) abort
+ " Search upwards from the file for compile_commands.json.
+ "
+ " If we find it, we'll `cd` to where the compile_commands.json file is,
+ " then use the file to set up import paths, etc.
+ let l:compile_commmands_path = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
+
+ let l:cd_command = !empty(l:compile_commmands_path)
+ \ ? ale#path#CdString(fnamemodify(l:compile_commmands_path, ':h'))
+ \ : ''
+ let l:compile_commands_option = !empty(l:compile_commmands_path)
+ \ ? '--project=compile_commands.json '
+ \ : ''
+
+ return l:cd_command
+ \ . ale#Escape(ale_linters#c#cppcheck#GetExecutable(a:buffer))
+ \ . ' -q --language=c '
+ \ . l:compile_commands_option
+ \ . ale#Var(a:buffer, 'c_cppcheck_options')
+ \ . ' %t'
+endfunction
call ale#linter#Define('c', {
\ 'name': 'cppcheck',
\ 'output_stream': 'both',
-\ 'executable': 'cppcheck',
-\ 'command': 'cppcheck -q --language=c '
-\ . g:ale_c_cppcheck_options
-\ . ' %t',
-\ 'callback': 'ale#handlers#HandleCppCheckFormat',
+\ 'executable_callback': 'ale_linters#c#cppcheck#GetExecutable',
+\ 'command_callback': 'ale_linters#c#cppcheck#GetCommand',
+\ 'callback': 'ale#handlers#cppcheck#HandleCppCheckFormat',
\})
diff --git a/ale_linters/c/flawfinder.vim b/ale_linters/c/flawfinder.vim
new file mode 100644
index 0000000..27f269f
--- /dev/null
+++ b/ale_linters/c/flawfinder.vim
@@ -0,0 +1,30 @@
+" Author: Christian Gibbons
+" Description: flawfinder linter for c files
+
+call ale#Set('c_flawfinder_executable', 'flawfinder')
+call ale#Set('c_flawfinder_options', '')
+call ale#Set('c_flawfinder_minlevel', 1)
+
+function! ale_linters#c#flawfinder#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'c_flawfinder_executable')
+endfunction
+
+function! ale_linters#c#flawfinder#GetCommand(buffer) abort
+
+ " Set the minimum vulnerability level for flawfinder to bother with
+ let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'c_flawfinder_minlevel')
+
+ return ale#Escape(ale_linters#c#flawfinder#GetExecutable(a:buffer))
+ \ . ' -CDQS'
+ \ . ale#Var(a:buffer, 'c_flawfinder_options')
+ \ . l:minlevel
+ \ . ' %t'
+endfunction
+
+call ale#linter#Define('c', {
+\ 'name': 'flawfinder',
+\ 'output_stream': 'stdout',
+\ 'executable_callback': 'ale_linters#c#flawfinder#GetExecutable',
+\ 'command_callback': 'ale_linters#c#flawfinder#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\})
diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim
index a487909..4b241e3 100644
--- a/ale_linters/c/gcc.vim
+++ b/ale_linters/c/gcc.vim
@@ -1,26 +1,29 @@
" Author: w0rp
" Description: gcc linter for c files
-" Set this option to change the GCC options for warnings for C.
-if !exists('g:ale_c_gcc_options')
- " let g:ale_c_gcc_options = '-Wall'
- " let g:ale_c_gcc_options = '-std=c99 -Wall'
- " c11 compatible
- let g:ale_c_gcc_options = '-std=c11 -Wall'
-endif
+call ale#Set('c_gcc_executable', 'gcc')
+call ale#Set('c_gcc_options', '-std=c11 -Wall')
+
+function! ale_linters#c#gcc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'c_gcc_executable')
+endfunction
function! ale_linters#c#gcc#GetCommand(buffer) abort
+ let l:paths = ale#c#FindLocalHeaderPaths(a:buffer)
+
" -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 . ' -'
+ return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer))
+ \ . ' -S -x c -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' '
+ \ . ale#c#IncludeOptions(l:paths)
+ \ . ale#Var(a:buffer, 'c_gcc_options') . ' -'
endfunction
call ale#linter#Define('c', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
-\ 'executable': 'gcc',
+\ 'executable_callback': 'ale_linters#c#gcc#GetExecutable',
\ 'command_callback': 'ale_linters#c#gcc#GetCommand',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/chef/foodcritic.vim b/ale_linters/chef/foodcritic.vim
index 44ab568..2c28246 100644
--- a/ale_linters/chef/foodcritic.vim
+++ b/ale_linters/chef/foodcritic.vim
@@ -1,28 +1,37 @@
" Author: Edward Larkey
+" Author: Jose Junior
+" Author: w0rp
" Description: This file adds the foodcritic linter for Chef files.
+call ale#Set('chef_foodcritic_executable', 'foodcritic')
+call ale#Set('chef_foodcritic_options', '')
+
+function! ale_linters#chef#foodcritic#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'chef_foodcritic_executable')
+endfunction
+
+function! ale_linters#chef#foodcritic#GetCommand(buffer) abort
+ let l:executable = ale_linters#chef#foodcritic#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'chef_foodcritic_options')
+
+ return ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . escape(l:options, '~') : '')
+ \ . ' %s'
+endfunction
+
function! ale_linters#chef#foodcritic#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" FC002: Avoid string interpolation where not required: httpd.rb:13
- let l:pattern = '^\(.\+:\s.\+\):\s\(.\+\):\(\d\+\)$'
+ let l:pattern = '\v([^:]+): (.+): ([a-zA-Z]?:?[^:]+):(\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:text = l:match[1]
-
- " vcol is Needed to indicate that the column is a character.
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[3] + 0,
- \ 'col': 0,
- \ 'text': l:text,
+ \ 'code': l:match[1],
+ \ 'text': l:match[2],
+ \ 'filename': l:match[3],
+ \ 'lnum': l:match[4] + 0,
\ 'type': 'W',
\})
endfor
@@ -32,8 +41,8 @@ endfunction
call ale#linter#Define('chef', {
\ 'name': 'foodcritic',
-\ 'executable': 'foodcritic',
-\ 'command': 'foodcritic %t',
+\ 'executable_callback': 'ale_linters#chef#foodcritic#GetExecutable',
+\ 'command_callback': 'ale_linters#chef#foodcritic#GetCommand',
\ 'callback': 'ale_linters#chef#foodcritic#Handle',
+\ 'lint_file': 1,
\})
-
diff --git a/ale_linters/clojure/joker.vim b/ale_linters/clojure/joker.vim
new file mode 100644
index 0000000..e78066f
--- /dev/null
+++ b/ale_linters/clojure/joker.vim
@@ -0,0 +1,32 @@
+" Author: Nic West
+" Description: linter for clojure using joker https://github.com/candid82/joker
+
+function! ale_linters#clojure#joker#HandleJokerFormat(buffer, lines) abort
+ " output format
+ " ::: :
+ let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+):? ((Read error|Parse error|Parse warning|Exception): ?(.+))$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:type = 'E'
+ if l:match[4] is? 'Parse warning'
+ let l:type = 'W'
+ endif
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[3],
+ \ 'type': l:type,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('clojure', {
+\ 'name': 'joker',
+\ 'output_stream': 'stderr',
+\ 'executable': 'joker',
+\ 'command': 'joker --lint %t',
+\ 'callback': 'ale_linters#clojure#joker#HandleJokerFormat',
+\})
diff --git a/ale_linters/cmake/cmakelint.vim b/ale_linters/cmake/cmakelint.vim
index fb876c6..7867651 100644
--- a/ale_linters/cmake/cmakelint.vim
+++ b/ale_linters/cmake/cmakelint.vim
@@ -8,17 +8,17 @@ 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
+ return ale#Var(a:buffer, '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'
+ \ . ' ' . ale#Var(a:buffer, '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',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/coffee/coffee.vim b/ale_linters/coffee/coffee.vim
index ac9ef79..f253928 100644
--- a/ale_linters/coffee/coffee.vim
+++ b/ale_linters/coffee/coffee.vim
@@ -2,7 +2,7 @@
" Description: Coffee for checking coffee files
function! ale_linters#coffee#coffee#GetExecutable(buffer) abort
- return ale#util#ResolveLocalPath(
+ return ale#path#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/coffee',
\ 'coffee'
@@ -19,5 +19,5 @@ call ale#linter#Define('coffee', {
\ 'executable_callback': 'ale_linters#coffee#coffee#GetExecutable',
\ 'command_callback': 'ale_linters#coffee#coffee#GetCommand',
\ 'output_stream': 'stderr',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/coffee/coffeelint.vim b/ale_linters/coffee/coffeelint.vim
index e32f7b7..6d3df35 100644
--- a/ale_linters/coffee/coffeelint.vim
+++ b/ale_linters/coffee/coffeelint.vim
@@ -2,7 +2,7 @@
" Description: coffeelint linter for coffeescript files
function! ale_linters#coffee#coffeelint#GetExecutable(buffer) abort
- return ale#util#ResolveLocalPath(
+ return ale#path#ResolveLocalPath(
\ a:buffer,
\ 'node_modules/.bin/coffeelint',
\ 'coffeelint'
@@ -24,25 +24,11 @@ function! ale_linters#coffee#coffeelint#Handle(buffer, lines) abort
let l:pattern = 'stdin,\(\d\+\),\(\d*\),\(.\{-1,}\),\(.\+\)'
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: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
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type,
+ \ 'lnum': str2nr(l:match[1]),
+ \ 'type': l:match[3] is# 'error' ? 'E' : 'W',
+ \ 'text': l:match[4],
\})
endfor
diff --git a/ale_linters/cpp/clang.vim b/ale_linters/cpp/clang.vim
index 9915ac3..105df82 100644
--- a/ale_linters/cpp/clang.vim
+++ b/ale_linters/cpp/clang.vim
@@ -1,23 +1,29 @@
" Author: Tomota Nakamura
" 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
+call ale#Set('cpp_clang_executable', 'clang++')
+call ale#Set('cpp_clang_options', '-std=c++14 -Wall')
+
+function! ale_linters#cpp#clang#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_clang_executable')
+endfunction
function! ale_linters#cpp#clang#GetCommand(buffer) abort
+ let l:paths = ale#c#FindLocalHeaderPaths(a:buffer)
+
" -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 . ' -'
+ return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer))
+ \ . ' -S -x c++ -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' '
+ \ . ale#c#IncludeOptions(l:paths)
+ \ . ale#Var(a:buffer, 'cpp_clang_options') . ' -'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
-\ 'executable': 'clang++',
+\ 'executable_callback': 'ale_linters#cpp#clang#GetExecutable',
\ 'command_callback': 'ale_linters#cpp#clang#GetCommand',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/cpp/clangcheck.vim b/ale_linters/cpp/clangcheck.vim
new file mode 100644
index 0000000..a109d5d
--- /dev/null
+++ b/ale_linters/cpp/clangcheck.vim
@@ -0,0 +1,39 @@
+" Author: gagbo
+" Description: clang-check linter for cpp files
+
+call ale#Set('cpp_clangcheck_executable', 'clang-check')
+call ale#Set('cpp_clangcheck_options', '')
+call ale#Set('c_build_dir', '')
+
+function! ale_linters#cpp#clangcheck#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_clangcheck_executable')
+endfunction
+
+function! ale_linters#cpp#clangcheck#GetCommand(buffer) abort
+ let l:user_options = ale#Var(a:buffer, 'cpp_clangcheck_options')
+
+ " Try to find compilation database to link automatically
+ let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
+
+ if empty(l:build_dir)
+ let l:build_dir = ale#c#FindCompileCommands(a:buffer)
+ endif
+
+ " The extra arguments in the command are used to prevent .plist files from
+ " being generated. These are only added if no build directory can be
+ " detected.
+ return ale#Escape(ale_linters#cpp#clangcheck#GetExecutable(a:buffer))
+ \ . ' -analyze %s'
+ \ . (empty(l:build_dir) ? ' -extra-arg -Xclang -extra-arg -analyzer-output=text' : '')
+ \ . (!empty(l:user_options) ? ' ' . l:user_options : '')
+ \ . (!empty(l:build_dir) ? ' -p ' . ale#Escape(l:build_dir) : '')
+endfunction
+
+call ale#linter#Define('cpp', {
+\ 'name': 'clangcheck',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#cpp#clangcheck#GetExecutable',
+\ 'command_callback': 'ale_linters#cpp#clangcheck#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/cpp/clangtidy.vim b/ale_linters/cpp/clangtidy.vim
index 11088c4..1d5fb77 100644
--- a/ale_linters/cpp/clangtidy.vim
+++ b/ale_linters/cpp/clangtidy.vim
@@ -1,18 +1,58 @@
-" Author: vdeurzen , w0rp
+" Author: vdeurzen , w0rp ,
+" gagbo
" 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')
+call ale#Set('cpp_clangtidy_executable', 'clang-tidy')
+" Set this option to check the checks clang-tidy will apply.
+call ale#Set('cpp_clangtidy_checks', ['*'])
+" Set this option to manually set some options for clang-tidy.
+" This will disable compile_commands.json detection.
+call ale#Set('cpp_clangtidy_options', '')
+call ale#Set('c_build_dir', '')
+
+function! ale_linters#cpp#clangtidy#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_clangtidy_executable')
+endfunction
+
+function! s:GetBuildDirectory(buffer) abort
+ " Don't include build directory for header files, as compile_commands.json
+ " files don't consider headers to be translation units, and provide no
+ " commands for compiling header files.
+ if expand('#' . a:buffer) =~# '\v\.(h|hpp)$'
+ return ''
+ endif
+
+ let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
+
+ " c_build_dir has the priority if defined
+ if !empty(l:build_dir)
+ return l:build_dir
+ endif
+
+ return ale#c#FindCompileCommands(a:buffer)
+endfunction
function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort
- return 'clang-tidy %t -- ' . g:ale_cpp_clangtidy_options
+ let l:checks = join(ale#Var(a:buffer, 'cpp_clangtidy_checks'), ',')
+ let l:build_dir = s:GetBuildDirectory(a:buffer)
+
+ " Get the extra options if we couldn't find a build directory.
+ let l:options = empty(l:build_dir)
+ \ ? ale#Var(a:buffer, 'cpp_clangtidy_options')
+ \ : ''
+
+ return ale#Escape(ale_linters#cpp#clangtidy#GetExecutable(a:buffer))
+ \ . (!empty(l:checks) ? ' -checks=' . ale#Escape(l:checks) : '')
+ \ . ' %s'
+ \ . (!empty(l:build_dir) ? ' -p ' . ale#Escape(l:build_dir) : '')
+ \ . (!empty(l:options) ? ' -- ' . l:options : '')
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clangtidy',
\ 'output_stream': 'stdout',
-\ 'executable': 'clang-tidy',
+\ 'executable_callback': 'ale_linters#cpp#clangtidy#GetExecutable',
\ 'command_callback': 'ale_linters#cpp#clangtidy#GetCommand',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'lint_file': 1,
\})
diff --git a/ale_linters/cpp/cppcheck.vim b/ale_linters/cpp/cppcheck.vim
index b287715..8b2aa80 100644
--- a/ale_linters/cpp/cppcheck.vim
+++ b/ale_linters/cpp/cppcheck.vim
@@ -1,15 +1,39 @@
" Author: Bart Libert
" 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')
+call ale#Set('cpp_cppcheck_executable', 'cppcheck')
+call ale#Set('cpp_cppcheck_options', '--enable=style')
+
+function! ale_linters#cpp#cppcheck#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_cppcheck_executable')
+endfunction
+
+function! ale_linters#cpp#cppcheck#GetCommand(buffer) abort
+ " Search upwards from the file for compile_commands.json.
+ "
+ " If we find it, we'll `cd` to where the compile_commands.json file is,
+ " then use the file to set up import paths, etc.
+ let l:compile_commmands_path = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
+
+ let l:cd_command = !empty(l:compile_commmands_path)
+ \ ? ale#path#CdString(fnamemodify(l:compile_commmands_path, ':h'))
+ \ : ''
+ let l:compile_commands_option = !empty(l:compile_commmands_path)
+ \ ? '--project=compile_commands.json '
+ \ : ''
+
+ return l:cd_command
+ \ . ale#Escape(ale_linters#cpp#cppcheck#GetExecutable(a:buffer))
+ \ . ' -q --language=c++ '
+ \ . l:compile_commands_option
+ \ . ale#Var(a:buffer, 'cpp_cppcheck_options')
+ \ . ' %t'
+endfunction
call ale#linter#Define('cpp', {
\ 'name': 'cppcheck',
\ 'output_stream': 'both',
-\ 'executable': 'cppcheck',
-\ 'command': 'cppcheck -q --language=c++ '
-\ . g:ale_cpp_cppcheck_options
-\ . ' %t',
-\ 'callback': 'ale#handlers#HandleCppCheckFormat',
+\ 'executable_callback': 'ale_linters#cpp#cppcheck#GetExecutable',
+\ 'command_callback': 'ale_linters#cpp#cppcheck#GetCommand',
+\ 'callback': 'ale#handlers#cppcheck#HandleCppCheckFormat',
\})
diff --git a/ale_linters/cpp/cpplint.vim b/ale_linters/cpp/cpplint.vim
new file mode 100644
index 0000000..346ac81
--- /dev/null
+++ b/ale_linters/cpp/cpplint.vim
@@ -0,0 +1,26 @@
+" Author: Dawid Kurek https://github.com/dawikur
+" Description: cpplint for cpp files
+
+call ale#Set('cpp_cpplint_executable', 'cpplint')
+call ale#Set('cpp_cpplint_options', '')
+
+function! ale_linters#cpp#cpplint#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_cpplint_executable')
+endfunction
+
+function! ale_linters#cpp#cpplint#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'cpp_cpplint_options')
+
+ return ale#Escape(ale_linters#cpp#cpplint#GetExecutable(a:buffer))
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' %s'
+endfunction
+
+call ale#linter#Define('cpp', {
+\ 'name': 'cpplint',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#cpp#cpplint#GetExecutable',
+\ 'command_callback': 'ale_linters#cpp#cpplint#GetCommand',
+\ 'callback': 'ale#handlers#cpplint#HandleCppLintFormat',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/cpp/flawfinder.vim b/ale_linters/cpp/flawfinder.vim
new file mode 100644
index 0000000..a19f596
--- /dev/null
+++ b/ale_linters/cpp/flawfinder.vim
@@ -0,0 +1,30 @@
+" Author: Christian Gibbons
+" Description: flawfinder linter for c++ files
+
+call ale#Set('cpp_flawfinder_executable', 'flawfinder')
+call ale#Set('cpp_flawfinder_options', '')
+call ale#Set('cpp_flawfinder_minlevel', 1)
+
+function! ale_linters#cpp#flawfinder#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_flawfinder_executable')
+endfunction
+
+function! ale_linters#cpp#flawfinder#GetCommand(buffer) abort
+
+ " Set the minimum vulnerability level for flawfinder to bother with
+ let l:minlevel = ' --minlevel=' . ale#Var(a:buffer, 'cpp_flawfinder_minlevel')
+
+ return ale#Escape(ale_linters#cpp#flawfinder#GetExecutable(a:buffer))
+ \ . ' -CDQS'
+ \ . ale#Var(a:buffer, 'cpp_flawfinder_options')
+ \ . l:minlevel
+ \ . ' %t'
+endfunction
+
+call ale#linter#Define('cpp', {
+\ 'name': 'flawfinder',
+\ 'output_stream': 'stdout',
+\ 'executable_callback': 'ale_linters#cpp#flawfinder#GetExecutable',
+\ 'command_callback': 'ale_linters#cpp#flawfinder#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\})
diff --git a/ale_linters/cpp/gcc.vim b/ale_linters/cpp/gcc.vim
index ad1b93b..40dffc9 100644
--- a/ale_linters/cpp/gcc.vim
+++ b/ale_linters/cpp/gcc.vim
@@ -1,32 +1,29 @@
" Author: geam
" Description: gcc linter for cpp files
+"
+call ale#Set('cpp_gcc_executable', 'gcc')
+call ale#Set('cpp_gcc_options', '-std=c++14 -Wall')
-" Set this option to change the GCC options for warnings for C.
-if !exists('g:ale_cpp_gcc_options')
- " added c++14 standard support
- " POSIX thread and standard c++ thread and atomic library Linker
- " let g:ale_cpp_gcc_options = '-std=c++1z' for c++17
- " for previous version and default, you can just use
- " let g:ale_cpp_gcc_options = '-Wall'
- " for more see man pages of gcc
- " $ man g++
- " make sure g++ in your $PATH
- " Add flags according to your requirements
- let g:ale_cpp_gcc_options = '-std=c++14 -Wall'
-endif
+function! ale_linters#cpp#gcc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cpp_gcc_executable')
+endfunction
function! ale_linters#cpp#gcc#GetCommand(buffer) abort
+ let l:paths = ale#c#FindLocalHeaderPaths(a:buffer)
+
" -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 . ' -'
+ return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer))
+ \ . ' -S -x c++ -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' '
+ \ . ale#c#IncludeOptions(l:paths)
+ \ . ale#Var(a:buffer, 'cpp_gcc_options') . ' -'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'g++',
\ 'output_stream': 'stderr',
-\ 'executable': 'g++',
+\ 'executable_callback': 'ale_linters#cpp#gcc#GetExecutable',
\ 'command_callback': 'ale_linters#cpp#gcc#GetCommand',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/crystal/crystal.vim b/ale_linters/crystal/crystal.vim
new file mode 100644
index 0000000..81579d6
--- /dev/null
+++ b/ale_linters/crystal/crystal.vim
@@ -0,0 +1,31 @@
+" Author: Jordan Andree , David Alexander
+" Description: This file adds support for checking Crystal with crystal build
+
+function! ale_linters#crystal#crystal#Handle(buffer, lines) abort
+ let l:output = []
+
+ for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
+ call add(l:output, {
+ \ 'lnum': l:error.line + 0,
+ \ 'col': l:error.column + 0,
+ \ 'text': l:error.message,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#crystal#crystal#GetCommand(buffer) abort
+ return 'crystal build -f json --no-codegen --no-color -o '
+ \ . ale#Escape(g:ale#util#nul_file)
+ \ . ' %s'
+endfunction
+
+call ale#linter#Define('crystal', {
+\ 'name': 'crystal',
+\ 'executable': 'crystal',
+\ 'output_stream': 'both',
+\ 'lint_file': 1,
+\ 'command_callback': 'ale_linters#crystal#crystal#GetCommand',
+\ 'callback': 'ale_linters#crystal#crystal#Handle',
+\})
diff --git a/ale_linters/cs/mcs.vim b/ale_linters/cs/mcs.vim
index f1e1fc8..b5c4054 100644
--- a/ale_linters/cs/mcs.vim
+++ b/ale_linters/cs/mcs.vim
@@ -1,29 +1,23 @@
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'
+ return 'mcs -unsafe --parse ' . ale#Var(a:buffer, 'cs_mcs_options') . ' %t'
endfunction
function! ale_linters#cs#mcs#Handle(buffer, lines) abort
" Look for lines like the following.
"
" Tests.cs(12,29): error CSXXXX: ; expected
- let l:pattern = '^.\+.cs(\(\d\+\),\(\d\+\)): \(.\+\): \(.\+\)'
+ let l:pattern = '^\v(.+\.cs)\((\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
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[1] + 0,
- \ 'col': l:match[2] + 0,
- \ 'text': l:match[3] . ': ' . l:match[4],
- \ 'type': l:match[3] =~# '^error' ? 'E' : 'W',
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
+ \ 'code': l:match[5],
+ \ 'text': l:match[6],
\})
endfor
diff --git a/ale_linters/cs/mcsc.vim b/ale_linters/cs/mcsc.vim
new file mode 100644
index 0000000..8a78d3b
--- /dev/null
+++ b/ale_linters/cs/mcsc.vim
@@ -0,0 +1,81 @@
+call ale#Set('cs_mcsc_options', '')
+call ale#Set('cs_mcsc_source', '')
+call ale#Set('cs_mcsc_assembly_path', [])
+call ale#Set('cs_mcsc_assemblies', [])
+
+function! s:GetWorkingDirectory(buffer) abort
+ let l:working_directory = ale#Var(a:buffer, 'cs_mcsc_source')
+
+ if !empty(l:working_directory)
+ return l:working_directory
+ endif
+
+ return expand('#' . a:buffer . ':p:h')
+endfunction
+
+function! ale_linters#cs#mcsc#GetCommand(buffer) abort
+ " Pass assembly paths via the -lib: parameter.
+ let l:path_list = ale#Var(a:buffer, 'cs_mcsc_assembly_path')
+
+ let l:lib_option = !empty(l:path_list)
+ \ ? '-lib:' . join(map(copy(l:path_list), 'ale#Escape(v:val)'), ',')
+ \ : ''
+
+ " Pass paths to DLL files via the -r: parameter.
+ let l:assembly_list = ale#Var(a:buffer, 'cs_mcsc_assemblies')
+
+ let l:r_option = !empty(l:assembly_list)
+ \ ? '-r:' . join(map(copy(l:assembly_list), 'ale#Escape(v:val)'), ',')
+ \ : ''
+
+ " register temporary module target file with ale
+ let l:out = tempname()
+ call ale#engine#ManageFile(a:buffer, l:out)
+
+ " The code is compiled as a module and the output is redirected to a
+ " temporary file.
+ return ale#path#CdString(s:GetWorkingDirectory(a:buffer))
+ \ . 'mcs -unsafe'
+ \ . ' ' . ale#Var(a:buffer, 'cs_mcsc_options')
+ \ . ' ' . l:lib_option
+ \ . ' ' . l:r_option
+ \ . ' -out:' . l:out
+ \ . ' -t:module'
+ \ . ' -recurse:' . ale#Escape('*.cs')
+endfunction
+
+function! ale_linters#cs#mcsc#Handle(buffer, lines) abort
+ " Look for lines like the following.
+ "
+ " Tests.cs(12,29): error CSXXXX: ; expected
+ "
+ " NOTE: pattern also captures file name as linter compiles all
+ " files within the source tree rooted at the specified source
+ " path and not just the file loaded in the buffer
+ let l:pattern = '^\v(.+\.cs)\((\d+),(\d+)\)\: ([^ ]+) ([^ ]+): (.+)$'
+ let l:output = []
+
+ let l:dir = s:GetWorkingDirectory(a:buffer)
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
+ \ 'code': l:match[5],
+ \ 'text': l:match[6],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('cs',{
+\ 'name': 'mcsc',
+\ 'output_stream': 'stderr',
+\ 'executable': 'mcs',
+\ 'command_callback': 'ale_linters#cs#mcsc#GetCommand',
+\ 'callback': 'ale_linters#cs#mcsc#Handle',
+\ 'lint_file': 1
+\})
diff --git a/ale_linters/css/csslint.vim b/ale_linters/css/csslint.vim
index 39176ce..98b7fdd 100644
--- a/ale_linters/css/csslint.vim
+++ b/ale_linters/css/csslint.vim
@@ -1,9 +1,18 @@
" Author: w0rp
" Description: This file adds support for checking CSS code with csslint.
+function! ale_linters#css#csslint#GetCommand(buffer) abort
+ let l:csslintrc = ale#path#FindNearestFile(a:buffer, '.csslintrc')
+ let l:config_option = !empty(l:csslintrc)
+ \ ? '--config=' . ale#Escape(l:csslintrc)
+ \ : ''
+
+ return 'csslint --format=compact ' . l:config_option . ' %t'
+endfunction
+
call ale#linter#Define('css', {
\ 'name': 'csslint',
\ 'executable': 'csslint',
-\ 'command': 'csslint --format=compact %t',
-\ 'callback': 'ale#handlers#HandleCSSLintFormat',
+\ 'command_callback': 'ale_linters#css#csslint#GetCommand',
+\ 'callback': 'ale#handlers#css#HandleCSSLintFormat',
\})
diff --git a/ale_linters/css/stylelint.vim b/ale_linters/css/stylelint.vim
index ef49630..9f68319 100644
--- a/ale_linters/css/stylelint.vim
+++ b/ale_linters/css/stylelint.vim
@@ -1,29 +1,18 @@
" Author: diartyz
-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)
+call ale#Set('css_stylelint_executable', 'stylelint')
+call ale#Set('css_stylelint_options', '')
+call ale#Set('css_stylelint_use_global', 0)
function! ale_linters#css#stylelint#GetExecutable(buffer) abort
- if g:ale_css_stylelint_use_global
- return g:ale_css_stylelint_executable
- endif
-
- return ale#util#ResolveLocalPath(
- \ a:buffer,
+ return ale#node#FindExecutable(a:buffer, 'css_stylelint', [
\ 'node_modules/.bin/stylelint',
- \ g:ale_css_stylelint_executable
- \)
+ \])
endfunction
function! ale_linters#css#stylelint#GetCommand(buffer) abort
return ale_linters#css#stylelint#GetExecutable(a:buffer)
- \ . ' ' . g:ale_css_stylelint_options
+ \ . ' ' . ale#Var(a:buffer, 'css_stylelint_options')
\ . ' --stdin-filename %s'
endfunction
@@ -31,5 +20,5 @@ call ale#linter#Define('css', {
\ 'name': 'stylelint',
\ 'executable_callback': 'ale_linters#css#stylelint#GetExecutable',
\ 'command_callback': 'ale_linters#css#stylelint#GetCommand',
-\ 'callback': 'ale#handlers#HandleStyleLintFormat',
+\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/cuda/nvcc.vim b/ale_linters/cuda/nvcc.vim
new file mode 100644
index 0000000..7aaa5cc
--- /dev/null
+++ b/ale_linters/cuda/nvcc.vim
@@ -0,0 +1,56 @@
+" Author: blahgeek
+" Description: NVCC linter for cuda files
+
+call ale#Set('cuda_nvcc_executable', 'nvcc')
+call ale#Set('cuda_nvcc_options', '-std=c++11')
+
+function! ale_linters#cuda#nvcc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'cuda_nvcc_executable')
+endfunction
+
+function! ale_linters#cuda#nvcc#GetCommand(buffer) abort
+ " Unused: use ale#util#nul_file
+ " let l:output_file = tempname() . '.ii'
+ " call ale#engine#ManageFile(a:buffer, l:output_file)
+
+ return ale#Escape(ale_linters#cuda#nvcc#GetExecutable(a:buffer))
+ \ . ' -cuda '
+ \ . ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer))
+ \ . ale#Var(a:buffer, 'cuda_nvcc_options') . ' %s'
+ \ . ' -o ' . g:ale#util#nul_file
+endfunction
+
+function! ale_linters#cuda#nvcc#HandleNVCCFormat(buffer, lines) abort
+ " Look for lines like the following.
+ "
+ " test.cu(8): error: argument of type "void *" is incompatible with parameter of type "int *"
+ let l:pattern = '\v^([^:\(\)]+):?\(?(\d+)\)?:(\d+)?:?\s*\w*\s*(error|warning): (.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+
+ let l:item = {
+ \ 'lnum': str2nr(l:match[2]),
+ \ 'type': l:match[4] =~# 'error' ? 'E' : 'W',
+ \ 'text': l:match[5],
+ \ 'filename': fnamemodify(l:match[1], ':p'),
+ \}
+
+ if !empty(l:match[3])
+ let l:item.col = str2nr(l:match[3])
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('cuda', {
+\ 'name': 'nvcc',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#cuda#nvcc#GetExecutable',
+\ 'command_callback': 'ale_linters#cuda#nvcc#GetCommand',
+\ 'callback': 'ale_linters#cuda#nvcc#HandleNVCCFormat',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/d/dmd.vim b/ale_linters/d/dmd.vim
index bf3d3d3..b91238a 100644
--- a/ale_linters/d/dmd.vim
+++ b/ale_linters/d/dmd.vim
@@ -5,7 +5,7 @@ function! s:FindDUBConfig(buffer) abort
" Find a DUB configuration file in ancestor paths.
" The most DUB-specific names will be tried first.
for l:possible_filename in ['dub.sdl', 'dub.json', 'package.json']
- let l:dub_file = ale#util#FindNearestFile(a:buffer, l:possible_filename)
+ let l:dub_file = ale#path#FindNearestFile(a:buffer, l:possible_filename)
if !empty(l:dub_file)
return l:dub_file
@@ -31,7 +31,7 @@ function! ale_linters#d#dmd#DUBCommand(buffer) abort
" To support older dub versions, we just change the directory to
" the directory where we found the dub config, and then run `dub describe`
" from that directory.
- return 'cd ' . fnameescape(fnamemodify(l:dub_file, ':h'))
+ return 'cd ' . ale#Escape(fnamemodify(l:dub_file, ':h'))
\ . ' && dub describe --import-paths'
endfunction
@@ -42,7 +42,7 @@ function! ale_linters#d#dmd#DMDCommand(buffer, dub_output) abort
for l:line in a:dub_output
if !empty(l:line)
" The arguments must be '-Ifilename', not '-I filename'
- call add(l:import_list, '-I' . fnameescape(l:line))
+ call add(l:import_list, '-I' . ale#Escape(l:line))
endif
endfor
@@ -56,25 +56,12 @@ function! ale_linters#d#dmd#Handle(buffer, lines) abort
let l:pattern = '^[^(]\+(\([0-9]\+\)\,\?\([0-9]*\)): \([^:]\+\): \(.\+\)'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- break
- endif
-
- let l:line = l:match[1] + 0
- let l:column = l:match[2] + 0
- let l:type = l:match[3]
- let l:text = l:match[4]
-
- " vcol is Needed to indicate that the column is a character.
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': bufnr('%'),
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type ==# 'Warning' ? 'W' : 'E',
+ \ 'lnum': l:match[1],
+ \ 'col': l:match[2],
+ \ 'type': l:match[3] is# 'Warning' ? 'W' : 'E',
+ \ 'text': l:match[4],
\})
endfor
diff --git a/ale_linters/dafny/dafny.vim b/ale_linters/dafny/dafny.vim
new file mode 100644
index 0000000..8bbf1b1
--- /dev/null
+++ b/ale_linters/dafny/dafny.vim
@@ -0,0 +1,25 @@
+" Author: Taylor Blau
+
+function! ale_linters#dafny#dafny#Handle(buffer, lines) abort
+ let l:pattern = '\v(.*)\((\d+),(\d+)\): (.*): (.*)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'col': l:match[3] + 0,
+ \ 'lnum': l:match[2] + 0,
+ \ 'text': l:match[5],
+ \ 'type': l:match[4] =~# '^Error' ? 'E' : 'W'
+ \ })
+ endfor
+ return l:output
+endfunction
+
+call ale#linter#Define('dafny', {
+\ 'name': 'dafny',
+\ 'executable': 'dafny',
+\ 'command': 'dafny %s /compile:0',
+\ 'callback': 'ale_linters#dafny#dafny#Handle',
+\ 'lint_file': 1,
+\ })
diff --git a/ale_linters/dart/dartanalyzer.vim b/ale_linters/dart/dartanalyzer.vim
new file mode 100644
index 0000000..ef33c9d
--- /dev/null
+++ b/ale_linters/dart/dartanalyzer.vim
@@ -0,0 +1,41 @@
+" Author: w0rp
+" Description: Check Dart files with dartanalyzer
+
+call ale#Set('dart_dartanalyzer_executable', 'dartanalyzer')
+
+function! ale_linters#dart#dartanalyzer#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'dart_dartanalyzer_executable')
+endfunction
+
+function! ale_linters#dart#dartanalyzer#GetCommand(buffer) abort
+ let l:executable = ale_linters#dart#dartanalyzer#GetExecutable(a:buffer)
+ let l:path = ale#path#FindNearestFile(a:buffer, '.packages')
+
+ return ale#Escape(l:executable)
+ \ . (!empty(l:path) ? ' --packages ' . ale#Escape(l:path) : '')
+ \ . ' %s'
+endfunction
+
+function! ale_linters#dart#dartanalyzer#Handle(buffer, lines) abort
+ let l:pattern = '\v^ ([a-z]+) . (.+) at (.+):(\d+):(\d+) . (.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'type': l:match[1] is# 'error' ? 'E' : 'W',
+ \ 'text': l:match[6] . ': ' . l:match[2],
+ \ 'lnum': str2nr(l:match[4]),
+ \ 'col': str2nr(l:match[5]),
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('dart', {
+\ 'name': 'dartanalyzer',
+\ 'executable_callback': 'ale_linters#dart#dartanalyzer#GetExecutable',
+\ 'command_callback': 'ale_linters#dart#dartanalyzer#GetCommand',
+\ 'callback': 'ale_linters#dart#dartanalyzer#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/dart/language_server.vim b/ale_linters/dart/language_server.vim
new file mode 100644
index 0000000..15c7701
--- /dev/null
+++ b/ale_linters/dart/language_server.vim
@@ -0,0 +1,30 @@
+" Author: aurieh
+" Description: A language server for dart
+
+call ale#Set('dart_language_server_executable', 'dart_language_server')
+
+function! ale_linters#dart#language_server#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'dart_language_server_executable')
+endfunction
+
+function! ale_linters#dart#language_server#GetLanguage(buffer) abort
+ return 'dart'
+endfunction
+
+function! ale_linters#dart#language_server#GetProjectRoot(buffer) abort
+ " Note: pub only looks for pubspec.yaml, there's no point in adding
+ " support for pubspec.yml
+ let l:pubspec = ale#path#FindNearestFile(a:buffer, 'pubspec.yaml')
+
+ return !empty(l:pubspec) ? fnamemodify(l:pubspec, ':h:h') : ''
+endfunction
+
+call ale#linter#Define('dart', {
+\ 'name': 'language_server',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale_linters#dart#language_server#GetExecutable',
+\ 'command_callback': 'ale_linters#dart#language_server#GetExecutable',
+\ 'language_callback': 'ale_linters#dart#language_server#GetLanguage',
+\ 'project_root_callback': 'ale_linters#dart#language_server#GetProjectRoot',
+\})
+
diff --git a/ale_linters/dockerfile/hadolint.vim b/ale_linters/dockerfile/hadolint.vim
index f82cb8a..7772afb 100644
--- a/ale_linters/dockerfile/hadolint.vim
+++ b/ale_linters/dockerfile/hadolint.vim
@@ -1,44 +1,97 @@
" Author: hauleth - https://github.com/hauleth
+" always, yes, never
+call ale#Set('dockerfile_hadolint_use_docker', 'never')
+call ale#Set('dockerfile_hadolint_docker_image', 'hadolint/hadolint')
+
function! ale_linters#dockerfile#hadolint#Handle(buffer, lines) abort
- " Matches patterns line the following:
- "
- " stdin:19: F: Pipe chain should start with a raw value.
- let l:pattern = '\v^/dev/stdin:?(\d+)? (\S+) (.+)$'
- let l:output = []
+ " Matches patterns line the following:
+ "
+ " /dev/stdin:19 DL3001 Pipe chain should start with a raw value.
+ " /dev/stdin:19:3 unexpected thing
+ let l:pattern = '\v^/dev/stdin:(\d+):?(\d+)? ((DL|SC)(\d+) )?(.+)$'
+ let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:lnum = 0
+ let l:colnum = 0
- if len(l:match) == 0
- continue
- endif
+ if l:match[1] isnot# ''
+ let l:lnum = l:match[1] + 0
+ endif
- let l:lnum = 0
+ if l:match[2] isnot# ''
+ let l:colnum = l:match[2] + 0
+ endif
- if l:match[1] !=# ''
- let l:lnum = l:match[1] + 0
- endif
+ let l:type = 'W'
+ let l:text = l:match[6]
+ let l:detail = l:match[6]
+ let l:domain = 'https://github.com/hadolint/hadolint/wiki/'
- let l:type = 'W'
- let l:text = l:match[3]
+ if l:match[4] is# 'SC'
+ let l:domain = 'https://github.com/koalaman/shellcheck/wiki/'
+ endif
- " vcol is Needed to indicate that the column is a character.
- call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:lnum,
- \ 'col': 0,
- \ 'type': l:type,
- \ 'text': l:text,
- \ 'nr': l:match[2],
- \})
- endfor
+ if l:match[5] isnot# ''
+ let l:code = l:match[4] . l:match[5]
+ let l:link = ' ( ' . l:domain . l:code . ' )'
+ let l:detail = l:code . l:link . "\n\n" . l:detail
+ else
+ let l:type = 'E'
+ endif
- return l:output
+ call add(l:output, {
+ \ 'lnum': l:lnum,
+ \ 'col': l:colnum,
+ \ 'type': l:type,
+ \ 'text': l:text,
+ \ 'detail': l:detail
+ \})
+ endfor
+
+ return l:output
endfunction
+" This is a little different than the typical 'executable' callback. We want
+" to afford the user the chance to say always use docker, never use docker,
+" and use docker if the hadolint executable is not present on the system.
+"
+" In the case of neither docker nor hadolint executables being present, it
+" really doesn't matter which we return -- either will have the effect of
+" 'nope, can't use this linter!'.
+
+function! ale_linters#dockerfile#hadolint#GetExecutable(buffer) abort
+ let l:use_docker = ale#Var(a:buffer, 'dockerfile_hadolint_use_docker')
+
+ " check for mandatory directives
+ if l:use_docker is# 'never'
+ return 'hadolint'
+ elseif l:use_docker is# 'always'
+ return 'docker'
+ endif
+
+ " if we reach here, we want to use 'hadolint' if present...
+ if executable('hadolint')
+ return 'hadolint'
+ endif
+
+ "... and 'docker' as a fallback.
+ return 'docker'
+endfunction
+
+function! ale_linters#dockerfile#hadolint#GetCommand(buffer) abort
+ let l:command = ale_linters#dockerfile#hadolint#GetExecutable(a:buffer)
+ if l:command is# 'docker'
+ return 'docker run --rm -i ' . ale#Var(a:buffer, 'dockerfile_hadolint_docker_image')
+ endif
+ return 'hadolint -'
+endfunction
+
+
call ale#linter#Define('dockerfile', {
- \ 'name': 'hadolint',
- \ 'executable': 'hadolint',
- \ 'command': 'hadolint -',
- \ 'callback': 'ale_linters#dockerfile#hadolint#Handle' })
+\ 'name': 'hadolint',
+\ 'executable_callback': 'ale_linters#dockerfile#hadolint#GetExecutable',
+\ 'command_callback': 'ale_linters#dockerfile#hadolint#GetCommand',
+\ 'callback': 'ale_linters#dockerfile#hadolint#Handle',
+\})
diff --git a/ale_linters/elixir/credo.vim b/ale_linters/elixir/credo.vim
index 8ce1242..af2ff48 100644
--- a/ale_linters/elixir/credo.vim
+++ b/ale_linters/elixir/credo.vim
@@ -1,43 +1,37 @@
" Author: hauleth - https://github.com/hauleth
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+)?: (.): (.+)$'
- let l:output = []
+ " 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+)?: (.): (.+)$'
+ let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:type = l:match[3]
+ let l:text = l:match[4]
- if len(l:match) == 0
- continue
- endif
+ if l:type is# 'C'
+ let l:type = 'E'
+ elseif l:type is# 'R'
+ let l:type = 'W'
+ endif
- let l:type = l:match[3]
- let l:text = l:match[4]
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'type': l:type,
+ \ 'text': l:text,
+ \})
+ endfor
- if l:type ==# 'C'
- let l:type = 'E'
- elseif l:type ==# 'R'
- 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,
- \ 'col': l:match[2] + 0,
- \ 'type': l:type,
- \ 'text': l:text,
- \})
- endfor
-
- return l:output
+ return l:output
endfunction
call ale#linter#Define('elixir', {
- \ 'name': 'credo',
- \ 'executable': 'mix',
- \ 'command': 'mix credo suggest --format=flycheck --read-from-stdin %s',
- \ 'callback': 'ale_linters#elixir#credo#Handle' })
+\ 'name': 'credo',
+\ 'executable': 'mix',
+\ 'command': 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s',
+\ 'callback': 'ale_linters#elixir#credo#Handle',
+\})
diff --git a/ale_linters/elixir/dialyxir.vim b/ale_linters/elixir/dialyxir.vim
new file mode 100644
index 0000000..5ef3a04
--- /dev/null
+++ b/ale_linters/elixir/dialyxir.vim
@@ -0,0 +1,34 @@
+" Author: Fran C. - https://github.com/franciscoj
+" Description: Add dialyzer support for elixir through dialyxir
+" https://github.com/jeremyjh/dialyxir
+
+function! ale_linters#elixir#dialyxir#Handle(buffer, lines) abort
+ " Matches patterns line the following:
+ "
+ " lib/filename.ex:19: Function fname/1 has no local return
+ let l:pattern = '\v(.+):(\d+): (.+)$'
+ let l:output = []
+ let l:type = 'W'
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ if bufname(a:buffer) == l:match[1]
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': 0,
+ \ 'type': l:type,
+ \ 'text': l:match[3],
+ \})
+ endif
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('elixir', {
+\ 'name': 'dialyxir',
+\ 'executable': 'mix',
+\ 'command': 'mix dialyzer',
+\ 'callback': 'ale_linters#elixir#dialyxir#Handle',
+\})
+
diff --git a/ale_linters/elixir/dogma.vim b/ale_linters/elixir/dogma.vim
new file mode 100644
index 0000000..71cf4f4
--- /dev/null
+++ b/ale_linters/elixir/dogma.vim
@@ -0,0 +1,38 @@
+" Author: archseer - https://github.com/archSeer
+
+function! ale_linters#elixir#dogma#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+)?: (.): (.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:type = l:match[3]
+ let l:text = l:match[4]
+
+ if l:type is# 'C'
+ let l:type = 'E'
+ elseif l:type is# 'R'
+ let l:type = 'W'
+ endif
+
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'type': l:type,
+ \ 'text': l:text,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('elixir', {
+\ 'name': 'dogma',
+\ 'executable': 'mix',
+\ 'command': 'mix help dogma && mix dogma %s --format=flycheck',
+\ 'lint_file': 1,
+\ 'callback': 'ale_linters#elixir#dogma#Handle',
+\})
diff --git a/ale_linters/elm/make.vim b/ale_linters/elm/make.vim
index 3a4febc..3783b5e 100644
--- a/ale_linters/elm/make.vim
+++ b/ale_linters/elm/make.vim
@@ -1,65 +1,89 @@
-" Author: buffalocoder - https://github.com/buffalocoder
+" Author: buffalocoder - https://github.com/buffalocoder, soywod - https://github.com/soywod
" Description: Elm linting in Ale. Closely follows the Syntastic checker in https://github.com/ElmCast/elm-vim.
+call ale#Set('elm_make_executable', 'elm-make')
+call ale#Set('elm_make_use_global', 0)
+
+function! ale_linters#elm#make#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'elm_make', [
+ \ 'node_modules/.bin/elm-make',
+ \])
+endfunction
+
function! ale_linters#elm#make#Handle(buffer, lines) abort
let l:output = []
let l:is_windows = has('win32')
let l:temp_dir = l:is_windows ? $TMP : $TMPDIR
+ let l:unparsed_lines = []
for l:line in a:lines
- if l:line[0] ==# '['
+ if l:line[0] is# '['
let l:errors = json_decode(l:line)
for l:error in l:errors
" Check if file is from the temp directory.
" Filters out any errors not related to the buffer.
if l:is_windows
- let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] ==? l:temp_dir
+ let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] is? l:temp_dir
else
- let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] ==# l:temp_dir
+ let l:file_is_buffer = l:error.file[0:len(l:temp_dir) - 1] is# l:temp_dir
endif
if l:file_is_buffer
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:error.region.start.line,
\ 'col': l:error.region.start.column,
- \ 'type': (l:error.type ==? 'error') ? 'E' : 'W',
+ \ 'end_lnum': l:error.region.end.line,
+ \ 'end_col': l:error.region.end.column,
+ \ 'type': (l:error.type is? 'error') ? 'E' : 'W',
\ 'text': l:error.overview,
\ 'detail': l:error.overview . "\n\n" . l:error.details
\})
endif
endfor
+ elseif l:line isnot# 'Successfully generated /dev/null'
+ call add(l:unparsed_lines, l:line)
endif
endfor
+ if len(l:unparsed_lines) > 0
+ call add(l:output, {
+ \ 'lnum': 1,
+ \ 'type': 'E',
+ \ 'text': l:unparsed_lines[0],
+ \ 'detail': join(l:unparsed_lines, "\n")
+ \})
+ endif
+
return l:output
endfunction
" Return the command to execute the linter in the projects directory.
" If it doesn't, then this will fail when imports are needed.
function! ale_linters#elm#make#GetCommand(buffer) abort
- let l:elm_package = ale#util#FindNearestFile(a:buffer, 'elm-package.json')
+ let l:elm_package = ale#path#FindNearestFile(a:buffer, 'elm-package.json')
+ let l:elm_exe = ale_linters#elm#make#GetExecutable(a:buffer)
if empty(l:elm_package)
let l:dir_set_cmd = ''
else
let l:root_dir = fnamemodify(l:elm_package, ':p:h')
- let l:dir_set_cmd = 'cd ' . fnameescape(l:root_dir) . ' && '
+ let l:dir_set_cmd = 'cd ' . ale#Escape(l:root_dir) . ' && '
endif
" The elm-make compiler, at the time of this writing, uses '/dev/null' as
" a sort of flag to tell the compiler not to generate an output file,
- " which is why this is hard coded here.
+ " which is why this is hard coded here. It does not use NUL on Windows.
" 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:elm_cmd = ale#Escape(l:elm_exe)
+ \ . ' --report=json'
+ \ . ' --output=/dev/null'
return l:dir_set_cmd . ' ' . l:elm_cmd . ' %t'
endfunction
call ale#linter#Define('elm', {
\ 'name': 'make',
-\ 'executable': 'elm-make',
+\ 'executable_callback': 'ale_linters#elm#make#GetExecutable',
\ 'output_stream': 'both',
\ 'command_callback': 'ale_linters#elm#make#GetCommand',
\ 'callback': 'ale_linters#elm#make#Handle'
\})
-
diff --git a/ale_linters/erlang/erlc.vim b/ale_linters/erlang/erlc.vim
index 871d4c8..bddb175 100644
--- a/ale_linters/erlang/erlc.vim
+++ b/ale_linters/erlang/erlc.vim
@@ -5,7 +5,10 @@ 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 'erlc -o ' . ale#Escape(l:output_file)
+ \ . ' ' . ale#Var(a:buffer, 'erlang_erlc_options')
+ \ . ' %t'
endfunction
function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
@@ -14,7 +17,7 @@ function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
" error.erl:4: variable 'B' is unbound
" error.erl:3: Warning: function main/0 is unused
" error.erl:4: Warning: variable 'A' is unused
- let l:pattern = '\v^([^:]+):(\d+): (Warning: )?(.+)$'
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+): (Warning: )?(.+)$'
" parse_transforms are a special case. The error message does not indicate a location:
" error.erl: undefined parse transform 'some_parse_transform'
@@ -24,7 +27,7 @@ function! ale_linters#erlang#erlc#Handle(buffer, lines) abort
let l:pattern_no_module_definition = '\v(no module definition)$'
let l:pattern_unused = '\v(.* is unused)$'
- let l:is_hrl = fnamemodify(bufname(a:buffer), ':e') ==# 'hrl'
+ let l:is_hrl = fnamemodify(bufname(a:buffer), ':e') is# 'hrl'
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
@@ -73,7 +76,6 @@ 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,
diff --git a/ale_linters/erlang/syntaxerl.vim b/ale_linters/erlang/syntaxerl.vim
new file mode 100644
index 0000000..46ecdcb
--- /dev/null
+++ b/ale_linters/erlang/syntaxerl.vim
@@ -0,0 +1,53 @@
+" Author: Dmitri Vereshchagin
+" Description: SyntaxErl linter for Erlang files
+
+call ale#Set('erlang_syntaxerl_executable', 'syntaxerl')
+
+
+function! ale_linters#erlang#syntaxerl#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'erlang_syntaxerl_executable')
+endfunction
+
+
+function! ale_linters#erlang#syntaxerl#FeatureCheck(buffer) abort
+ return s:GetEscapedExecutable(a:buffer) . ' -h'
+endfunction
+
+
+function! ale_linters#erlang#syntaxerl#GetCommand(buffer, output) abort
+ let l:use_b_option = match(a:output, '\C\V-b, --base\>') > -1
+
+ return s:GetEscapedExecutable(a:buffer) . (l:use_b_option ? ' -b %s %t' : ' %t')
+endfunction
+
+
+function! ale_linters#erlang#syntaxerl#Handle(buffer, lines) abort
+ let l:pattern = '\v\C:(\d+):( warning:)? (.+)'
+ let l:loclist = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:loclist, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'text': l:match[3],
+ \ 'type': empty(l:match[2]) ? 'E' : 'W',
+ \})
+ endfor
+
+ return l:loclist
+endfunction
+
+
+function! s:GetEscapedExecutable(buffer) abort
+ return ale#Escape(ale_linters#erlang#syntaxerl#GetExecutable(a:buffer))
+endfunction
+
+
+call ale#linter#Define('erlang', {
+\ 'name': 'syntaxerl',
+\ 'executable_callback': 'ale_linters#erlang#syntaxerl#GetExecutable',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#erlang#syntaxerl#FeatureCheck'},
+\ {'callback': 'ale_linters#erlang#syntaxerl#GetCommand'},
+\ ],
+\ 'callback': 'ale_linters#erlang#syntaxerl#Handle',
+\})
diff --git a/ale_linters/eruby/erb.vim b/ale_linters/eruby/erb.vim
new file mode 100644
index 0000000..61d9703
--- /dev/null
+++ b/ale_linters/eruby/erb.vim
@@ -0,0 +1,25 @@
+" Author: Matthias Guenther - https://wikimatze.de, Eddie Lebow https://github.com/elebow
+" Description: ERB from the Ruby standard library, for eruby/erb files
+
+function! ale_linters#eruby#erb#GetCommand(buffer) abort
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ if empty(l:rails_root)
+ return 'erb -P -T - -x %t | ruby -c'
+ endif
+
+ " Rails-flavored eRuby does not comply with the standard as understood by
+ " ERB, so we'll have to do some substitution. This does not reduce the
+ " effectiveness of the linter—the translated code is still evaluated.
+ return 'ruby -r erb -e ' . ale#Escape('puts ERB.new($stdin.read.gsub(%{<%=},%{<%}), nil, %{-}).src') . '< %t | ruby -c'
+endfunction
+
+call ale#linter#Define('eruby', {
+\ 'name': 'erb',
+\ 'aliases': ['erubylint'],
+\ 'executable': 'erb',
+\ 'output_stream': 'stderr',
+\ 'command_callback': 'ale_linters#eruby#erb#GetCommand',
+\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
+\})
+
diff --git a/ale_linters/eruby/erubi.vim b/ale_linters/eruby/erubi.vim
new file mode 100644
index 0000000..6f2d3ac
--- /dev/null
+++ b/ale_linters/eruby/erubi.vim
@@ -0,0 +1,35 @@
+" Author: Eddie Lebow https://github.com/elebow
+" Description: eruby checker using `erubi`
+
+function! ale_linters#eruby#erubi#CheckErubi(buffer) abort
+ return 'ruby -r erubi/capture_end -e ' . ale#Escape('""')
+endfunction
+
+function! ale_linters#eruby#erubi#GetCommand(buffer, check_erubi_output) abort
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ if (!empty(a:check_erubi_output))
+ " The empty command in CheckErubi returns nothing if erubi runs and
+ " emits an error if erubi is not present
+ return ''
+ endif
+
+ if empty(l:rails_root)
+ return 'ruby -r erubi/capture_end -e ' . ale#Escape('puts Erubi::CaptureEndEngine.new($stdin.read).src') . '< %t | ruby -c'
+ endif
+
+ " Rails-flavored eRuby does not comply with the standard as understood by
+ " Erubi, so we'll have to do some substitution. This does not reduce the
+ " effectiveness of the linter---the translated code is still evaluated.
+ return 'ruby -r erubi/capture_end -e ' . ale#Escape('puts Erubi::CaptureEndEngine.new($stdin.read.gsub(%{<%=},%{<%}), nil, %{-}).src') . '< %t | ruby -c'
+endfunction
+
+call ale#linter#Define('eruby', {
+\ 'name': 'erubi',
+\ 'executable': 'ruby',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#eruby#erubi#CheckErubi'},
+\ {'callback': 'ale_linters#eruby#erubi#GetCommand', 'output_stream': 'stderr'},
+\ ],
+\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
+\})
diff --git a/ale_linters/eruby/erubis.vim b/ale_linters/eruby/erubis.vim
new file mode 100644
index 0000000..1ebd4a0
--- /dev/null
+++ b/ale_linters/eruby/erubis.vim
@@ -0,0 +1,23 @@
+" Author: Jake Zimmerman , Eddie Lebow https://github.com/elebow
+" Description: eruby checker using `erubis`, instead of `erb`
+
+function! ale_linters#eruby#erubis#GetCommand(buffer) abort
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ if empty(l:rails_root)
+ return 'erubis -x %t | ruby -c'
+ endif
+
+ " Rails-flavored eRuby does not comply with the standard as understood by
+ " Erubis, so we'll have to do some substitution. This does not reduce the
+ " effectiveness of the linter - the translated code is still evaluated.
+ return 'ruby -r erubis -e ' . ale#Escape('puts Erubis::Eruby.new($stdin.read.gsub(%{<%=},%{<%})).src') . '< %t | ruby -c'
+endfunction
+
+call ale#linter#Define('eruby', {
+\ 'name': 'erubis',
+\ 'executable': 'erubis',
+\ 'output_stream': 'stderr',
+\ 'command_callback': 'ale_linters#eruby#erubis#GetCommand',
+\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
+\})
diff --git a/ale_linters/fish/fish.vim b/ale_linters/fish/fish.vim
new file mode 100644
index 0000000..19158cb
--- /dev/null
+++ b/ale_linters/fish/fish.vim
@@ -0,0 +1,36 @@
+" Author: Niraj Thapaliya - https://github.com/nthapaliya
+" Description: Lints fish files using fish -n
+
+function! ale_linters#fish#fish#Handle(buffer, lines) abort
+ " Matches patterns such as:
+ "
+ " home/.config/fish/functions/foo.fish (line 1): Missing end to balance this function definition
+ " function foo
+ " ^
+ " fish: Error while reading file .config/fish/functions/foo.fish
+ let l:pattern = '^.* (line \(\d\+\)): \(.*\)$'
+ let l:output = []
+
+ let l:i = 0
+ while l:i < len(a:lines)
+ let l:match = matchlist(a:lines[l:i], l:pattern)
+ if len(l:match) && len(l:match[2])
+ call add(l:output, {
+ \ 'col': len(a:lines[l:i + 2]),
+ \ 'lnum': str2nr(l:match[1]),
+ \ 'text': l:match[2],
+ \})
+ endif
+ let l:i += 1
+ endwhile
+
+ return l:output
+endfunction
+
+call ale#linter#Define('fish', {
+\ 'name': 'fish',
+\ 'output_stream': 'stderr',
+\ 'executable': 'fish',
+\ 'command': 'fish -n %t',
+\ 'callback': 'ale_linters#fish#fish#Handle',
+\})
diff --git a/ale_linters/fortran/gcc.vim b/ale_linters/fortran/gcc.vim
index 6d3f495..5f2ac01 100644
--- a/ale_linters/fortran/gcc.vim
+++ b/ale_linters/fortran/gcc.vim
@@ -1,6 +1,15 @@
" Author: w0rp
" Description: gcc for Fortran files
+" This option can be set to 0 to use -ffixed-form
+if !exists('g:ale_fortran_gcc_use_free_form')
+ let g:ale_fortran_gcc_use_free_form = 1
+endif
+
+if !exists('g:ale_fortran_gcc_executable')
+ let g:ale_fortran_gcc_executable = 'gcc'
+endif
+
" Set this option to change the GCC options for warnings for Fortran.
if !exists('g:ale_fortran_gcc_options')
let g:ale_fortran_gcc_options = '-Wall'
@@ -35,7 +44,7 @@ function! ale_linters#fortran#gcc#Handle(buffer, lines) abort
" Now we have the text, we can set it and add the error.
let l:last_loclist_obj.text = l:match[2]
- let l:last_loclist_obj.type = l:match[1] ==# 'Warning' ? 'W' : 'E'
+ let l:last_loclist_obj.type = l:match[1] is# 'Warning' ? 'W' : 'E'
call add(l:output, l:last_loclist_obj)
else
let l:last_loclist_obj = {
@@ -52,12 +61,26 @@ function! ale_linters#fortran#gcc#Handle(buffer, lines) abort
return l:output
endfunction
+function! ale_linters#fortran#gcc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'fortran_gcc_executable')
+endfunction
+
+function! ale_linters#fortran#gcc#GetCommand(buffer) abort
+ let l:layout_option = ale#Var(a:buffer, 'fortran_gcc_use_free_form')
+ \ ? '-ffree-form'
+ \ : '-ffixed-form'
+
+ return ale_linters#fortran#gcc#GetExecutable(a:buffer)
+ \ . ' -S -x f95 -fsyntax-only '
+ \ . l:layout_option . ' '
+ \ . ale#Var(a:buffer, 'fortran_gcc_options') . ' '
+ \ . '-'
+endfunction
+
call ale#linter#Define('fortran', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
-\ 'executable': 'gcc',
-\ 'command': 'gcc -S -x f95 -fsyntax-only -ffree-form '
-\ . g:ale_fortran_gcc_options
-\ . ' -',
+\ 'executable_callback': 'ale_linters#fortran#gcc#GetExecutable',
+\ 'command_callback': 'ale_linters#fortran#gcc#GetCommand',
\ 'callback': 'ale_linters#fortran#gcc#Handle',
\})
diff --git a/ale_linters/fountain/proselint.vim b/ale_linters/fountain/proselint.vim
new file mode 100644
index 0000000..353a2e5
--- /dev/null
+++ b/ale_linters/fountain/proselint.vim
@@ -0,0 +1,9 @@
+" Author: Jansen Mitchell https://github.com/JansenMitchell
+" Description: proselint for Fountain files
+
+call ale#linter#Define('fountain', {
+\ 'name': 'proselint',
+\ 'executable': 'proselint',
+\ 'command': 'proselint %t',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\})
diff --git a/ale_linters/fuse/fusionlint.vim b/ale_linters/fuse/fusionlint.vim
new file mode 100644
index 0000000..968e801
--- /dev/null
+++ b/ale_linters/fuse/fusionlint.vim
@@ -0,0 +1,41 @@
+" Author: RyanSquared
+" Description: `fusion-lint` linter for FusionScript files
+
+let g:ale_fuse_fusionlint_executable =
+\ get(g:, 'ale_fuse_fusionlint_executable', 'fusion-lint')
+
+let g:ale_fuse_fusionlint_options =
+\ get(g:, 'ale_fuse_fusionlint_options', '')
+
+function! ale_linters#fuse#fusionlint#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'fuse_fusionlint_executable')
+endfunction
+
+function! ale_linters#fuse#fusionlint#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#fuse#fusionlint#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'fuse_fusionlint_options')
+ \ . ' --filename %s -i'
+endfunction
+
+function! ale_linters#fuse#fusionlint#Handle(buffer, lines) abort
+ let l:pattern = '^.*:\(\d\+\):\(\d\+\): (\([WE]\)\d\+) \(.\+\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[4],
+ \ 'type': l:match[3],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('fuse', {
+\ 'name': 'fusionlint',
+\ 'executable_callback': 'ale_linters#fuse#fusionlint#GetExecutable',
+\ 'command_callback': 'ale_linters#fuse#fusionlint#GetCommand',
+\ 'callback': 'ale_linters#fuse#fusionlint#Handle',
+\})
diff --git a/ale_linters/gitcommit/gitlint.vim b/ale_linters/gitcommit/gitlint.vim
new file mode 100644
index 0000000..49aeda7
--- /dev/null
+++ b/ale_linters/gitcommit/gitlint.vim
@@ -0,0 +1,52 @@
+" Author: Nick Yamane
+" Description: gitlint for git commit message files
+
+let g:ale_gitcommit_gitlint_executable =
+\ get(g:, 'ale_gitcommit_gitlint_executable', 'gitlint')
+let g:ale_gitcommit_gitlint_options = get(g:, 'ale_gitcommit_gitlint_options', '')
+let g:ale_gitcommit_gitlint_use_global = get(g:, 'ale_gitcommit_gitlint_use_global', 0)
+
+
+function! ale_linters#gitcommit#gitlint#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'gitcommit_gitlint', ['gitlint'])
+endfunction
+
+function! ale_linters#gitcommit#gitlint#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'gitcommit_gitlint_options')
+ let l:executable = ale_linters#gitcommit#gitlint#GetExecutable(a:buffer)
+ return ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' lint'
+endfunction
+
+
+function! ale_linters#gitcommit#gitlint#Handle(buffer, lines) abort
+ " Matches patterns line the following:
+ let l:pattern = '\v^(\d+): (\w+) (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:code = l:match[2]
+
+ let l:item = {
+ \ 'lnum': l:match[1] + 0,
+ \ 'text': l:match[3],
+ \ 'code': l:code,
+ \ 'type': 'E',
+ \}
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
+
+call ale#linter#Define('gitcommit', {
+\ 'name': 'gitlint',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#gitcommit#gitlint#GetExecutable',
+\ 'command_callback': 'ale_linters#gitcommit#gitlint#GetCommand',
+\ 'callback': 'ale_linters#gitcommit#gitlint#Handle',
+\})
+
diff --git a/ale_linters/glsl/glslang.vim b/ale_linters/glsl/glslang.vim
new file mode 100644
index 0000000..21a03ee
--- /dev/null
+++ b/ale_linters/glsl/glslang.vim
@@ -0,0 +1,46 @@
+" Author: Sven-Hendrik Haase
+" Description: glslang-based linter for glsl files
+"
+" TODO: Once https://github.com/KhronosGroup/glslang/pull/1047 is accepted,
+" we can use stdin.
+
+let g:ale_glsl_glslang_executable =
+\ get(g:, 'ale_glsl_glslang_executable', 'glslangValidator')
+
+let g:ale_glsl_glslang_options = get(g:, 'ale_glsl_glslang_options', '')
+
+function! ale_linters#glsl#glslang#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'glsl_glslang_executable')
+endfunction
+
+function! ale_linters#glsl#glslang#GetCommand(buffer) abort
+ return ale_linters#glsl#glslang#GetExecutable(a:buffer)
+ \ . ' ' . ale#Var(a:buffer, 'glsl_glslang_options')
+ \ . ' ' . '-C %t'
+endfunction
+
+function! ale_linters#glsl#glslang#Handle(buffer, lines) abort
+ " Matches patterns like the following:
+ "
+ " ERROR: 0:5: 'foo' : undeclared identifier
+ let l:pattern = '^\(.\+\): \(\d\+\):\(\d\+\): \(.\+\)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': str2nr(l:match[3]),
+ \ 'col': str2nr(l:match[2]),
+ \ 'text': l:match[4],
+ \ 'type': l:match[1] is# 'ERROR' ? 'E' : 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('glsl', {
+\ 'name': 'glslang',
+\ 'executable_callback': 'ale_linters#glsl#glslang#GetExecutable',
+\ 'command_callback': 'ale_linters#glsl#glslang#GetCommand',
+\ 'callback': 'ale_linters#glsl#glslang#Handle',
+\})
diff --git a/ale_linters/glsl/glslls.vim b/ale_linters/glsl/glslls.vim
new file mode 100644
index 0000000..67ea379
--- /dev/null
+++ b/ale_linters/glsl/glslls.vim
@@ -0,0 +1,38 @@
+" Author: Sven-Hendrik Haase
+" Description: A language server for glsl
+
+call ale#Set('glsl_glslls_executable', 'glslls')
+call ale#Set('glsl_glslls_logfile', '')
+
+function! ale_linters#glsl#glslls#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'glsl_glslls_executable')
+endfunction
+
+function! ale_linters#glsl#glslls#GetCommand(buffer) abort
+ let l:executable = ale_linters#glsl#glslls#GetExecutable(a:buffer)
+ let l:logfile = ale#Var(a:buffer, 'glsl_glslls_logfile')
+ let l:logfile_args = ''
+ if l:logfile isnot# ''
+ let l:logfile_args = ' --verbose -l ' . l:logfile
+ endif
+ return ale#Escape(l:executable) . l:logfile_args . ' --stdin'
+endfunction
+
+function! ale_linters#glsl#glslls#GetLanguage(buffer) abort
+ return 'glsl'
+endfunction
+
+function! ale_linters#glsl#glslls#GetProjectRoot(buffer) abort
+ let l:project_root = ale#c#FindProjectRoot(a:buffer)
+
+ return !empty(l:project_root) ? fnamemodify(l:project_root, ':h:h') : ''
+endfunction
+
+call ale#linter#Define('glsl', {
+\ 'name': 'glslls',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale_linters#glsl#glslls#GetExecutable',
+\ 'command_callback': 'ale_linters#glsl#glslls#GetCommand',
+\ 'language_callback': 'ale_linters#glsl#glslls#GetLanguage',
+\ 'project_root_callback': 'ale_linters#glsl#glslls#GetProjectRoot',
+\})
diff --git a/ale_linters/go/gobuild.vim b/ale_linters/go/gobuild.vim
index 0bac9f2..068877a 100644
--- a/ale_linters/go/gobuild.vim
+++ b/ale_linters/go/gobuild.vim
@@ -1,208 +1,72 @@
-" Author: Joshua Rubin
+" Author: Joshua Rubin , Ben Reedy ,
+" Jeff Willette
" Description: go build for Go files
-
" inspired by work from dzhou121
+call ale#Set('go_gobuild_options', '')
+
+function! ale_linters#go#gobuild#ResetEnv() abort
+ unlet! s:go_env
+endfunction
+
function! ale_linters#go#gobuild#GoEnv(buffer) abort
- if exists('s:go_env')
- return ''
- endif
-
- return 'go env GOPATH GOROOT'
-endfunction
-
-let s:SplitChar = has('unix') ? ':' : ':'
-
-" get a list of all source directories from $GOPATH and $GOROOT
-function! s:SrcDirs() abort
- let l:paths = split(s:go_env.GOPATH, s:SplitChar)
- call add(l:paths, s:go_env.GOROOT)
-
- return l:paths
-endfunction
-
-" figure out from a directory like `/home/user/go/src/some/package` that the
-" import for that path is simply `some/package`
-function! s:PackageImportPath(buffer) abort
- let l:bufname = resolve(bufname(a:buffer))
- let l:pkgdir = fnamemodify(l:bufname, ':p:h')
-
- for l:path in s:SrcDirs()
- let l:path = l:path . '/src/'
-
- if stridx(l:pkgdir, l:path) == 0
- return l:pkgdir[strlen(l:path):]
- endif
- endfor
-
- return ''
-endfunction
-
-" get the package info data structure using `go list`
-function! ale_linters#go#gobuild#GoList(buffer, goenv_output) abort
- if !empty(a:goenv_output)
- let s:go_env = {
- \ 'GOPATH': a:goenv_output[0],
- \ 'GOROOT': a:goenv_output[1],
- \}
- endif
-
- return 'go list -json ' . shellescape(s:PackageImportPath(a:buffer))
-endfunction
-
-let s:filekeys = [
-\ 'GoFiles',
-\ 'CgoFiles',
-\ 'CFiles',
-\ 'CXXFiles',
-\ 'MFiles',
-\ 'HFiles',
-\ 'FFiles',
-\ 'SFiles',
-\ 'SwigFiles',
-\ 'SwigCXXFiles',
-\ 'SysoFiles',
-\ 'TestGoFiles',
-\ 'XTestGoFiles',
-\]
-
-" get the go and test go files from the package
-" will return empty list if the package has any cgo or other invalid files
-function! s:PkgFiles(pkginfo) abort
- let l:files = []
-
- for l:key in s:filekeys
- if has_key(a:pkginfo, l:key)
- call extend(l:files, a:pkginfo[l:key])
- endif
- endfor
-
- " resolve the path of the file relative to the window directory
- return map(l:files, 'shellescape(fnamemodify(resolve(a:pkginfo.Dir . ''/'' . v:val), '':p''))')
-endfunction
-
-function! ale_linters#go#gobuild#CopyFiles(buffer, golist_output) abort
- let l:tempdir = tempname()
- let l:temppkgdir = l:tempdir . '/src/' . s:PackageImportPath(a:buffer)
- call mkdir(l:temppkgdir, 'p', 0700)
-
- if empty(a:golist_output)
- return 'echo ' . shellescape(l:tempdir)
- endif
-
- " parse the output
- let l:pkginfo = json_decode(join(a:golist_output, "\n"))
-
- " get all files for the package
- let l:files = s:PkgFiles(l:pkginfo)
-
- " copy the files to a temp directory with $GOPATH structure
- return 'cp ' . join(l:files, ' ') . ' ' . shellescape(l:temppkgdir) . ' && echo ' . shellescape(l:tempdir)
-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)
-
- " write the a:buffer and any modified buffers from the package to the tempdir
- for l:bufnum in range(1, bufnr('$'))
- " ignore unloaded buffers (can't be a:buffer or a modified buffer)
- if !bufloaded(l:bufnum)
- continue
+ if exists('s:go_env')
+ return ''
endif
- " ignore non-Go buffers
- if getbufvar(l:bufnum, '&ft') !=# 'go'
- continue
- endif
-
- " only consider buffers other than a:buffer if they have the same import
- " path as a:buffer and are modified
- if l:bufnum != a:buffer
- if s:PackageImportPath(l:bufnum) !=# l:importpath
- continue
- endif
-
- if !getbufvar(l:bufnum, '&mod')
- continue
- endif
- endif
-
- call writefile(getbufline(l:bufnum, 1, '$'), l:tempdir . '/src/' . s:PkgFile(l:bufnum))
- endfor
-
- let l:gopaths = [ l:tempdir ]
- call extend(l:gopaths, split(s:go_env.GOPATH, s:SplitChar))
-
- return 'GOPATH=' . shellescape(join(l:gopaths, s:SplitChar)) . ' go test -c -o /dev/null ' . shellescape(l:importpath)
+ return 'go env GOPATH GOROOT'
endfunction
-function! s:PkgFile(buffer) abort
- let l:bufname = resolve(bufname(a:buffer))
- let l:importpath = s:PackageImportPath(a:buffer)
- let l:fname = fnamemodify(l:bufname, ':t')
+function! ale_linters#go#gobuild#GetCommand(buffer, goenv_output) abort
+ let l:options = ale#Var(a:buffer, 'go_gobuild_options')
- return l:importpath . '/' . l:fname
-endfunction
-
-function! s:FindBuffer(file) abort
- for l:buffer in range(1, bufnr('$'))
- if !buflisted(l:buffer)
- continue
+ if !exists('s:go_env')
+ let s:go_env = {
+ \ 'GOPATH': a:goenv_output[0],
+ \ 'GOROOT': a:goenv_output[1],
+ \}
endif
- let l:pkgfile = s:PkgFile(l:buffer)
+ let l:gopath_env_command = has('win32')
+ \ ? 'set GOPATH=' . ale#Escape(s:go_env.GOPATH) . ' && '
+ \ : 'GOPATH=' . ale#Escape(s:go_env.GOPATH) . ' '
- if a:file =~ '/' . l:pkgfile . '$'
- return l:buffer
- endif
- endfor
-
- return -1
+ " Run go test in local directory with relative path
+ return l:gopath_env_command
+ \ . ale#path#BufferCdString(a:buffer)
+ \ . 'go test'
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' -c -o /dev/null ./'
endfunction
-let s:path_pattern = '[a-zA-Z]\?\\\?:\?[[:alnum:]/\.\-_]\+'
-let s:handler_pattern = '^\(' . s:path_pattern . '\):\(\d\+\):\?\(\d\+\)\?: \(.\+\)$'
+function! ale_linters#go#gobuild#GetMatches(lines) abort
+ " Matches patterns like the following:
+ "
+ " file.go:27: missing argument for Printf("%s"): format reads arg 2, have only 1 args
+ " file.go:53:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
+ " file.go:5:2: expected declaration, found 'STRING' "log"
-let s:multibuffer = 0
+ " go test returns relative paths so use tail of filename as part of pattern matcher
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? (.+)$'
+
+ return ale#util#GetMatches(a:lines, l:pattern)
+endfunction
function! ale_linters#go#gobuild#Handler(buffer, lines) abort
- let l:output = []
+ let l:dir = expand('#' . a:buffer . ':p:h')
+ let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, s:handler_pattern)
+ for l:match in ale_linters#go#gobuild#GetMatches(a:lines)
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4],
+ \ 'type': 'E',
+ \})
+ endfor
- if len(l:match) == 0
- continue
- endif
-
- let l:buffer = s:FindBuffer(l:match[1])
-
- if l:buffer == -1
- continue
- endif
-
- if !s:multibuffer && l:buffer != a:buffer
- " strip lines from other buffers
- continue
- endif
-
- call add(l:output, {
- \ 'bufnr': l:buffer,
- \ 'lnum': l:match[2] + 0,
- \ 'col': l:match[3] + 0,
- \ 'text': l:match[4],
- \ 'type': 'E',
- \})
- endfor
-
- return l:output
+ return l:output
endfunction
call ale#linter#Define('go', {
@@ -210,9 +74,8 @@ call ale#linter#Define('go', {
\ 'executable': 'go',
\ 'command_chain': [
\ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#go#gobuild#GoList', 'output_stream': 'stdout'},
-\ {'callback': 'ale_linters#go#gobuild#CopyFiles', 'output_stream': 'stdout'},
\ {'callback': 'ale_linters#go#gobuild#GetCommand', 'output_stream': 'stderr'},
\ ],
\ 'callback': 'ale_linters#go#gobuild#Handler',
+\ 'lint_file': 1,
\})
diff --git a/ale_linters/go/gofmt.vim b/ale_linters/go/gofmt.vim
index b38e4e9..337deef 100644
--- a/ale_linters/go/gofmt.vim
+++ b/ale_linters/go/gofmt.vim
@@ -6,5 +6,5 @@ call ale#linter#Define('go', {
\ 'output_stream': 'stderr',
\ 'executable': 'gofmt',
\ 'command': 'gofmt -e %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/go/golint.vim b/ale_linters/go/golint.vim
index 8fe5b69..d580fda 100644
--- a/ale_linters/go/golint.vim
+++ b/ale_linters/go/golint.vim
@@ -3,7 +3,8 @@
call ale#linter#Define('go', {
\ 'name': 'golint',
+\ 'output_stream': 'both',
\ 'executable': 'golint',
\ 'command': 'golint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/go/gometalinter.vim b/ale_linters/go/gometalinter.vim
new file mode 100644
index 0000000..375a8b0
--- /dev/null
+++ b/ale_linters/go/gometalinter.vim
@@ -0,0 +1,62 @@
+" Author: Ben Reedy , Jeff Willette
+" Description: Adds support for the gometalinter suite for Go files
+
+call ale#Set('go_gometalinter_options', '')
+call ale#Set('go_gometalinter_executable', 'gometalinter')
+call ale#Set('go_gometalinter_lint_package', 0)
+
+function! ale_linters#go#gometalinter#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'go_gometalinter_executable')
+endfunction
+
+function! ale_linters#go#gometalinter#GetCommand(buffer) abort
+ let l:executable = ale_linters#go#gometalinter#GetExecutable(a:buffer)
+ let l:filename = expand('#' . a:buffer . ':t')
+ let l:options = ale#Var(a:buffer, 'go_gometalinter_options')
+ let l:lint_package = ale#Var(a:buffer, 'go_gometalinter_lint_package')
+
+ " BufferCdString is used so that we can be sure the paths output from gometalinter can
+ " be calculated to absolute paths in the Handler
+ if l:lint_package
+ return ale#path#BufferCdString(a:buffer)
+ \ . ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '') . ' .'
+ endif
+
+ return ale#path#BufferCdString(a:buffer)
+ \ . ale#Escape(l:executable)
+ \ . ' --include=' . ale#Escape(ale#util#EscapePCRE(l:filename))
+ \ . (!empty(l:options) ? ' ' . l:options : '') . ' .'
+endfunction
+
+function! ale_linters#go#gometalinter#GetMatches(lines) abort
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:?:?(warning|error):?\s\*?(.+)$'
+
+ return ale#util#GetMatches(a:lines, l:pattern)
+endfunction
+
+function! ale_linters#go#gometalinter#Handler(buffer, lines) abort
+ let l:dir = expand('#' . a:buffer . ':p:h')
+ let l:output = []
+
+ for l:match in ale_linters#go#gometalinter#GetMatches(a:lines)
+ " l:match[1] will already be an absolute path, output from gometalinter
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'type': tolower(l:match[4]) is# 'warning' ? 'W' : 'E',
+ \ 'text': l:match[5],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('go', {
+\ 'name': 'gometalinter',
+\ 'executable_callback': 'ale_linters#go#gometalinter#GetExecutable',
+\ 'command_callback': 'ale_linters#go#gometalinter#GetCommand',
+\ 'callback': 'ale_linters#go#gometalinter#Handler',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/go/gosimple.vim b/ale_linters/go/gosimple.vim
new file mode 100644
index 0000000..8a4c01e
--- /dev/null
+++ b/ale_linters/go/gosimple.vim
@@ -0,0 +1,11 @@
+" Author: Ben Reedy
+" Description: gosimple for Go files
+
+call ale#linter#Define('go', {
+\ 'name': 'gosimple',
+\ 'executable': 'gosimple',
+\ 'command': 'gosimple %s',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\ 'output_stream': 'both',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/go/gotype.vim b/ale_linters/go/gotype.vim
new file mode 100644
index 0000000..731f4c9
--- /dev/null
+++ b/ale_linters/go/gotype.vim
@@ -0,0 +1,23 @@
+" Author: Jelte Fennema
+" Description: gotype for Go files
+
+call ale#linter#Define('go', {
+\ 'name': 'gotype',
+\ 'output_stream': 'stderr',
+\ 'executable': 'gotype',
+\ 'command_callback': 'ale_linters#go#gotype#GetCommand',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
+\})
+
+"\ 'command':
+function! ale_linters#go#gotype#GetCommand(buffer) abort
+ let l:cur_file = expand('#' . a:buffer . ':p')
+ if l:cur_file =~# '_test\.go$'
+ return
+ endif
+
+ let l:module_files = globpath(expand('#' . a:buffer . ':p:h'), '*.go', 0, 1)
+ let l:other_module_files = filter(l:module_files, 'v:val isnot# ' . ale#util#EscapeVim(l:cur_file) . ' && v:val !~# "_test\.go$"')
+ return 'gotype %t ' . join(map(l:other_module_files, 'ale#Escape(v:val)'))
+
+endfunction
diff --git a/ale_linters/go/govet.vim b/ale_linters/go/govet.vim
index e605a29..aae5969 100644
--- a/ale_linters/go/govet.vim
+++ b/ale_linters/go/govet.vim
@@ -1,10 +1,35 @@
" Author: neersighted
" Description: go vet for Go files
+"
+" Author: John Eikenberry
+" Description: updated to work with go1.10
+
+function! ale_linters#go#govet#GetCommand(buffer) abort
+ return ale#path#BufferCdString(a:buffer) . ' go vet .'
+endfunction
+
+function! ale_linters#go#govet#Handler(buffer, lines) abort
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$'
+ let l:output = []
+ let l:dir = expand('#' . a:buffer . ':p:h')
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4],
+ \ 'type': 'E',
+ \})
+ endfor
+ return l:output
+endfunction
call ale#linter#Define('go', {
\ 'name': 'go vet',
\ 'output_stream': 'stderr',
\ 'executable': 'go',
-\ 'command': 'go vet %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'command_callback': 'ale_linters#go#govet#GetCommand',
+\ 'callback': 'ale_linters#go#govet#Handler',
+\ 'lint_file': 1,
\})
diff --git a/ale_linters/go/staticcheck.vim b/ale_linters/go/staticcheck.vim
new file mode 100644
index 0000000..ce9e6e3
--- /dev/null
+++ b/ale_linters/go/staticcheck.vim
@@ -0,0 +1,33 @@
+" Author: Ben Reedy
+" Description: staticcheck for Go files
+
+call ale#Set('go_staticcheck_options', '')
+call ale#Set('go_staticcheck_lint_package', 0)
+
+function! ale_linters#go#staticcheck#GetCommand(buffer) abort
+ let l:filename = expand('#' . a:buffer . ':t')
+ let l:options = ale#Var(a:buffer, 'go_staticcheck_options')
+ let l:lint_package = ale#Var(a:buffer, 'go_staticcheck_lint_package')
+
+ " BufferCdString is used so that we can be sure the paths output from
+ " staticcheck can be calculated to absolute paths in the Handler
+ if l:lint_package
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'staticcheck'
+ \ . (!empty(l:options) ? ' ' . l:options : '') . ' .'
+ endif
+
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'staticcheck'
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' ' . ale#Escape(l:filename)
+endfunction
+
+call ale#linter#Define('go', {
+\ 'name': 'staticcheck',
+\ 'executable': 'staticcheck',
+\ 'command_callback': 'ale_linters#go#staticcheck#GetCommand',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\ 'output_stream': 'both',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/graphql/eslint.vim b/ale_linters/graphql/eslint.vim
new file mode 100644
index 0000000..dfcbf9d
--- /dev/null
+++ b/ale_linters/graphql/eslint.vim
@@ -0,0 +1,9 @@
+" Author: Benjie Gillam
+" Description: eslint for GraphQL files
+
+call ale#linter#Define('graphql', {
+\ 'name': 'eslint',
+\ 'executable_callback': 'ale#handlers#eslint#GetExecutable',
+\ 'command_callback': 'ale#handlers#eslint#GetCommand',
+\ 'callback': 'ale#handlers#eslint#Handle',
+\})
diff --git a/ale_linters/graphql/gqlint.vim b/ale_linters/graphql/gqlint.vim
new file mode 100644
index 0000000..882cc69
--- /dev/null
+++ b/ale_linters/graphql/gqlint.vim
@@ -0,0 +1,9 @@
+" Author: Michiel Westerbeek
+" Description: Linter for GraphQL Schemas
+
+call ale#linter#Define('graphql', {
+\ 'name': 'gqlint',
+\ 'executable': 'gqlint',
+\ 'command': 'gqlint --reporter=simple %t',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\})
diff --git a/ale_linters/haml/hamllint.vim b/ale_linters/haml/hamllint.vim
index fead747..d663359 100644
--- a/ale_linters/haml/hamllint.vim
+++ b/ale_linters/haml/hamllint.vim
@@ -1,21 +1,39 @@
-" Author: Patrick Lewis - https://github.com/patricklewis
+" Author: Patrick Lewis - https://github.com/patricklewis, thenoseman - https://github.com/thenoseman
" Description: haml-lint for Haml files
+function! ale_linters#haml#hamllint#GetCommand(buffer) abort
+ let l:prefix = ''
+
+ let l:rubocop_config_file_path = ale#path#FindNearestFile(a:buffer, '.rubocop.yml')
+ let l:hamllint_config_file_path = ale#path#FindNearestFile(a:buffer, '.haml-lint.yml')
+
+ " Set HAML_LINT_RUBOCOP_CONF variable as it is needed for haml-lint to
+ " pick up the rubocop config.
+ "
+ " See https://github.com/brigade/haml-lint/blob/master/lib/haml_lint/linter/rubocop.rb#L89
+ " HamlLint::Linter::RuboCop#rubocop_flags
+ if !empty(l:rubocop_config_file_path)
+ if ale#Has('win32')
+ let l:prefix = 'set HAML_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config_file_path) . ' &&'
+ else
+ let l:prefix = 'HAML_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config_file_path)
+ endif
+ endif
+
+ return (!empty(l:prefix) ? l:prefix . ' ' : '')
+ \ . 'haml-lint'
+ \ . (!empty(l:hamllint_config_file_path) ? ' --config ' . ale#Escape(l:hamllint_config_file_path) : '')
+ \ . ' %t'
+endfunction
+
function! ale_linters#haml#hamllint#Handle(buffer, lines) abort
" Matches patterns like the following:
" :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
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'type': l:match[2],
\ 'text': l:match[3]
@@ -28,6 +46,6 @@ endfunction
call ale#linter#Define('haml', {
\ 'name': 'hamllint',
\ 'executable': 'haml-lint',
-\ 'command': 'haml-lint %t',
+\ 'command_callback': 'ale_linters#haml#hamllint#GetCommand',
\ 'callback': 'ale_linters#haml#hamllint#Handle'
\})
diff --git a/ale_linters/handlebars/embertemplatelint.vim b/ale_linters/handlebars/embertemplatelint.vim
new file mode 100644
index 0000000..68ea715
--- /dev/null
+++ b/ale_linters/handlebars/embertemplatelint.vim
@@ -0,0 +1,48 @@
+" Author: Adrian Zalewski
+" Description: Ember-template-lint for checking Handlebars files
+
+call ale#Set('handlebars_embertemplatelint_executable', 'ember-template-lint')
+call ale#Set('handlebars_embertemplatelint_use_global', 0)
+
+function! ale_linters#handlebars#embertemplatelint#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'handlebars_embertemplatelint', [
+ \ 'node_modules/.bin/ember-template-lint',
+ \])
+endfunction
+
+function! ale_linters#handlebars#embertemplatelint#GetCommand(buffer) abort
+ return ale_linters#handlebars#embertemplatelint#GetExecutable(a:buffer)
+ \ . ' --json %t'
+endfunction
+
+function! ale_linters#handlebars#embertemplatelint#Handle(buffer, lines) abort
+ let l:output = []
+ let l:json = ale#util#FuzzyJSONDecode(a:lines, {})
+
+ for l:error in get(values(l:json), 0, [])
+ if has_key(l:error, 'fatal')
+ call add(l:output, {
+ \ 'lnum': get(l:error, 'line', 1),
+ \ 'col': get(l:error, 'column', 1),
+ \ 'text': l:error.message,
+ \ 'type': l:error.severity == 1 ? 'W' : 'E',
+ \})
+ else
+ call add(l:output, {
+ \ 'lnum': l:error.line,
+ \ 'col': l:error.column,
+ \ 'text': l:error.rule . ': ' . l:error.message,
+ \ 'type': l:error.severity == 1 ? 'W' : 'E',
+ \})
+ endif
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('handlebars', {
+\ 'name': 'ember-template-lint',
+\ 'executable_callback': 'ale_linters#handlebars#embertemplatelint#GetExecutable',
+\ 'command_callback': 'ale_linters#handlebars#embertemplatelint#GetCommand',
+\ 'callback': 'ale_linters#handlebars#embertemplatelint#Handle',
+\})
diff --git a/ale_linters/haskell/ghc-mod.vim b/ale_linters/haskell/ghc-mod.vim
new file mode 100644
index 0000000..1b15d8c
--- /dev/null
+++ b/ale_linters/haskell/ghc-mod.vim
@@ -0,0 +1,16 @@
+" Author: wizzup
+" Description: ghc-mod for Haskell files
+
+call ale#linter#Define('haskell', {
+\ 'name': 'ghc-mod',
+\ 'executable': 'ghc-mod',
+\ 'command': 'ghc-mod --map-file %s=%t check %s',
+\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
+\})
+
+call ale#linter#Define('haskell', {
+\ 'name': 'stack-ghc-mod',
+\ 'executable': 'stack',
+\ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s',
+\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
+\})
diff --git a/ale_linters/haskell/ghc.vim b/ale_linters/haskell/ghc.vim
index 270f8ab..daf91c8 100644
--- a/ale_linters/haskell/ghc.vim
+++ b/ale_linters/haskell/ghc.vim
@@ -1,18 +1,18 @@
" Author: w0rp
" Description: ghc for Haskell files
+call ale#Set('haskell_ghc_options', '-fno-code -v0')
+
+function! ale_linters#haskell#ghc#GetCommand(buffer) abort
+ return 'ghc '
+ \ . ale#Var(a:buffer, 'haskell_ghc_options')
+ \ . ' %t'
+endfunction
+
call ale#linter#Define('haskell', {
\ 'name': 'ghc',
\ 'output_stream': 'stderr',
\ 'executable': 'ghc',
-\ 'command': 'ghc -fno-code -v0 %t',
-\ 'callback': 'ale#handlers#HandleGhcFormat',
-\})
-
-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_callback': 'ale_linters#haskell#ghc#GetCommand',
+\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/hdevtools.vim b/ale_linters/haskell/hdevtools.vim
index 92bb827..93c7ddd 100644
--- a/ale_linters/haskell/hdevtools.vim
+++ b/ale_linters/haskell/hdevtools.vim
@@ -1,9 +1,22 @@
-" Author: rob-b
+" Author: rob-b, Takano Akio
" Description: hdevtools for Haskell files
+call ale#Set('haskell_hdevtools_executable', 'hdevtools')
+call ale#Set('haskell_hdevtools_options', '-g -Wall')
+
+function! ale_linters#haskell#hdevtools#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'haskell_hdevtools_executable')
+endfunction
+
+function! ale_linters#haskell#hdevtools#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#haskell#hdevtools#GetExecutable(a:buffer))
+ \ . ' check ' . ale#Var(a:buffer, 'haskell_hdevtools_options')
+ \ . ' -p %s %t'
+endfunction
+
call ale#linter#Define('haskell', {
\ 'name': 'hdevtools',
-\ 'executable': 'hdevtools',
-\ 'command': 'hdevtools check -g -Wall -p %s %t',
-\ 'callback': 'ale#handlers#HandleGhcFormat',
+\ 'executable_callback': 'ale_linters#haskell#hdevtools#GetExecutable',
+\ 'command_callback': 'ale_linters#haskell#hdevtools#GetCommand',
+\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})
diff --git a/ale_linters/haskell/hlint.vim b/ale_linters/haskell/hlint.vim
index 6a907ab..be40d92 100644
--- a/ale_linters/haskell/hlint.vim
+++ b/ale_linters/haskell/hlint.vim
@@ -2,18 +2,24 @@
" Description: hlint for Haskell files
function! ale_linters#haskell#hlint#Handle(buffer, lines) abort
- let l:errors = json_decode(join(a:lines, ''))
-
let l:output = []
- for l:error in l:errors
- " vcol is Needed to indicate that the column is a character.
+ for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
+ if l:error.severity is# 'Error'
+ let l:type = 'E'
+ elseif l:error.severity is# 'Suggestion'
+ let l:type = 'I'
+ else
+ let l:type = 'W'
+ endif
+
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:error.startLine + 0,
- \ 'col': l:error.startColumn + 0,
+ \ 'lnum': str2nr(l:error.startLine),
+ \ 'col': str2nr(l:error.startColumn),
+ \ 'end_lnum': str2nr(l:error.endLine),
+ \ 'end_col': str2nr(l:error.endColumn),
\ 'text': l:error.severity . ': ' . l:error.hint . '. Found: ' . l:error.from . ' Why not: ' . l:error.to,
- \ 'type': l:error.severity ==# 'Error' ? 'E' : 'W',
+ \ 'type': l:type,
\})
endfor
diff --git a/ale_linters/haskell/stack_build.vim b/ale_linters/haskell/stack_build.vim
new file mode 100644
index 0000000..44e2e86
--- /dev/null
+++ b/ale_linters/haskell/stack_build.vim
@@ -0,0 +1,22 @@
+" Author: Jake Zimmerman
+" Description: Like stack-ghc, but for entire projects
+"
+" Note: Ideally, this would *only* typecheck. Right now, it also does codegen.
+" See .
+
+call ale#Set('haskell_stack_build_options', '--fast')
+
+function! ale_linters#haskell#stack_build#GetCommand(buffer) abort
+ let l:flags = ale#Var(a:buffer, 'haskell_stack_build_options')
+
+ return 'stack build ' . l:flags
+endfunction
+
+call ale#linter#Define('haskell', {
+\ 'name': 'stack-build',
+\ 'output_stream': 'stderr',
+\ 'executable': 'stack',
+\ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand',
+\ 'lint_file': 1,
+\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
+\})
diff --git a/ale_linters/haskell/stack_ghc.vim b/ale_linters/haskell/stack_ghc.vim
new file mode 100644
index 0000000..0367dc2
--- /dev/null
+++ b/ale_linters/haskell/stack_ghc.vim
@@ -0,0 +1,10 @@
+" Author: w0rp
+" Description: ghc for Haskell files, using Stack
+
+call ale#linter#Define('haskell', {
+\ 'name': 'stack-ghc',
+\ 'output_stream': 'stderr',
+\ 'executable': 'stack',
+\ 'command': 'stack ghc -- -fno-code -v0 %t',
+\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
+\})
diff --git a/ale_linters/help/alex.vim b/ale_linters/help/alex.vim
new file mode 100644
index 0000000..21b23b4
--- /dev/null
+++ b/ale_linters/help/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for help files
+
+call ale#linter#Define('help', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/help/proselint.vim b/ale_linters/help/proselint.vim
index cd6cb7f..6212450 100644
--- a/ale_linters/help/proselint.vim
+++ b/ale_linters/help/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('help', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/help/write-good.vim b/ale_linters/help/write-good.vim
new file mode 100644
index 0000000..11254cd
--- /dev/null
+++ b/ale_linters/help/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for vim Help files
+
+call ale#linter#Define('help', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/html/alex.vim b/ale_linters/html/alex.vim
new file mode 100644
index 0000000..5a1f61e
--- /dev/null
+++ b/ale_linters/html/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for HTML files
+
+call ale#linter#Define('html', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/html/htmlhint.vim b/ale_linters/html/htmlhint.vim
index c4afcee..88a83f1 100644
--- a/ale_linters/html/htmlhint.vim
+++ b/ale_linters/html/htmlhint.vim
@@ -1,36 +1,38 @@
" Author: KabbAmine , deathmaz <00maz1987@gmail.com>, diartyz
" Description: HTMLHint for checking html files
-" CLI options
-let g:ale_html_htmlhint_options = get(g:, 'ale_html_htmlhint_options', '--format=unix')
-
-let g:ale_html_htmlhint_executable =
-\ get(g:, 'ale_html_htmlhint_executable', 'htmlhint')
-
-let g:ale_html_htmlhint_use_global =
-\ get(g:, 'ale_html_htmlhint_use_global', 0)
+call ale#Set('html_htmlhint_options', '')
+call ale#Set('html_htmlhint_executable', 'htmlhint')
+call ale#Set('html_htmlhint_use_global', 0)
function! ale_linters#html#htmlhint#GetExecutable(buffer) abort
- if g:ale_html_htmlhint_use_global
- return g:ale_html_htmlhint_executable
- endif
-
- return ale#util#ResolveLocalPath(
- \ a:buffer,
+ return ale#node#FindExecutable(a:buffer, 'html_htmlhint', [
\ 'node_modules/.bin/htmlhint',
- \ g:ale_html_htmlhint_executable
- \)
+ \])
endfunction
function! ale_linters#html#htmlhint#GetCommand(buffer) abort
- return ale_linters#html#htmlhint#GetExecutable(a:buffer)
- \ . ' ' . g:ale_html_htmlhint_options
- \ . ' %t'
+ let l:options = ale#Var(a:buffer, 'html_htmlhint_options')
+ let l:config = l:options !~# '--config'
+ \ ? ale#path#FindNearestFile(a:buffer, '.htmlhintrc')
+ \ : ''
+
+ if !empty(l:config)
+ let l:options .= ' --config ' . ale#Escape(l:config)
+ endif
+
+ if !empty(l:options)
+ let l:options = substitute(l:options, '--format=unix', '', '')
+ endif
+
+ return ale#Escape(ale_linters#html#htmlhint#GetExecutable(a:buffer))
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --format=unix %t'
endfunction
call ale#linter#Define('html', {
\ 'name': 'htmlhint',
\ 'executable_callback': 'ale_linters#html#htmlhint#GetExecutable',
\ 'command_callback': 'ale_linters#html#htmlhint#GetCommand',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/html/proselint.vim b/ale_linters/html/proselint.vim
index 50ab5e4..9fd7d67 100644
--- a/ale_linters/html/proselint.vim
+++ b/ale_linters/html/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('html', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/html/tidy.vim b/ale_linters/html/tidy.vim
index de873c8..34152c6 100644
--- a/ale_linters/html/tidy.vim
+++ b/ale_linters/html/tidy.vim
@@ -3,7 +3,9 @@
" CLI options
let g:ale_html_tidy_executable = get(g:, 'ale_html_tidy_executable', 'tidy')
-let g:ale_html_tidy_args = get(g:, 'ale_html_tidy_args', '-q -e -language en')
+" Look for the old _args variable first.
+let s:default_options = get(g:, 'ale_html_tidy_args', '-q -e -language en')
+let g:ale_html_tidy_options = get(g:, 'ale_html_tidy_options', s:default_options)
function! ale_linters#html#tidy#GetCommand(buffer) abort
" Specify file encoding in options
@@ -23,11 +25,23 @@ function! ale_linters#html#tidy#GetCommand(buffer) abort
\ 'utf-8': '-utf8',
\ }, &fileencoding, '-utf8')
+ " On macOS, old tidy (released on 31 Oct 2006) is installed. It does not
+ " consider HTML5 so we should avoid it.
+ let l:executable = ale#Var(a:buffer, 'html_tidy_executable')
+ if has('mac') && l:executable is# 'tidy' && exists('*exepath')
+ \ && exepath(l:executable) is# '/usr/bin/tidy'
+ return ''
+ endif
+
return printf('%s %s %s -',
- \ g:ale_html_tidy_executable,
- \ g:ale_html_tidy_args,
+ \ l:executable,
+ \ ale#Var(a:buffer, 'html_tidy_options'),
\ l:file_encoding
- \ )
+ \)
+endfunction
+
+function! ale_linters#html#tidy#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'html_tidy_executable')
endfunction
function! ale_linters#html#tidy#Handle(buffer, lines) abort
@@ -37,21 +51,13 @@ function! ale_linters#html#tidy#Handle(buffer, lines) abort
let l:pattern = '^line \(\d\+\) column \(\d\+\) - \(Warning\|Error\): \(.\+\)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
- endif
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0
let l:col = l:match[2] + 0
- let l:type = l:match[3] ==# 'Error' ? 'E' : 'W'
+ let l:type = l:match[3] is# '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,
\ 'col': l:col,
\ 'text': l:text,
@@ -64,7 +70,7 @@ endfunction
call ale#linter#Define('html', {
\ 'name': 'tidy',
-\ 'executable': g:ale_html_tidy_executable,
+\ 'executable_callback': 'ale_linters#html#tidy#GetExecutable',
\ 'output_stream': 'stderr',
\ 'command_callback': 'ale_linters#html#tidy#GetCommand',
\ 'callback': 'ale_linters#html#tidy#Handle',
diff --git a/ale_linters/html/write-good.vim b/ale_linters/html/write-good.vim
new file mode 100644
index 0000000..9fae882
--- /dev/null
+++ b/ale_linters/html/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for nroff files
+
+call ale#linter#Define('html', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/idris/idris.vim b/ale_linters/idris/idris.vim
new file mode 100644
index 0000000..115d04f
--- /dev/null
+++ b/ale_linters/idris/idris.vim
@@ -0,0 +1,87 @@
+" Author: Scott Bonds
+" Description: default Idris compiler
+
+call ale#Set('idris_idris_executable', 'idris')
+call ale#Set('idris_idris_options', '--total --warnpartial --warnreach --warnipkg')
+
+function! ale_linters#idris#idris#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'idris_idris_executable')
+endfunction
+
+function! ale_linters#idris#idris#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'idris_idris_options')
+
+ return ale#Escape(ale_linters#idris#idris#GetExecutable(a:buffer))
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --check %s'
+endfunction
+
+function! ale_linters#idris#idris#Handle(buffer, lines) abort
+ " This was copied almost verbatim from ale#handlers#haskell#HandleGHCFormat
+
+ " Look for lines like the following:
+ " foo.idr:2:6:When checking right hand side of main with expected type
+ " bar.idr:11:11-13:
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\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 len(l:corrected_lines) > 0
+ if l:line is# ''
+ let l:corrected_lines[-1] .= ' ' " turn a blank line into a space
+ else
+ let l:corrected_lines[-1] .= l:line
+ endif
+ let l:corrected_lines[-1] = substitute(l:corrected_lines[-1], '\s\+', ' ', 'g')
+ endif
+ endfor
+
+ for l:line in l:corrected_lines
+ let l:match = matchlist(l:line, l:pattern)
+
+ if len(l:match) == 0
+ continue
+ endif
+
+ if !ale#path#IsBufferPath(a:buffer, l:match[1])
+ continue
+ endif
+
+ let l:errors = matchlist(l:match[5], '\v([wW]arning|[eE]rror) - ?(.*)')
+
+ if len(l:errors) > 0
+ let l:ghc_type = l:errors[1]
+ let l:text = l:errors[2]
+ else
+ let l:ghc_type = ''
+ let l:text = l:match[5][:0] is# ' ' ? l:match[5][1:] : l:match[5]
+ endif
+
+ if l:ghc_type is? 'Warning'
+ let l:type = 'W'
+ else
+ let l:type = 'E'
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:text,
+ \ 'type': l:type,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('idris', {
+\ 'name': 'idris',
+\ 'executable_callback': 'ale_linters#idris#idris#GetExecutable',
+\ 'command_callback': 'ale_linters#idris#idris#GetCommand',
+\ 'callback': 'ale_linters#idris#idris#Handle',
+\})
+
diff --git a/ale_linters/java/checkstyle.vim b/ale_linters/java/checkstyle.vim
new file mode 100644
index 0000000..8155170
--- /dev/null
+++ b/ale_linters/java/checkstyle.vim
@@ -0,0 +1,36 @@
+" Author: Devon Meunier
+" Description: checkstyle for Java files
+
+function! ale_linters#java#checkstyle#Handle(buffer, lines) abort
+ let l:pattern = '\v\[(WARN|ERROR)\] [a-zA-Z]?:?[^:]+:(\d+):(\d+)?:? (.*) \[(.+)\]$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'type': l:match[1] is? 'WARN' ? 'W' : 'E',
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4],
+ \ 'code': l:match[5],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#java#checkstyle#GetCommand(buffer) abort
+ return 'checkstyle '
+ \ . ale#Var(a:buffer, 'java_checkstyle_options')
+ \ . ' %t'
+endfunction
+
+if !exists('g:ale_java_checkstyle_options')
+ let g:ale_java_checkstyle_options = '-c /google_checks.xml'
+endif
+
+call ale#linter#Define('java', {
+\ 'name': 'checkstyle',
+\ 'executable': 'checkstyle',
+\ 'command_callback': 'ale_linters#java#checkstyle#GetCommand',
+\ 'callback': 'ale_linters#java#checkstyle#Handle',
+\})
diff --git a/ale_linters/java/javac.vim b/ale_linters/java/javac.vim
index 5652d9d..73e8414 100644
--- a/ale_linters/java/javac.vim
+++ b/ale_linters/java/javac.vim
@@ -1,17 +1,86 @@
" Author: farenjihn , w0rp
" Description: Lints java files using javac
+let s:classpath_sep = has('unix') ? ':' : ';'
+
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
- \ : ''
+function! ale_linters#java#javac#GetImportPaths(buffer) abort
+ let l:pom_path = ale#path#FindNearestFile(a:buffer, 'pom.xml')
- return 'javac -Xlint '
- \ . l:cp_option
- \ . ' ' . g:ale_java_javac_options
+ if !empty(l:pom_path) && executable('mvn')
+ return ale#path#CdString(fnamemodify(l:pom_path, ':h'))
+ \ . 'mvn dependency:build-classpath'
+ endif
+
+ let l:classpath_command = ale#gradle#BuildClasspathCommand(a:buffer)
+ if !empty(l:classpath_command)
+ return l:classpath_command
+ endif
+
+ return ''
+endfunction
+
+function! s:BuildClassPathOption(buffer, import_paths) abort
+ " Filter out lines like [INFO], etc.
+ let l:class_paths = filter(a:import_paths[:], 'v:val !~# ''[''')
+ call extend(
+ \ l:class_paths,
+ \ split(ale#Var(a:buffer, 'java_javac_classpath'), s:classpath_sep),
+ \)
+
+ return !empty(l:class_paths)
+ \ ? '-cp ' . ale#Escape(join(l:class_paths, s:classpath_sep))
+ \ : ''
+endfunction
+
+function! ale_linters#java#javac#GetCommand(buffer, import_paths) abort
+ let l:cp_option = s:BuildClassPathOption(a:buffer, a:import_paths)
+ let l:sp_option = ''
+
+ " Find the src directory, for files in this project.
+ let l:src_dir = ale#path#FindNearestDirectory(a:buffer, 'src/main/java')
+ let l:sp_dirs = []
+
+ if !empty(l:src_dir)
+ call add(l:sp_dirs, l:src_dir)
+
+ " Automatically include the jaxb directory too, if it's there.
+ let l:jaxb_dir = fnamemodify(l:src_dir, ':h:h')
+ \ . (has('win32') ? '\jaxb\' : '/jaxb/')
+
+ if isdirectory(l:jaxb_dir)
+ call add(l:sp_dirs, l:jaxb_dir)
+ endif
+
+ " Automatically include the test directory, but only for test code.
+ if expand('#' . a:buffer . ':p') =~? '\vsrc[/\\]test[/\\]java'
+ let l:test_dir = fnamemodify(l:src_dir, ':h:h:h')
+ \ . (has('win32') ? '\test\java\' : '/test/java/')
+
+ if isdirectory(l:test_dir)
+ call add(l:sp_dirs, l:test_dir)
+ endif
+ endif
+ endif
+
+ if !empty(l:sp_dirs)
+ let l:sp_option = '-sourcepath '
+ \ . ale#Escape(join(l:sp_dirs, s:classpath_sep))
+ endif
+
+ " Create .class files in a temporary directory, which we will delete later.
+ let l:class_file_directory = ale#engine#CreateDirectory(a:buffer)
+
+ " Always run javac from the directory the file is in, so we can resolve
+ " relative paths correctly.
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'javac -Xlint'
+ \ . ' ' . l:cp_option
+ \ . ' ' . l:sp_option
+ \ . ' -d ' . ale#Escape(l:class_file_directory)
+ \ . ' ' . ale#Var(a:buffer, 'java_javac_options')
\ . ' %t'
endfunction
@@ -21,23 +90,28 @@ function! ale_linters#java#javac#Handle(buffer, lines) abort
" Main.java:13: warning: [deprecation] donaught() in Testclass has been deprecated
" Main.java:16: error: ';' expected
- let l:pattern = '^.*\:\(\d\+\):\ \(.*\):\(.*\)$'
+ let l:directory = expand('#' . a:buffer . ':p:h')
+ let l:pattern = '\v^(.*):(\d+): (.+):(.+)$'
+ let l:col_pattern = '\v^(\s*\^)$'
+ let l:symbol_pattern = '\v^ +symbol: *(class|method) +([^ ]+)'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
+ for l:match in ale#util#GetMatches(a:lines, [l:pattern, l:col_pattern, l:symbol_pattern])
+ if empty(l:match[2]) && empty(l:match[3])
+ let l:output[-1].col = len(l:match[1])
+ elseif empty(l:match[3])
+ " Add symbols to 'cannot find symbol' errors.
+ if l:output[-1].text is# 'error: cannot find symbol'
+ let l:output[-1].text .= ': ' . l:match[2]
+ endif
+ else
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:directory, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'text': l:match[3] . ':' . l:match[4],
+ \ 'type': l:match[3] is# 'error' ? 'E' : 'W',
+ \})
endif
-
- call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[1] + 0,
- \ 'col': 1,
- \ 'text': l:match[2] . ':' . l:match[3],
- \ 'type': l:match[2] ==# 'error' ? 'E' : 'W',
- \})
endfor
return l:output
@@ -45,8 +119,10 @@ endfunction
call ale#linter#Define('java', {
\ 'name': 'javac',
-\ 'output_stream': 'stderr',
\ 'executable': 'javac',
-\ 'command_callback': 'ale_linters#java#javac#GetCommand',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#java#javac#GetImportPaths', 'output_stream': 'stdout'},
+\ {'callback': 'ale_linters#java#javac#GetCommand', 'output_stream': 'stderr'},
+\ ],
\ 'callback': 'ale_linters#java#javac#Handle',
\})
diff --git a/ale_linters/javascript/eslint.vim b/ale_linters/javascript/eslint.vim
index ac88dd0..23e1694 100644
--- a/ale_linters/javascript/eslint.vim
+++ b/ale_linters/javascript/eslint.vim
@@ -1,97 +1,10 @@
" Author: w0rp
" Description: eslint for JavaScript files
-let g:ale_javascript_eslint_executable =
-\ get(g:, 'ale_javascript_eslint_executable', 'eslint')
-
-let g:ale_javascript_eslint_options =
-\ get(g:, 'ale_javascript_eslint_options', '')
-
-let g:ale_javascript_eslint_use_global =
-\ get(g:, 'ale_javascript_eslint_use_global', 0)
-
-function! ale_linters#javascript#eslint#GetExecutable(buffer) abort
- if g:ale_javascript_eslint_use_global
- return g:ale_javascript_eslint_executable
- endif
-
- return ale#util#ResolveLocalPath(
- \ a:buffer,
- \ 'node_modules/.bin/eslint',
- \ g:ale_javascript_eslint_executable
- \)
-endfunction
-
-function! ale_linters#javascript#eslint#GetCommand(buffer) abort
- return ale_linters#javascript#eslint#GetExecutable(a:buffer)
- \ . ' ' . g:ale_javascript_eslint_options
- \ . ' -f unix --stdin --stdin-filename %s'
-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]
- " /path/to/some-filename.js:56:41: Missing semicolon. [Error/semi]
- let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) \[\(.\+\)\]$'
- " This second pattern matches lines like the following:
- "
- " /path/to/some-filename.js:13:3: Parsing error: Unexpected token
- let l:parsing_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
- " Try the parsing pattern for parsing errors.
- let l:match = matchlist(l:line, l:parsing_pattern)
- endif
-
- if len(l:match) == 0
- continue
- endif
-
- let l:type = 'Error'
- let l:text = l:match[3]
-
- " Take the error type from the output if available.
- if !empty(l:match[4])
- let l:type = split(l:match[4], '/')[0]
- 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,
- \ 'col': l:match[2] + 0,
- \ 'text': l:text,
- \ 'type': l:type ==# 'Warning' ? 'W' : 'E',
- \})
- endfor
-
- return l:output
-endfunction
-
call ale#linter#Define('javascript', {
\ 'name': 'eslint',
-\ 'executable_callback': 'ale_linters#javascript#eslint#GetExecutable',
-\ 'command_callback': 'ale_linters#javascript#eslint#GetCommand',
-\ 'callback': 'ale_linters#javascript#eslint#Handle',
+\ 'output_stream': 'both',
+\ 'executable_callback': 'ale#handlers#eslint#GetExecutable',
+\ 'command_callback': 'ale#handlers#eslint#GetCommand',
+\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim
old mode 100644
new mode 100755
index 98bda75..643ea19
--- a/ale_linters/javascript/flow.vim
+++ b/ale_linters/javascript/flow.vim
@@ -1,40 +1,119 @@
" Author: Zach Perrault -- @zperrault
+" Author: Florian Beeres
" Description: FlowType checking for JavaScript files
-let g:ale_javascript_flow_executable =
-\ get(g:, 'ale_javascript_flow_executable', 'flow')
-
-let g:ale_javascript_flow_use_global =
-\ get(g:, 'ale_javascript_flow_use_global', 0)
+call ale#Set('javascript_flow_executable', 'flow')
+call ale#Set('javascript_flow_use_home_config', 0)
+call ale#Set('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
-
- 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')
+ let l:flow_config = ale#path#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'
+ " Don't run Flow with a configuration file from the home directory by
+ " default, which can eat all of your RAM.
+ if fnamemodify(l:flow_config, ':h') is? $HOME
+ \&& !ale#Var(a:buffer, 'javascript_flow_use_home_config')
+ return ''
+ endif
+
+ return ale#node#FindExecutable(a:buffer, 'javascript_flow', [
+ \ 'node_modules/.bin/flow',
+ \])
+endfunction
+
+function! ale_linters#javascript#flow#VersionCheck(buffer) abort
+ let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer)
+
+ if empty(l:executable)
+ return ''
+ endif
+
+ return ale#Escape(l:executable) . ' --version'
+endfunction
+
+function! ale_linters#javascript#flow#GetCommand(buffer, version_lines) abort
+ let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer)
+
+ if empty(l:executable)
+ return ''
+ endif
+
+ let l:version = ale#semver#GetVersion(l:executable, a:version_lines)
+
+ " If we can parse the version number, then only use --respect-pragma
+ " if the version is >= 0.36.0, which added the argument.
+ let l:use_respect_pragma = empty(l:version)
+ \ || ale#semver#GTE(l:version, [0, 36])
+
+ return ale#Escape(l:executable)
+ \ . ' check-contents'
+ \ . (l:use_respect_pragma ? ' --respect-pragma': '')
+ \ . ' --json --from ale %s'
+endfunction
+
+" Filter lines of flow output until we find the first line where the JSON
+" output starts.
+function! s:GetJSONLines(lines) abort
+ let l:start_index = 0
+
+ for l:line in a:lines
+ if l:line[:0] is# '{'
+ break
+ endif
+
+ let l:start_index += 1
+ endfor
+
+ return a:lines[l:start_index :]
+endfunction
+
+function! s:ExtraErrorMsg(current, new) abort
+ let l:newMsg = ''
+
+ if a:current is# ''
+ " extra messages appear to already have a :
+ let l:newMsg = a:new
+ else
+ let l:newMsg = a:current . ' ' . a:new
+ endif
+
+ return l:newMsg
+endfunction
+
+
+function! s:GetDetails(error) abort
+ let l:detail = ''
+
+ for l:extra_error in a:error.extra
+
+ if has_key(l:extra_error, 'message')
+ for l:extra_message in l:extra_error.message
+ let l:detail = s:ExtraErrorMsg(l:detail, l:extra_message.descr)
+ endfor
+ endif
+
+ if has_key(l:extra_error, 'children')
+ for l:child in l:extra_error.children
+ for l:child_message in l:child.message
+ let l:detail = l:detail . ' ' . l:child_message.descr
+ endfor
+ endfor
+ endif
+
+ endfor
+
+ return l:detail
endfunction
function! ale_linters#javascript#flow#Handle(buffer, lines) abort
- let l:str = join(a:lines, '')
+ let l:str = join(s:GetJSONLines(a:lines), '')
- if l:str ==# ''
+ if empty(l:str)
return []
endif
@@ -48,13 +127,17 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort
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
+ " Comments have no line of column information, so we skip them.
+ " In certain cases, `l:message.loc.source` points to a different path
+ " than the buffer one, thus we skip this loc information too.
+ if has_key(l:message, 'loc')
+ \&& l:line is# 0
+ \&& ale#path#IsBufferPath(a:buffer, l:message.loc.source)
let l:line = l:message.loc.start.line + 0
let l:col = l:message.loc.start.column + 0
endif
- if l:text ==# ''
+ if l:text is# ''
let l:text = l:message.descr . ':'
else
let l:text = l:text . ' ' . l:message.descr
@@ -65,13 +148,19 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort
let l:text = l:text . ' See also: ' . l:error.operation.descr
endif
- call add(l:output, {
- \ 'bufnr': a:buffer,
+ let l:errorToAdd = {
\ 'lnum': l:line,
\ 'col': l:col,
\ 'text': l:text,
- \ 'type': l:error.level ==# 'error' ? 'E' : 'W',
- \})
+ \ 'type': has_key(l:error, 'level') && l:error.level is# 'error' ? 'E' : 'W',
+ \}
+
+ if has_key(l:error, 'extra')
+ let l:errorToAdd.detail = s:GetDetails(l:error)
+ endif
+
+ call add(l:output, l:errorToAdd)
+
endfor
return l:output
@@ -80,6 +169,10 @@ endfunction
call ale#linter#Define('javascript', {
\ 'name': 'flow',
\ 'executable_callback': 'ale_linters#javascript#flow#GetExecutable',
-\ 'command_callback': 'ale_linters#javascript#flow#GetCommand',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#javascript#flow#VersionCheck'},
+\ {'callback': 'ale_linters#javascript#flow#GetCommand'},
+\ ],
\ 'callback': 'ale_linters#javascript#flow#Handle',
+\ 'add_newline': !has('win32'),
\})
diff --git a/ale_linters/javascript/jscs.vim b/ale_linters/javascript/jscs.vim
index 52710ba..bcf3ee3 100644
--- a/ale_linters/javascript/jscs.vim
+++ b/ale_linters/javascript/jscs.vim
@@ -1,9 +1,67 @@
" Author: Chris Kyrouac - https://github.com/fijshion
" Description: jscs for JavaScript files
+call ale#Set('javascript_jscs_executable', 'jscs')
+call ale#Set('javascript_jscs_use_global', 0)
+
+function! ale_linters#javascript#jscs#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'javascript_jscs', [
+ \ 'node_modules/.bin/jscs',
+ \])
+endfunction
+
+function! ale_linters#javascript#jscs#GetCommand(buffer) abort
+ " Search for a local JShint config locaation, and default to a global one.
+ let l:jscs_config = ale#path#ResolveLocalPath(
+ \ a:buffer,
+ \ '.jscsrc',
+ \ get(g:, 'ale_jscs_config_loc', '')
+ \)
+
+ let l:command = ale#Escape(ale_linters#javascript#jscs#GetExecutable(a:buffer))
+ let l:command .= ' --reporter inline --no-colors'
+
+ if !empty(l:jscs_config)
+ let l:command .= ' --config ' . ale#Escape(l:jscs_config)
+ endif
+
+ let l:command .= ' -'
+
+ return l:command
+endfunction
+
+function! ale_linters#javascript#jscs#Handle(buffer, lines) abort
+ " Matches patterns looking like the following
+ "
+ " foobar.js: line 2, col 1, Expected indentation of 1 characters
+ "
+ let l:pattern = '\v^.*:\s+line (\d+),\s+col\s+(\d+),\s+(.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:obj = {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[3]
+ \}
+
+ let l:code_match = matchlist(l:match[3], '\v([^ :]+): (.+)$')
+
+ if !empty(l:code_match)
+ let l:obj.code = l:code_match[1]
+ let l:obj.text = l:code_match[2]
+ endif
+
+ call add(l:output, l:obj)
+ endfor
+
+ return l:output
+endfunction
+
call ale#linter#Define('javascript', {
\ 'name': 'jscs',
-\ 'executable': 'jscs',
-\ 'command': 'jscs -r unix -n -',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'executable_callback': 'ale_linters#javascript#jscs#GetExecutable',
+\ 'command_callback': 'ale_linters#javascript#jscs#GetCommand',
+\ 'callback': 'ale_linters#javascript#jscs#Handle',
\})
+
diff --git a/ale_linters/javascript/jshint.vim b/ale_linters/javascript/jshint.vim
index f4b6651..93b16a8 100644
--- a/ale_linters/javascript/jshint.vim
+++ b/ale_linters/javascript/jshint.vim
@@ -1,37 +1,28 @@
" Author: Chris Kyrouac - https://github.com/fijshion
" Description: JSHint for Javascript files
-let g:ale_javascript_jshint_executable =
-\ get(g:, 'ale_javascript_jshint_executable', 'jshint')
-
-let g:ale_javascript_jshint_use_global =
-\ get(g:, 'ale_javascript_jshint_use_global', 0)
+call ale#Set('javascript_jshint_executable', 'jshint')
+call ale#Set('javascript_jshint_use_global', 0)
function! ale_linters#javascript#jshint#GetExecutable(buffer) abort
- if g:ale_javascript_jshint_use_global
- return g:ale_javascript_jshint_executable
- endif
-
- return ale#util#ResolveLocalPath(
- \ a:buffer,
+ return ale#node#FindExecutable(a:buffer, 'javascript_jshint', [
\ 'node_modules/.bin/jshint',
- \ g:ale_javascript_jshint_executable
- \)
+ \])
endfunction
function! ale_linters#javascript#jshint#GetCommand(buffer) abort
" Search for a local JShint config locaation, and default to a global one.
- let l:jshint_config = ale#util#ResolveLocalPath(
+ let l:jshint_config = ale#path#ResolveLocalPath(
\ a:buffer,
\ '.jshintrc',
\ get(g:, 'ale_jshint_config_loc', '')
\)
- let l:command = ale_linters#javascript#jshint#GetExecutable(a:buffer)
- let l:command .= ' --reporter unix'
+ let l:command = ale#Escape(ale_linters#javascript#jshint#GetExecutable(a:buffer))
+ let l:command .= ' --reporter unix --extract auto'
if !empty(l:jshint_config)
- let l:command .= ' --config ' . fnameescape(l:jshint_config)
+ let l:command .= ' --config ' . ale#Escape(l:jshint_config)
endif
let l:command .= ' -'
@@ -43,5 +34,5 @@ call ale#linter#Define('javascript', {
\ 'name': 'jshint',
\ 'executable_callback': 'ale_linters#javascript#jshint#GetExecutable',
\ 'command_callback': 'ale_linters#javascript#jshint#GetCommand',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/javascript/standard.vim b/ale_linters/javascript/standard.vim
index 1232b65..aa6a3a7 100644
--- a/ale_linters/javascript/standard.vim
+++ b/ale_linters/javascript/standard.vim
@@ -1,69 +1,30 @@
" 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)
+call ale#Set('javascript_standard_executable', 'standard')
+call ale#Set('javascript_standard_use_global', 0)
+call ale#Set('javascript_standard_options', '')
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,
+ return ale#node#FindExecutable(a:buffer, 'javascript_standard', [
+ \ 'node_modules/standard/bin/cmd.js',
\ '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
+ let l:executable = ale_linters#javascript#standard#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'javascript_standard_options')
+
+ return ale#node#Executable(a:buffer, l:executable)
+ \ . (!empty(l:options) ? ' ' . l: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]
-
- " vcol is Needed to indicate that the column is a character.
- 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
-
+" standard uses eslint and the output format is the same
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',
+\ 'callback': 'ale#handlers#eslint#Handle',
\})
-
diff --git a/ale_linters/javascript/xo.vim b/ale_linters/javascript/xo.vim
index e27f6f3..cf305eb 100644
--- a/ale_linters/javascript/xo.vim
+++ b/ale_linters/javascript/xo.vim
@@ -1,41 +1,26 @@
" Author: Daniel Lupu
" 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)
+call ale#Set('javascript_xo_executable', 'xo')
+call ale#Set('javascript_xo_use_global', 0)
+call ale#Set('javascript_xo_options', '')
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,
+ return ale#node#FindExecutable(a:buffer, 'javascript_xo', [
\ '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
+ return ale#Escape(ale_linters#javascript#xo#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, '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
-
+" xo uses eslint and the output format is the same
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',
+\ 'callback': 'ale#handlers#eslint#Handle',
\})
diff --git a/ale_linters/json/jsonlint.vim b/ale_linters/json/jsonlint.vim
index e1fb2bd..75f4708 100644
--- a/ale_linters/json/jsonlint.vim
+++ b/ale_linters/json/jsonlint.vim
@@ -7,20 +7,11 @@ function! ale_linters#json#jsonlint#Handle(buffer, lines) abort
let l:pattern = '^line \(\d\+\), col \(\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
-
- " vcol is needed to indicate that the column is a character
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 0,
\ 'text': l:match[3],
- \ 'type': 'E',
\})
endfor
diff --git a/ale_linters/kotlin/kotlinc.vim b/ale_linters/kotlin/kotlinc.vim
new file mode 100644
index 0000000..00f94be
--- /dev/null
+++ b/ale_linters/kotlin/kotlinc.vim
@@ -0,0 +1,170 @@
+" Author: Francis Agyapong
+" Description: A linter for the Kotlin programming language that uses kotlinc
+
+let g:ale_kotlin_kotlinc_options = get(g:, 'ale_kotlin_kotlinc_options', '')
+let g:ale_kotlin_kotlinc_enable_config = get(g:, 'ale_kotlin_kotlinc_enable_config', 0)
+let g:ale_kotlin_kotlinc_config_file = get(g:, 'ale_kotlin_kotlinc_config_file', '.ale_kotlinc_config')
+let g:ale_kotlin_kotlinc_classpath = get(g:, 'ale_kotlin_kotlinc_classpath', '')
+let g:ale_kotlin_kotlinc_sourcepath = get(g:, 'ale_kotlin_kotlinc_sourcepath', '')
+let g:ale_kotlin_kotlinc_use_module_file = get(g:, 'ale_kotlin_kotlinc_use_module_file', 0)
+let g:ale_kotlin_kotlinc_module_filename = get(g:, 'ale_kotlin_kotlinc_module_filename', 'module.xml')
+
+let s:classpath_sep = has('unix') ? ':' : ';'
+
+function! ale_linters#kotlin#kotlinc#GetImportPaths(buffer) abort
+ " exec maven/gradle only if classpath is not set
+ if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') isnot# ''
+ return ''
+ else
+ let l:pom_path = ale#path#FindNearestFile(a:buffer, 'pom.xml')
+ if !empty(l:pom_path) && executable('mvn')
+ return ale#path#CdString(fnamemodify(l:pom_path, ':h'))
+ \ . 'mvn dependency:build-classpath'
+ endif
+
+ let l:classpath_command = ale#gradle#BuildClasspathCommand(a:buffer)
+ if !empty(l:classpath_command)
+ return l:classpath_command
+ endif
+
+ return ''
+ endif
+endfunction
+
+function! s:BuildClassPathOption(buffer, import_paths) abort
+ " Filter out lines like [INFO], etc.
+ let l:class_paths = filter(a:import_paths[:], 'v:val !~# ''[''')
+ call extend(
+ \ l:class_paths,
+ \ split(ale#Var(a:buffer, 'kotlin_kotlinc_classpath'), s:classpath_sep),
+ \)
+
+ return !empty(l:class_paths)
+ \ ? ' -cp ' . ale#Escape(join(l:class_paths, s:classpath_sep))
+ \ : ''
+endfunction
+
+function! ale_linters#kotlin#kotlinc#GetCommand(buffer, import_paths) abort
+ let l:kotlinc_opts = ale#Var(a:buffer, 'kotlin_kotlinc_options')
+ let l:command = 'kotlinc '
+
+ " If the config file is enabled and readable, source it
+ if ale#Var(a:buffer, 'kotlin_kotlinc_enable_config')
+ let l:conf = expand(ale#Var(a:buffer, 'kotlin_kotlinc_config_file'), 1)
+
+ if filereadable(l:conf)
+ execute 'source ' . fnameescape(l:conf)
+ endif
+ endif
+
+ " If use module and module file is readable use that and return
+ if ale#Var(a:buffer, 'kotlin_kotlinc_use_module_file')
+ let l:module_filename = ale#Escape(expand(ale#Var(a:buffer, 'kotlin_kotlinc_module_filename'), 1))
+
+ if filereadable(l:module_filename)
+ let l:kotlinc_opts .= ' -module ' . l:module_filename
+ let l:command .= 'kotlinc ' . l:kotlinc_opts
+
+ return l:command
+ endif
+ endif
+
+ " We only get here if not using module or the module file not readable
+ if ale#Var(a:buffer, 'kotlin_kotlinc_classpath') isnot# ''
+ let l:kotlinc_opts .= ' -cp ' . ale#Var(a:buffer, 'kotlin_kotlinc_classpath')
+ else
+ " get classpath from maven/gradle
+ let l:kotlinc_opts .= s:BuildClassPathOption(a:buffer, a:import_paths)
+ endif
+
+ let l:fname = ''
+ if ale#Var(a:buffer, 'kotlin_kotlinc_sourcepath') isnot# ''
+ let l:fname .= expand(ale#Var(a:buffer, 'kotlin_kotlinc_sourcepath'), 1) . ' '
+ else
+ " Find the src directory for files in this project.
+
+ let l:project_root = ale#gradle#FindProjectRoot(a:buffer)
+ if !empty(l:project_root)
+ let l:src_dir = l:project_root
+ else
+ let l:src_dir = ale#path#FindNearestDirectory(a:buffer, 'src/main/java')
+ \ . ' ' . ale#path#FindNearestDirectory(a:buffer, 'src/main/kotlin')
+ endif
+
+ let l:fname .= expand(l:src_dir, 1) . ' '
+ endif
+ let l:fname .= ale#Escape(expand('#' . a:buffer . ':p'))
+ let l:command .= l:kotlinc_opts . ' ' . l:fname
+
+ return l:command
+endfunction
+
+function! ale_linters#kotlin#kotlinc#Handle(buffer, lines) abort
+ let l:code_pattern = '^\(.*\):\([0-9]\+\):\([0-9]\+\):\s\+\(error\|warning\):\s\+\(.*\)'
+ let l:general_pattern = '^\(warning\|error\|info\):\s*\(.*\)'
+ let l:output = []
+
+ for l:line in a:lines
+ let l:match = matchlist(l:line, l:code_pattern)
+
+ if len(l:match) == 0
+ continue
+ endif
+
+ let l:file = l:match[1]
+ let l:line = l:match[2] + 0
+ let l:column = l:match[3] + 0
+ let l:type = l:match[4]
+ let l:text = l:match[5]
+
+ let l:buf_abspath = fnamemodify(l:file, ':p')
+ let l:curbuf_abspath = expand('#' . a:buffer . ':p')
+
+ " Skip if file is not loaded
+ if l:buf_abspath isnot# l:curbuf_abspath
+ continue
+ endif
+ let l:type_marker_str = l:type is# 'warning' ? 'W' : 'E'
+
+ call add(l:output, {
+ \ 'lnum': l:line,
+ \ 'col': l:column,
+ \ 'text': l:text,
+ \ 'type': l:type_marker_str,
+ \})
+ endfor
+
+ " Non-code related messages
+ for l:line in a:lines
+ let l:match = matchlist(l:line, l:general_pattern)
+
+ if len(l:match) == 0
+ continue
+ endif
+
+ let l:type = l:match[1]
+ let l:text = l:match[2]
+
+ let l:type_marker_str = l:type is# 'warning' || l:type is# 'info' ? 'W' : 'E'
+
+ call add(l:output, {
+ \ 'lnum': 1,
+ \ 'text': l:text,
+ \ 'type': l:type_marker_str,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('kotlin', {
+\ 'name': 'kotlinc',
+\ 'executable': 'kotlinc',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#kotlin#kotlinc#GetImportPaths', 'output_stream': 'stdout'},
+\ {'callback': 'ale_linters#kotlin#kotlinc#GetCommand', 'output_stream': 'stderr'},
+\ ],
+\ 'callback': 'ale_linters#kotlin#kotlinc#Handle',
+\ 'lint_file': 1,
+\})
+
diff --git a/ale_linters/kotlin/ktlint.vim b/ale_linters/kotlin/ktlint.vim
new file mode 100644
index 0000000..f474e84
--- /dev/null
+++ b/ale_linters/kotlin/ktlint.vim
@@ -0,0 +1,54 @@
+" Author: Francis Agyapong
+" Description: Lint kotlin files using ktlint
+
+call ale#Set('kotlin_ktlint_executable', 'ktlint')
+call ale#Set('kotlin_ktlint_rulesets', [])
+call ale#Set('kotlin_ktlint_format', 0)
+
+
+function! ale_linters#kotlin#ktlint#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'kotlin_ktlint_executable')
+ let l:file_path = expand('#' . a:buffer . ':p')
+ let l:options = ''
+
+ " Formmatted content written to original file, not sure how to handle
+ " if ale#Var(a:buffer, 'kotlin_ktlint_format')
+ " let l:options = l:options . ' --format'
+ " endif
+
+ for l:ruleset in ale#Var(a:buffer, 'kotlin_ktlint_rulesets')
+ let l:options = l:options . ' --ruleset ' . l:ruleset
+ endfor
+
+ return l:executable . ' ' . l:options . ' ' . l:file_path
+endfunction
+
+function! ale_linters#kotlin#ktlint#Handle(buffer, lines) abort
+ let l:message_pattern = '^\(.*\):\([0-9]\+\):\([0-9]\+\):\s\+\(.*\)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:message_pattern)
+ let l:line = l:match[2] + 0
+ let l:column = l:match[3] + 0
+ let l:text = l:match[4]
+
+ let l:type = l:text =~? 'not a valid kotlin file' ? 'E' : 'W'
+
+ call add(l:output, {
+ \ 'lnum': l:line,
+ \ 'col': l:column,
+ \ 'text': l:text,
+ \ 'type': l:type
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('kotlin', {
+\ 'name': 'ktlint',
+\ 'executable': 'ktlint',
+\ 'command_callback': 'ale_linters#kotlin#ktlint#GetCommand',
+\ 'callback': 'ale_linters#kotlin#ktlint#Handle',
+\ 'lint_file': 1
+\})
diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim
new file mode 100755
index 0000000..108679d
--- /dev/null
+++ b/ale_linters/less/lessc.vim
@@ -0,0 +1,56 @@
+" Author: zanona , w0rp
+" Description: This file adds support for checking Less code with lessc.
+
+call ale#Set('less_lessc_executable', 'lessc')
+call ale#Set('less_lessc_options', '')
+call ale#Set('less_lessc_use_global', 0)
+
+function! ale_linters#less#lessc#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'less_lessc', [
+ \ 'node_modules/.bin/lessc',
+ \])
+endfunction
+
+function! ale_linters#less#lessc#GetCommand(buffer) abort
+ let l:executable = ale_linters#less#lessc#GetExecutable(a:buffer)
+ let l:dir = expand('#' . a:buffer . ':p:h')
+ let l:options = ale#Var(a:buffer, 'less_lessc_options')
+
+ return ale#Escape(l:executable)
+ \ . ' --no-color --lint'
+ \ . ' --include-path=' . ale#Escape(l:dir)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' -'
+endfunction
+
+function! ale_linters#less#lessc#Handle(buffer, lines) abort
+ let l:dir = expand('#' . a:buffer . ':p:h')
+ " Matches patterns like the following:
+ let l:pattern = '^\(\w\+\): \(.\{-}\) in \(.\{-}\) on line \(\d\+\), column \(\d\+\):$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:item = {
+ \ 'lnum': l:match[4] + 0,
+ \ 'col': l:match[5] + 0,
+ \ 'text': l:match[2],
+ \ 'type': 'E',
+ \}
+
+ if l:match[3] isnot# '-'
+ let l:item.filename = ale#path#GetAbsPath(l:dir, l:match[3])
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('less', {
+\ 'name': 'lessc',
+\ 'executable_callback': 'ale_linters#less#lessc#GetExecutable',
+\ 'command_callback': 'ale_linters#less#lessc#GetCommand',
+\ 'callback': 'ale_linters#less#lessc#Handle',
+\ 'output_stream': 'stderr',
+\})
diff --git a/ale_linters/less/stylelint.vim b/ale_linters/less/stylelint.vim
new file mode 100644
index 0000000..690c8c9
--- /dev/null
+++ b/ale_linters/less/stylelint.vim
@@ -0,0 +1,27 @@
+" Author: diartyz , w0rp
+
+call ale#Set('less_stylelint_executable', 'stylelint')
+call ale#Set('less_stylelint_options', '')
+call ale#Set('less_stylelint_use_global', 0)
+
+function! ale_linters#less#stylelint#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'less_stylelint', [
+ \ 'node_modules/.bin/stylelint',
+ \])
+endfunction
+
+function! ale_linters#less#stylelint#GetCommand(buffer) abort
+ let l:executable = ale_linters#less#stylelint#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'less_stylelint_options')
+
+ return ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --stdin-filename %s'
+endfunction
+
+call ale#linter#Define('less', {
+\ 'name': 'stylelint',
+\ 'executable_callback': 'ale_linters#less#stylelint#GetExecutable',
+\ 'command_callback': 'ale_linters#less#stylelint#GetCommand',
+\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
+\})
diff --git a/ale_linters/llvm/llc.vim b/ale_linters/llvm/llc.vim
new file mode 100644
index 0000000..0a4903e
--- /dev/null
+++ b/ale_linters/llvm/llc.vim
@@ -0,0 +1,35 @@
+" Author: rhysd
+" Description: Support for checking LLVM IR with llc
+
+call ale#Set('llvm_llc_executable', 'llc')
+
+function! ale_linters#llvm#llc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'llvm_llc_executable')
+endfunction
+
+function! ale_linters#llvm#llc#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#llvm#llc#GetExecutable(a:buffer))
+ \ . ' -filetype=null -o='
+ \ . ale#Escape(g:ale#util#nul_file)
+endfunction
+
+function! ale_linters#llvm#llc#HandleErrors(buffer, lines) abort
+ " Handle '{path}: {file}:{line}:{col}: error: {message}' format
+ let l:pattern = '\v^[a-zA-Z]?:?[^:]+: [^:]+:(\d+):(\d+): (.+)$'
+ let l:matches = ale#util#GetMatches(a:lines, l:pattern)
+
+ return map(l:matches, "{
+ \ 'lnum': str2nr(v:val[1]),
+ \ 'col': str2nr(v:val[2]),
+ \ 'text': v:val[3],
+ \ 'type': 'E',
+ \}")
+endfunction
+
+call ale#linter#Define('llvm', {
+\ 'name': 'llc',
+\ 'executable_callback': 'ale_linters#llvm#llc#GetExecutable',
+\ 'output_stream': 'stderr',
+\ 'command_callback': 'ale_linters#llvm#llc#GetCommand',
+\ 'callback': 'ale_linters#llvm#llc#HandleErrors',
+\})
diff --git a/ale_linters/lua/luac.vim b/ale_linters/lua/luac.vim
new file mode 100644
index 0000000..4a6bb40
--- /dev/null
+++ b/ale_linters/lua/luac.vim
@@ -0,0 +1,40 @@
+" Author: Jon Xie https://github.com/xiejiangzhi
+" Description: luac linter for lua files
+
+call ale#Set('lua_luac_executable', 'luac')
+
+function! ale_linters#lua#luac#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'lua_luac_executable')
+endfunction
+
+function! ale_linters#lua#luac#GetCommand(buffer) abort
+ let l:executable = ale_linters#lua#luac#GetExecutable(a:buffer)
+ return ale#Escape(l:executable) . ' -p - '
+endfunction
+
+function! ale_linters#lua#luac#Handle(buffer, lines) abort
+ " Matches patterns line the following:
+ "
+ " luac: stdin:5: '=' expected near ')'
+ " luac: stdin:8: ')' expected (to close '(' at line 6) near '123'
+ let l:pattern = '\v^.*:(\d+): (.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'type': 'E',
+ \ 'text': l:match[2],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('lua', {
+\ 'name': 'luac',
+\ 'executable_callback': 'ale_linters#lua#luac#GetExecutable',
+\ 'command_callback': 'ale_linters#lua#luac#GetCommand',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale_linters#lua#luac#Handle',
+\})
diff --git a/ale_linters/lua/luacheck.vim b/ale_linters/lua/luacheck.vim
index c4c79b1..725153c 100644
--- a/ale_linters/lua/luacheck.vim
+++ b/ale_linters/lua/luacheck.vim
@@ -4,28 +4,40 @@
let g:ale_lua_luacheck_executable =
\ get(g:, 'ale_lua_luacheck_executable', 'luacheck')
+let g:ale_lua_luacheck_options =
+\ get(g:, 'ale_lua_luacheck_options', '')
+
+function! ale_linters#lua#luacheck#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'lua_luacheck_executable')
+endfunction
+
+function! ale_linters#lua#luacheck#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#lua#luacheck#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'lua_luacheck_options')
+ \ . ' --formatter plain --codes --filename %s -'
+endfunction
+
function! ale_linters#lua#luacheck#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" artal.lua:159:17: (W111) shadowing definition of loop variable 'i' on line 106
" artal.lua:182:7: (W213) unused loop variable 'i'
- let l:pattern = '^.*:\(\d\+\):\(\d\+\): (\([WE]\)\d\+) \(.\+\)$'
+ let l:pattern = '^.*:\(\d\+\):\(\d\+\): (\([WE]\)\(\d\+\)) \(.\+\)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ if !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ \ && l:match[3] is# 'W'
+ \ && index(range(611, 614), str2nr(l:match[4])) >= 0
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,
\ 'col': l:match[2] + 0,
- \ 'text': l:match[4],
\ 'type': l:match[3],
+ \ 'code': l:match[3] . l:match[4],
+ \ 'text': l:match[5],
\})
endfor
@@ -34,7 +46,7 @@ endfunction
call ale#linter#Define('lua', {
\ 'name': 'luacheck',
-\ 'executable': g:ale_lua_luacheck_executable,
-\ 'command': g:ale_lua_luacheck_executable . ' --formatter plain --codes --filename %s -',
+\ 'executable_callback': 'ale_linters#lua#luacheck#GetExecutable',
+\ 'command_callback': 'ale_linters#lua#luacheck#GetCommand',
\ 'callback': 'ale_linters#lua#luacheck#Handle',
\})
diff --git a/ale_linters/mail/alex.vim b/ale_linters/mail/alex.vim
new file mode 100644
index 0000000..b0651cc
--- /dev/null
+++ b/ale_linters/mail/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for HTML files
+
+call ale#linter#Define('mail', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/mail/proselint.vim b/ale_linters/mail/proselint.vim
new file mode 100644
index 0000000..82c8d1f
--- /dev/null
+++ b/ale_linters/mail/proselint.vim
@@ -0,0 +1,9 @@
+" Author: Daniel M. Capella https://github.com/polyzen
+" Description: proselint for mail files
+
+call ale#linter#Define('mail', {
+\ 'name': 'proselint',
+\ 'executable': 'proselint',
+\ 'command': 'proselint %t',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\})
diff --git a/ale_linters/mail/vale.vim b/ale_linters/mail/vale.vim
new file mode 100644
index 0000000..e6dfd2e
--- /dev/null
+++ b/ale_linters/mail/vale.vim
@@ -0,0 +1,9 @@
+" Author: chew-z https://github.com/chew-z
+" Description: vale for Markdown files
+
+call ale#linter#Define('mail', {
+\ 'name': 'vale',
+\ 'executable': 'vale',
+\ 'command': 'vale --output=JSON %t',
+\ 'callback': 'ale#handlers#vale#Handle',
+\})
diff --git a/ale_linters/make/checkmake.vim b/ale_linters/make/checkmake.vim
new file mode 100644
index 0000000..63c35db
--- /dev/null
+++ b/ale_linters/make/checkmake.vim
@@ -0,0 +1,24 @@
+" Author: aurieh - https://github.com/aurieh
+
+function! ale_linters#make#checkmake#Handle(buffer, lines) abort
+ let l:pattern = '\v^(\d+):(.+):(.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:match[1] + 0,
+ \ 'type': 'E',
+ \ 'code': l:match[2],
+ \ 'text': l:match[3],
+ \})
+ endfor
+ return l:output
+endfunction
+
+call ale#linter#Define('make', {
+\ 'name': 'checkmake',
+\ 'executable': 'checkmake',
+\ 'command': 'checkmake %s --format="{{.LineNumber}}:{{.Rule}}:{{.Violation}}"',
+\ 'callback': 'ale_linters#make#checkmake#Handle',
+\})
diff --git a/ale_linters/markdown/alex.vim b/ale_linters/markdown/alex.vim
new file mode 100644
index 0000000..2930614
--- /dev/null
+++ b/ale_linters/markdown/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for markdown files
+
+call ale#linter#Define('markdown', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/markdown/mdl.vim b/ale_linters/markdown/mdl.vim
index 7e64304..16b08cc 100644
--- a/ale_linters/markdown/mdl.vim
+++ b/ale_linters/markdown/mdl.vim
@@ -1,22 +1,29 @@
-" Author: Steve Dignam
-" Description: Support for mdl, a markdown linter
+" Author: Steve Dignam , Josh Leeb-du Toit
+" Description: Support for mdl, a markdown linter.
+
+call ale#Set('markdown_mdl_executable', 'mdl')
+call ale#Set('markdown_mdl_options', '')
+
+function! ale_linters#markdown#mdl#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'markdown_mdl_executable')
+endfunction
+
+function! ale_linters#markdown#mdl#GetCommand(buffer) abort
+ let l:executable = ale_linters#markdown#mdl#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'markdown_mdl_options')
+
+ return ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+endfunction
function! ale_linters#markdown#mdl#Handle(buffer, lines) abort
" matches: '(stdin):173: MD004 Unordered list style'
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
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
- \ 'col': 0,
\ 'text': l:match[2],
\ 'type': 'W',
\})
@@ -27,7 +34,7 @@ endfunction
call ale#linter#Define('markdown', {
\ 'name': 'mdl',
-\ 'executable': 'mdl',
-\ 'command': 'mdl',
+\ 'executable_callback': 'ale_linters#markdown#mdl#GetExecutable',
+\ 'command_callback': 'ale_linters#markdown#mdl#GetCommand',
\ 'callback': 'ale_linters#markdown#mdl#Handle'
\})
diff --git a/ale_linters/markdown/proselint.vim b/ale_linters/markdown/proselint.vim
index 295b1d0..289d881 100644
--- a/ale_linters/markdown/proselint.vim
+++ b/ale_linters/markdown/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('markdown', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/markdown/redpen.vim b/ale_linters/markdown/redpen.vim
new file mode 100644
index 0000000..ff2cbaf
--- /dev/null
+++ b/ale_linters/markdown/redpen.vim
@@ -0,0 +1,9 @@
+" Author: rhysd https://rhysd.github.io
+" Description: Redpen, a proofreading tool (http://redpen.cc)
+
+call ale#linter#Define('markdown', {
+\ 'name': 'redpen',
+\ 'executable': 'redpen',
+\ 'command': 'redpen -f markdown -r json %t',
+\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput',
+\})
diff --git a/ale_linters/markdown/remark_lint.vim b/ale_linters/markdown/remark_lint.vim
new file mode 100644
index 0000000..5b3b3d4
--- /dev/null
+++ b/ale_linters/markdown/remark_lint.vim
@@ -0,0 +1,28 @@
+" Author rhysd https://rhysd.github.io/
+" Description: remark-lint for Markdown files
+
+function! ale_linters#markdown#remark_lint#Handle(buffer, lines) abort
+ " matches: ' 1:4 warning Incorrect list-item indent: add 1 space list-item-indent remark-lint'
+ let l:pattern = '^ \+\(\d\+\):\(\d\+\) \(warning\|error\) \(.\+\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'type': l:match[3] is# 'error' ? 'E' : 'W',
+ \ 'text': l:match[4],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('markdown', {
+\ 'name': 'remark-lint',
+\ 'executable': 'remark',
+\ 'command': 'remark --no-stdout --no-color %s',
+\ 'callback': 'ale_linters#markdown#remark_lint#Handle',
+\ 'lint_file': 1,
+\ 'output_stream': 'stderr',
+\})
diff --git a/ale_linters/markdown/vale.vim b/ale_linters/markdown/vale.vim
new file mode 100644
index 0000000..838c4db
--- /dev/null
+++ b/ale_linters/markdown/vale.vim
@@ -0,0 +1,9 @@
+" Author: chew-z https://github.com/chew-z
+" Description: vale for Markdown files
+
+call ale#linter#Define('markdown', {
+\ 'name': 'vale',
+\ 'executable': 'vale',
+\ 'command': 'vale --output=JSON %t',
+\ 'callback': 'ale#handlers#vale#Handle',
+\})
diff --git a/ale_linters/markdown/write-good.vim b/ale_linters/markdown/write-good.vim
new file mode 100644
index 0000000..21dbff1
--- /dev/null
+++ b/ale_linters/markdown/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for Markdown files
+
+call ale#linter#Define('markdown', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/matlab/mlint.vim b/ale_linters/matlab/mlint.vim
index 563cf19..3276633 100644
--- a/ale_linters/matlab/mlint.vim
+++ b/ale_linters/matlab/mlint.vim
@@ -5,7 +5,7 @@ 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
+ return ale#Var(a:buffer, 'matlab_mlint_executable')
endfunction
function! ale_linters#matlab#mlint#GetCommand(buffer) abort
@@ -22,13 +22,7 @@ function! ale_linters#matlab#mlint#Handle(buffer, lines) abort
let l:pattern = '^L \(\d\+\) (C \([0-9-]\+\)): \([A-Z]\+\): \(.\+\)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
- endif
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:lnum = l:match[1] + 0
let l:col = l:match[2] + 0
let l:code = l:match[3]
@@ -36,11 +30,10 @@ function! ale_linters#matlab#mlint#Handle(buffer, lines) abort
" Suppress erroneous waring about filename
" TODO: Enable this error when copying filename is supported
- if l:code ==# 'FNDEF'
+ if l:code is# 'FNDEF'
continue
endif
- " vcol is needed to indicate that the column is a character.
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:lnum,
diff --git a/ale_linters/nim/nimcheck.vim b/ale_linters/nim/nimcheck.vim
index 0c1373e..bff45f7 100644
--- a/ale_linters/nim/nimcheck.vim
+++ b/ale_linters/nim/nimcheck.vim
@@ -1,58 +1,57 @@
" 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
+ let l:output = []
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
" 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
+
+ if l:buffer_filename isnot# '' && l:temp_buffer_filename isnot# 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'
+ let l:item = {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4],
+ \ 'type': 'W',
+ \}
" Extract error type from message of type 'Error: Some error message'
- let l:textmatch = matchlist(l:match[4], '^\(.\{-}\): .\+$')
+ let l:error_match = matchlist(l:item.text, '^\(.\{-}\): \(.\+\)$')
- if len(l:textmatch) > 0
- let l:errortype = l:textmatch[1]
- if l:errortype ==# 'Error'
- let l:type = 'E'
+ if !empty(l:error_match)
+ if l:error_match[1] is# 'Error'
+ let l:item.type = 'E'
+ let l:item.text = l:error_match[2]
+ elseif l:error_match[1] is# 'Warning'
+ \|| l:error_match[1] is# 'Hint'
+ let l:item.text = l:error_match[2]
endif
endif
- call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type,
- \})
+ let l:code_match = matchlist(l:item.text, '\v^(.+) \[([^ \[]+)\]$')
+
+ if !empty(l:code_match)
+ let l:item.text = l:code_match[1]
+ let l:item.code = l:code_match[2]
+ endif
+
+ call add(l:output, l:item)
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'
+function! ale_linters#nim#nimcheck#GetCommand(buffer) abort
+ return 'nim check --verbosity:0 --colors:off --listFullPaths %s'
endfunction
@@ -61,5 +60,6 @@ call ale#linter#Define('nim', {
\ 'executable': 'nim',
\ 'output_stream': 'both',
\ 'command_callback': 'ale_linters#nim#nimcheck#GetCommand',
-\ 'callback': 'ale_linters#nim#nimcheck#Handle'
+\ 'callback': 'ale_linters#nim#nimcheck#Handle',
+\ 'lint_file': 1,
\})
diff --git a/ale_linters/nix/nix.vim b/ale_linters/nix/nix.vim
index 27c1d51..0a0c5c3 100644
--- a/ale_linters/nix/nix.vim
+++ b/ale_linters/nix/nix.vim
@@ -2,19 +2,11 @@
" 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
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[3] + 0,
\ 'col': l:match[4] + 0,
\ 'text': l:match[1] . ': ' . l:match[2],
diff --git a/ale_linters/nroff/alex.vim b/ale_linters/nroff/alex.vim
new file mode 100644
index 0000000..a10db2d
--- /dev/null
+++ b/ale_linters/nroff/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for nroff files
+
+call ale#linter#Define('nroff', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/nroff/proselint.vim b/ale_linters/nroff/proselint.vim
index ce5ff1e..a23e56b 100644
--- a/ale_linters/nroff/proselint.vim
+++ b/ale_linters/nroff/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('nroff', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/nroff/write-good.vim b/ale_linters/nroff/write-good.vim
new file mode 100644
index 0000000..d318fb2
--- /dev/null
+++ b/ale_linters/nroff/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for nroff files
+
+call ale#linter#Define('nroff', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/objc/clang.vim b/ale_linters/objc/clang.vim
new file mode 100644
index 0000000..f4725a0
--- /dev/null
+++ b/ale_linters/objc/clang.vim
@@ -0,0 +1,23 @@
+" Author: Bang Lee
+" Description: clang linter for objc files
+
+" Set this option to change the Clang options for warnings for ObjC.
+if !exists('g:ale_objc_clang_options')
+ let g:ale_objc_clang_options = '-std=c11 -Wall'
+endif
+
+function! ale_linters#objc#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 objective-c -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
+ \ . ' ' . ale#Var(a:buffer, 'objc_clang_options') . ' -'
+endfunction
+
+call ale#linter#Define('objc', {
+\ 'name': 'clang',
+\ 'output_stream': 'stderr',
+\ 'executable': 'clang',
+\ 'command_callback': 'ale_linters#objc#clang#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\})
diff --git a/ale_linters/objcpp/clang.vim b/ale_linters/objcpp/clang.vim
new file mode 100644
index 0000000..0e9cefe
--- /dev/null
+++ b/ale_linters/objcpp/clang.vim
@@ -0,0 +1,23 @@
+" Author: Bang Lee
+" Description: clang linter for objcpp files
+
+" Set this option to change the Clang options for warnings for ObjCPP.
+if !exists('g:ale_objcpp_clang_options')
+ let g:ale_objcpp_clang_options = '-std=c++14 -Wall'
+endif
+
+function! ale_linters#objcpp#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 objective-c++ -fsyntax-only '
+ \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
+ \ . ' ' . ale#Var(a:buffer, 'objcpp_clang_options') . ' -'
+endfunction
+
+call ale#linter#Define('objcpp', {
+\ 'name': 'clang',
+\ 'output_stream': 'stderr',
+\ 'executable': 'clang++',
+\ 'command_callback': 'ale_linters#objcpp#clang#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\})
diff --git a/ale_linters/ocaml/merlin.vim b/ale_linters/ocaml/merlin.vim
index 75cdb12..cfec996 100644
--- a/ale_linters/ocaml/merlin.vim
+++ b/ale_linters/ocaml/merlin.vim
@@ -2,12 +2,11 @@
" Description: Report errors in OCaml code with Merlin
if !exists('g:merlin')
- finish
+ finish
endif
function! ale_linters#ocaml#merlin#Handle(buffer, lines) abort
- let l:errors = merlin#ErrorLocList()
- return l:errors
+ return merlin#ErrorLocList()
endfunction
call ale#linter#Define('ocaml', {
@@ -16,4 +15,3 @@ call ale#linter#Define('ocaml', {
\ 'command': 'true',
\ 'callback': 'ale_linters#ocaml#merlin#Handle',
\})
-
diff --git a/ale_linters/ocaml/ols.vim b/ale_linters/ocaml/ols.vim
new file mode 100644
index 0000000..c0255a6
--- /dev/null
+++ b/ale_linters/ocaml/ols.vim
@@ -0,0 +1,14 @@
+" Author: Michael Jungo
+" Description: A language server for OCaml
+
+call ale#Set('ocaml_ols_executable', 'ocaml-language-server')
+call ale#Set('ocaml_ols_use_global', 0)
+
+call ale#linter#Define('ocaml', {
+\ 'name': 'ols',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale#handlers#ols#GetExecutable',
+\ 'command_callback': 'ale#handlers#ols#GetCommand',
+\ 'language_callback': 'ale#handlers#ols#GetLanguage',
+\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot',
+\})
diff --git a/ale_linters/perl/perl.vim b/ale_linters/perl/perl.vim
index 9d24ed9..1b9aa95 100644
--- a/ale_linters/perl/perl.vim
+++ b/ale_linters/perl/perl.vim
@@ -5,42 +5,55 @@ 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')
+\ get(g:, 'ale_perl_perl_options', '-c -Mwarnings -Ilib')
function! ale_linters#perl#perl#GetExecutable(buffer) abort
- return g:ale_perl_perl_executable
+ return ale#Var(a:buffer, '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
+ return ale#Escape(ale_linters#perl#perl#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'perl_perl_options')
\ . ' %t'
endfunction
+let s:begin_failed_skip_pattern = '\v' . join([
+\ '^Compilation failed in require',
+\ '^Can''t locate',
+\], '|')
+
function! ale_linters#perl#perl#Handle(buffer, lines) abort
let l:pattern = '\(.\+\) at \(.\+\) line \(\d\+\)'
let l:output = []
+ let l:basename = expand('#' . a:buffer . ':t')
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
+ let l:type = 'E'
+ if a:lines[-1] =~# 'syntax OK'
+ let l:type = 'W'
+ endif
- if len(l:match) == 0
- continue
- endif
+ let l:seen = {}
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[3]
- let l:column = 1
+ let l:file = l:match[2]
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,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type,
- \})
+ if ale#path#IsBufferPath(a:buffer, l:file)
+ \ && !has_key(l:seen,l:line)
+ \ && (
+ \ l:text isnot# 'BEGIN failed--compilation aborted'
+ \ || empty(l:output)
+ \ || match(l:output[-1].text, s:begin_failed_skip_pattern) < 0
+ \ )
+ call add(l:output, {
+ \ 'lnum': l:line,
+ \ 'text': l:text,
+ \ 'type': l:type,
+ \})
+
+ let l:seen[l:line] = 1
+ endif
endfor
return l:output
diff --git a/ale_linters/perl/perlcritic.vim b/ale_linters/perl/perlcritic.vim
index f146085..24f7eb8 100644
--- a/ale_linters/perl/perlcritic.vim
+++ b/ale_linters/perl/perlcritic.vim
@@ -1,29 +1,67 @@
-" Author: Vincent Lequertier
+" Author: Vincent Lequertier , Chris Weyl
" Description: This file adds support for checking perl with perl critic
+let g:ale_perl_perlcritic_executable =
+\ get(g:, 'ale_perl_perlcritic_executable', 'perlcritic')
+
+let g:ale_perl_perlcritic_profile =
+\ get(g:, 'ale_perl_perlcritic_profile', '.perlcriticrc')
+
+let g:ale_perl_perlcritic_options =
+\ get(g:, 'ale_perl_perlcritic_options', '')
+
+let g:ale_perl_perlcritic_showrules =
+\ get(g:, 'ale_perl_perlcritic_showrules', 0)
+
+function! ale_linters#perl#perlcritic#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'perl_perlcritic_executable')
+endfunction
+
+function! ale_linters#perl#perlcritic#GetProfile(buffer) abort
+
+ " first see if we've been overridden
+ let l:profile = ale#Var(a:buffer, 'perl_perlcritic_profile')
+ if l:profile is? ''
+ return ''
+ endif
+
+ " otherwise, iterate upwards to find it
+ return ale#path#FindNearestFile(a:buffer, l:profile)
+endfunction
+
+function! ale_linters#perl#perlcritic#GetCommand(buffer) abort
+ let l:critic_verbosity = '%l:%c %m\n'
+ if ale#Var(a:buffer, 'perl_perlcritic_showrules')
+ let l:critic_verbosity = '%l:%c %m [%p]\n'
+ endif
+
+ let l:profile = ale_linters#perl#perlcritic#GetProfile(a:buffer)
+ let l:options = ale#Var(a:buffer, 'perl_perlcritic_options')
+
+ let l:command = ale#Escape(ale_linters#perl#perlcritic#GetExecutable(a:buffer))
+ \ . " --verbose '". l:critic_verbosity . "' --nocolor"
+
+ if l:profile isnot? ''
+ let l:command .= ' --profile ' . ale#Escape(l:profile)
+ endif
+ if l:options isnot? ''
+ let l:command .= ' ' . l:options
+ endif
+
+ return l:command
+endfunction
+
+
function! ale_linters#perl#perlcritic#Handle(buffer, lines) abort
- let l:pattern = '\(.\+\) at \(.\+\) line \(\d\+\)'
+ 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: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.
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type,
+ \ 'lnum': l:match[1],
+ \ 'col': l:match[2],
+ \ 'text': l:match[3],
+ \ 'type': 'W'
\})
endfor
@@ -32,8 +70,8 @@ endfunction
call ale#linter#Define('perl', {
\ 'name': 'perlcritic',
-\ 'executable': 'perlcritic',
\ 'output_stream': 'stdout',
-\ 'command': 'perlcritic --verbose 3 --nocolor',
+\ 'executable_callback': 'ale_linters#perl#perlcritic#GetExecutable',
+\ 'command_callback': 'ale_linters#perl#perlcritic#GetCommand',
\ 'callback': 'ale_linters#perl#perlcritic#Handle',
\})
diff --git a/ale_linters/php/hack.vim b/ale_linters/php/hack.vim
index 762486b..77d3a58 100644
--- a/ale_linters/php/hack.vim
+++ b/ale_linters/php/hack.vim
@@ -5,23 +5,15 @@ function! ale_linters#php#hack#Handle(buffer, lines) abort
let l:pattern = '^\(.*\):\(\d\+\):\(\d\+\),\(\d\+\): \(.\+])\)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ if a:buffer != bufnr(l:match[1])
continue
endif
- if a:buffer != bufnr(l:match[1])
- continue
- endif
-
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[5],
- \ 'type': 'E',
\})
endfor
diff --git a/ale_linters/php/langserver.vim b/ale_linters/php/langserver.vim
new file mode 100644
index 0000000..be2d6ef
--- /dev/null
+++ b/ale_linters/php/langserver.vim
@@ -0,0 +1,34 @@
+" Author: Eric Stern
+" Description: PHP Language server integration for ALE
+
+call ale#Set('php_langserver_executable', 'php-language-server.php')
+call ale#Set('php_langserver_use_global', 0)
+
+function! ale_linters#php#langserver#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'php_langserver', [
+ \ 'vendor/bin/php-language-server.php',
+ \])
+endfunction
+
+function! ale_linters#php#langserver#GetCommand(buffer) abort
+ return 'php ' . ale#Escape(ale_linters#php#langserver#GetExecutable(a:buffer))
+endfunction
+
+function! ale_linters#php#langserver#GetLanguage(buffer) abort
+ return 'php'
+endfunction
+
+function! ale_linters#php#langserver#GetProjectRoot(buffer) abort
+ let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git')
+
+ return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : ''
+endfunction
+
+call ale#linter#Define('php', {
+\ 'name': 'langserver',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale_linters#php#langserver#GetExecutable',
+\ 'command_callback': 'ale_linters#php#langserver#GetCommand',
+\ 'language_callback': 'ale_linters#php#langserver#GetLanguage',
+\ 'project_root_callback': 'ale_linters#php#langserver#GetProjectRoot',
+\})
diff --git a/ale_linters/php/phan.vim b/ale_linters/php/phan.vim
new file mode 100644
index 0000000..f3b3d48
--- /dev/null
+++ b/ale_linters/php/phan.vim
@@ -0,0 +1,36 @@
+" Author: diegoholiveira
+" Description: static analyzer for PHP
+
+" Define the minimum severity
+let g:ale_php_phan_minimum_severity = get(g:, 'ale_php_phan_minimum_severity', 0)
+
+function! ale_linters#php#phan#GetCommand(buffer) abort
+ return 'phan -y '
+ \ . ale#Var(a:buffer, 'php_phan_minimum_severity')
+ \ . ' %s'
+endfunction
+
+function! ale_linters#php#phan#Handle(buffer, lines) abort
+ " Matches against lines like the following:
+ "
+ " /path/to/some-filename.php:18 ERRORTYPE message
+ let l:pattern = '^.*:\(\d\+\)\s\(\w\+\)\s\(.\+\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'text': l:match[3],
+ \ 'type': 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('php', {
+\ 'name': 'phan',
+\ 'executable': 'phan',
+\ 'command_callback': 'ale_linters#php#phan#GetCommand',
+\ 'callback': 'ale_linters#php#phan#Handle',
+\})
diff --git a/ale_linters/php/php.vim b/ale_linters/php/php.vim
index 6edc66b..6470383 100644
--- a/ale_linters/php/php.vim
+++ b/ale_linters/php/php.vim
@@ -1,29 +1,28 @@
-" Author: Spencer Wood
+" Author: Spencer Wood , Adriaan Zonnenberg
" 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 = '\vParse error:\s+(.+unexpected ''(.+)%(expecting.+)@= - Parse error: syntax error, unexpected ';', expecting ']' in Standard input code on line 15
+ let l:pattern = '\v^%(Fatal|Parse) error:\s+(.+unexpected ''(.+)%(expecting.+)@
+" Author: jwilliams108 , Eric Stern
" Description: phpcs for PHP files
+let g:ale_php_phpcs_standard = get(g:, 'ale_php_phpcs_standard', '')
+
+call ale#Set('php_phpcs_executable', 'phpcs')
+call ale#Set('php_phpcs_use_global', 0)
+
+function! ale_linters#php#phpcs#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'php_phpcs', [
+ \ 'vendor/bin/phpcs',
+ \ 'phpcs'
+ \])
+endfunction
+
function! ale_linters#php#phpcs#GetCommand(buffer) abort
- let l:command = 'phpcs -s --report=emacs --stdin-path=%s'
+ let l:executable = ale_linters#php#phpcs#GetExecutable(a:buffer)
- " This option can be set to change the standard used by phpcs
- if exists('g:ale_php_phpcs_standard')
- let l:command .= ' --standard=' . g:ale_php_phpcs_standard
- endif
+ let l:standard = ale#Var(a:buffer, 'php_phpcs_standard')
+ let l:standard_option = !empty(l:standard)
+ \ ? '--standard=' . l:standard
+ \ : ''
- return l:command
+ return ale#Escape(l:executable)
+ \ . ' -s --report=emacs --stdin-path=%s ' . l:standard_option
endfunction
function! ale_linters#php#phpcs#Handle(buffer, lines) abort
" Matches against lines like the following:
"
" /path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)
- let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) - \(.\+\) \(\(.\+\)\)$'
+ 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:text = l:match[4]
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:code = l:match[5]
+ let l:text = l:match[4] . ' (' . l:code . ')'
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,
\ 'col': l:match[2] + 0,
\ 'text': l:text,
- \ 'type': l:type ==# 'error' ? 'E' : 'W',
+ \ 'type': l:type is# 'error' ? 'E' : 'W',
\})
endfor
@@ -44,7 +50,7 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phpcs',
-\ 'executable': 'phpcs',
+\ 'executable_callback': 'ale_linters#php#phpcs#GetExecutable',
\ 'command_callback': 'ale_linters#php#phpcs#GetCommand',
\ 'callback': 'ale_linters#php#phpcs#Handle',
\})
diff --git a/ale_linters/php/phpmd.vim b/ale_linters/php/phpmd.vim
index e172a6a..e945075 100644
--- a/ale_linters/php/phpmd.vim
+++ b/ale_linters/php/phpmd.vim
@@ -1,28 +1,34 @@
-" Author: medains
+" Author: medains , David Sierra
" Description: phpmd for PHP files
+let g:ale_php_phpmd_executable = get(g:, 'ale_php_phpmd_executable', 'phpmd')
+
" 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#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'php_phpmd_executable')
+endfunction
+
+function! ale_linters#php#phpmd#GetCommand(buffer) abort
+ let l:executable = ale_linters#php#phpmd#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable)
+ \ . ' %s text '
+ \ . ale#Var(a:buffer, 'php_phpmd_ruleset')
+ \ . ' --ignore-violations-on-exit %t'
+endfunction
+
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:pattern = '^.*:\(\d\+\)\s\+\(.\+\)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
- endif
-
- " vcol is Needed to indicate that the column is a character.
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[1] + 0,
- \ 'col': 0,
\ 'text': l:match[2],
\ 'type': 'W',
\})
@@ -33,7 +39,7 @@ endfunction
call ale#linter#Define('php', {
\ 'name': 'phpmd',
-\ 'executable': 'phpmd',
-\ 'command': 'phpmd %s text ' . g:ale_php_phpmd_ruleset . ' --ignore-violations-on-exit %t',
+\ 'executable_callback': 'ale_linters#php#phpmd#GetExecutable',
+\ 'command_callback': 'ale_linters#php#phpmd#GetCommand',
\ 'callback': 'ale_linters#php#phpmd#Handle',
\})
diff --git a/ale_linters/php/phpstan.vim b/ale_linters/php/phpstan.vim
new file mode 100644
index 0000000..2476208
--- /dev/null
+++ b/ale_linters/php/phpstan.vim
@@ -0,0 +1,53 @@
+" Author: medains , ardis
+" Description: phpstan for PHP files
+
+" Set to change the ruleset
+let g:ale_php_phpstan_executable = get(g:, 'ale_php_phpstan_executable', 'phpstan')
+let g:ale_php_phpstan_level = get(g:, 'ale_php_phpstan_level', '4')
+let g:ale_php_phpstan_configuration = get(g:, 'ale_php_phpstan_configuration', '')
+
+function! ale_linters#php#phpstan#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'php_phpstan_executable')
+endfunction
+
+function! ale_linters#php#phpstan#GetCommand(buffer) abort
+ let l:executable = ale_linters#php#phpstan#GetExecutable(a:buffer)
+
+ let l:configuration = ale#Var(a:buffer, 'php_phpstan_configuration')
+ let l:configuration_option = !empty(l:configuration)
+ \ ? ' -c ' . l:configuration
+ \ : ''
+
+ return ale#Escape(l:executable)
+ \ . ' analyze -l'
+ \ . ale#Var(a:buffer, 'php_phpstan_level')
+ \ . ' --errorFormat raw'
+ \ . l:configuration_option
+ \ . ' %s'
+endfunction
+
+function! ale_linters#php#phpstan#Handle(buffer, lines) abort
+ " Matches against lines like the following:
+ "
+ " filename.php:15:message
+ " C:\folder\filename.php:15:message
+ let l:pattern = '^\([a-zA-Z]:\)\?[^:]\+:\(\d\+\):\(.*\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[2] + 0,
+ \ 'text': l:match[3],
+ \ 'type': 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('php', {
+\ 'name': 'phpstan',
+\ 'executable_callback': 'ale_linters#php#phpstan#GetExecutable',
+\ 'command_callback': 'ale_linters#php#phpstan#GetCommand',
+\ 'callback': 'ale_linters#php#phpstan#Handle',
+\})
diff --git a/ale_linters/po/alex.vim b/ale_linters/po/alex.vim
new file mode 100644
index 0000000..411d835
--- /dev/null
+++ b/ale_linters/po/alex.vim
@@ -0,0 +1,11 @@
+" Author: Cian Butler https://github.com/butlerx
+" Description: alex for PO files
+
+call ale#linter#Define('po', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/po/msgfmt.vim b/ale_linters/po/msgfmt.vim
new file mode 100644
index 0000000..60c25d3
--- /dev/null
+++ b/ale_linters/po/msgfmt.vim
@@ -0,0 +1,10 @@
+" Author: Cian Butler https://github.com/butlerx
+" Description: msgfmt for PO files
+
+call ale#linter#Define('po', {
+\ 'name': 'msgfmt',
+\ 'executable': 'msgfmt',
+\ 'output_stream': 'stderr',
+\ 'command': 'msgfmt --statistics %t',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\})
diff --git a/ale_linters/po/proselint.vim b/ale_linters/po/proselint.vim
new file mode 100644
index 0000000..ce13250
--- /dev/null
+++ b/ale_linters/po/proselint.vim
@@ -0,0 +1,9 @@
+" Author: Cian Butler https://github.com/butlerx
+" Description: proselint for PO files
+
+call ale#linter#Define('po', {
+\ 'name': 'proselint',
+\ 'executable': 'proselint',
+\ 'command': 'proselint %t',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
+\})
diff --git a/ale_linters/po/write-good.vim b/ale_linters/po/write-good.vim
new file mode 100644
index 0000000..5a01cb6
--- /dev/null
+++ b/ale_linters/po/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Cian Butler https://github.com/butlerx
+" Description: write-good for PO files
+
+call ale#linter#Define('po', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/pod/alex.vim b/ale_linters/pod/alex.vim
new file mode 100644
index 0000000..5c09bef
--- /dev/null
+++ b/ale_linters/pod/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for pod files
+
+call ale#linter#Define('pod', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/pod/proselint.vim b/ale_linters/pod/proselint.vim
index aa7c940..2eb83f5 100644
--- a/ale_linters/pod/proselint.vim
+++ b/ale_linters/pod/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('pod', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/pod/write-good.vim b/ale_linters/pod/write-good.vim
new file mode 100644
index 0000000..14ed5c0
--- /dev/null
+++ b/ale_linters/pod/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for Pod files
+
+call ale#linter#Define('pod', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/pony/ponyc.vim b/ale_linters/pony/ponyc.vim
new file mode 100644
index 0000000..b332905
--- /dev/null
+++ b/ale_linters/pony/ponyc.vim
@@ -0,0 +1,21 @@
+" Description: ponyc linter for pony files
+
+call ale#Set('pony_ponyc_executable', 'ponyc')
+call ale#Set('pony_ponyc_options', '--pass paint')
+
+function! ale_linters#pony#ponyc#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'pony_ponyc_executable')
+endfunction
+
+function! ale_linters#pony#ponyc#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#pony#ponyc#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'pony_ponyc_options')
+endfunction
+
+call ale#linter#Define('pony', {
+\ 'name': 'ponyc',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#pony#ponyc#GetExecutable',
+\ 'command_callback': 'ale_linters#pony#ponyc#GetCommand',
+\ 'callback': 'ale#handlers#pony#HandlePonycFormat',
+\})
diff --git a/ale_linters/proto/protoc_gen_lint.vim b/ale_linters/proto/protoc_gen_lint.vim
new file mode 100644
index 0000000..c8b5c33
--- /dev/null
+++ b/ale_linters/proto/protoc_gen_lint.vim
@@ -0,0 +1,27 @@
+" Author: Jeff Willette
+" Description: run the protoc-gen-lint plugin for the protoc binary
+
+call ale#Set('proto_protoc_gen_lint_options', '')
+
+function! ale_linters#proto#protoc_gen_lint#GetCommand(buffer) abort
+ let l:dirname = expand('#' . a:buffer . ':p:h')
+
+ let l:options = ['-I ' . ale#Escape(l:dirname)]
+
+ if !empty(ale#Var(a:buffer, 'proto_protoc_gen_lint_options'))
+ let l:options += [ale#Var(a:buffer, 'proto_protoc_gen_lint_options')]
+ endif
+
+ let l:options += ['--lint_out=. ' . '%s']
+
+ return 'protoc' . ' ' . join(l:options)
+endfunction
+
+call ale#linter#Define('proto', {
+\ 'name': 'protoc-gen-lint',
+\ 'lint_file': 1,
+\ 'output_stream': 'stderr',
+\ 'executable': 'protoc',
+\ 'command_callback': 'ale_linters#proto#protoc_gen_lint#GetCommand',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
+\})
diff --git a/ale_linters/pug/puglint.vim b/ale_linters/pug/puglint.vim
index 4ded7ea..6c29efe 100644
--- a/ale_linters/pug/puglint.vim
+++ b/ale_linters/pug/puglint.vim
@@ -1,10 +1,48 @@
" Author: w0rp -
" Description: pug-lint for checking Pug/Jade files.
+call ale#Set('pug_puglint_options', '')
+call ale#Set('pug_puglint_executable', 'pug-lint')
+call ale#Set('pug_puglint_use_global', 0)
+
+function! ale_linters#pug#puglint#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'pug_puglint', [
+ \ 'node_modules/.bin/pug-lint',
+ \])
+endfunction
+
+function! s:FindConfig(buffer) abort
+ for l:filename in [
+ \ '.pug-lintrc',
+ \ '.pug-lintrc.js',
+ \ '.pug-lintrc.json',
+ \ 'package.json',
+ \]
+ let l:config = ale#path#FindNearestFile(a:buffer, l:filename)
+
+ if !empty(l:config)
+ return l:config
+ endif
+ endfor
+
+ return ''
+endfunction
+
+function! ale_linters#pug#puglint#GetCommand(buffer) abort
+ let l:executable = ale_linters#pug#puglint#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'pug_puglint_options')
+ let l:config = s:FindConfig(a:buffer)
+
+ return ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . (!empty(l:config) ? ' -c ' . ale#Escape(l:config) : '')
+ \ . ' -r inline %t'
+endfunction
+
call ale#linter#Define('pug', {
\ 'name': 'puglint',
-\ 'executable': 'pug-lint',
+\ 'executable_callback': 'ale_linters#pug#puglint#GetExecutable',
\ 'output_stream': 'stderr',
-\ 'command': 'pug-lint -r inline %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'command_callback': 'ale_linters#pug#puglint#GetCommand',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/puppet/puppet.vim b/ale_linters/puppet/puppet.vim
index 6561bf8..8beeb61 100644
--- a/ale_linters/puppet/puppet.vim
+++ b/ale_linters/puppet/puppet.vim
@@ -3,24 +3,16 @@
function! ale_linters#puppet#puppet#Handle(buffer, lines) abort
" Matches patterns like the following:
" Error: Could not parse for environment production: Syntax error at ':' at /root/puppetcode/modules/nginx/manifests/init.pp:43:12
+ " Error: Could not parse for environment production: Syntax error at '='; expected '}' at /root/puppetcode/modules/pancakes/manifests/init.pp:5"
- let l:pattern = '^Error: .*: \(.\+\) at .\+:\(\d\+\):\(\d\+\)$'
+ let l:pattern = '^Error: .*: \(.\+\) at .\+\.pp:\(\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
-
- " vcol is needed to indicate that the column is a character
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[1],
- \ 'type': 'E',
\})
endfor
diff --git a/ale_linters/puppet/puppetlint.vim b/ale_linters/puppet/puppetlint.vim
index 05745cf..13da511 100644
--- a/ale_linters/puppet/puppetlint.vim
+++ b/ale_linters/puppet/puppetlint.vim
@@ -1,10 +1,26 @@
-" Author: Alexander Olofsson
+" Author: Alexander Olofsson , Robert Flechtner
+" Description: puppet-lint for puppet files
+
+let g:ale_puppet_puppetlint_executable =
+\ get(g:, 'ale_puppet_puppetlint_executable', 'puppet-lint')
+
+let g:ale_puppet_puppetlint_options =
+\ get(g:, 'ale_puppet_puppetlint_options', '--no-autoloader_layout-check')
+
+function! ale_linters#puppet#puppetlint#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'puppet_puppetlint_executable')
+endfunction
+
+function! ale_linters#puppet#puppetlint#GetCommand(buffer) abort
+ return ale_linters#puppet#puppetlint#GetExecutable(a:buffer)
+ \ . ' ' . ale#Var(a:buffer, 'puppet_puppetlint_options')
+ \ . ' --log-format "-:%{line}:%{column}: %{kind}: [%{check}] %{message}"'
+ \ . ' %t'
+endfunction
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',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'executable_callback': 'ale_linters#puppet#puppetlint#GetExecutable',
+\ 'command_callback': 'ale_linters#puppet#puppetlint#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
\})
diff --git a/ale_linters/pyrex/cython.vim b/ale_linters/pyrex/cython.vim
index 9168e09..bd5a447 100644
--- a/ale_linters/pyrex/cython.vim
+++ b/ale_linters/pyrex/cython.vim
@@ -6,5 +6,5 @@ call ale#linter#Define('pyrex', {
\ 'output_stream': 'stderr',
\ 'executable': 'cython',
\ 'command': 'cython --warning-extra -o ' . g:ale#util#nul_file . ' %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsError',
+\ 'callback': 'ale#handlers#unix#HandleAsError',
\})
diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim
index bd136b2..e7bbcfb 100644
--- a/ale_linters/python/flake8.vim
+++ b/ale_linters/python/flake8.vim
@@ -4,15 +4,22 @@
let g:ale_python_flake8_executable =
\ get(g:, 'ale_python_flake8_executable', 'flake8')
-let g:ale_python_flake8_args =
-\ get(g:, 'ale_python_flake8_args', '')
+" Support an old setting as a fallback.
+let s:default_options = get(g:, 'ale_python_flake8_args', '')
+let g:ale_python_flake8_options =
+\ get(g:, 'ale_python_flake8_options', s:default_options)
+let g:ale_python_flake8_use_global = get(g:, 'ale_python_flake8_use_global', 0)
-" A map from Python executable paths to semver strings parsed for those
-" executables, so we don't have to look up the version number constantly.
-let s:version_cache = {}
+function! s:UsingModule(buffer) abort
+ return ale#Var(a:buffer, 'python_flake8_options') =~# ' *-m flake8'
+endfunction
function! ale_linters#python#flake8#GetExecutable(buffer) abort
- return g:ale_python_flake8_executable
+ if !s:UsingModule(a:buffer)
+ return ale#python#FindExecutable(a:buffer, 'python_flake8', ['flake8'])
+ endif
+
+ return ale#Var(a:buffer, 'python_flake8_executable')
endfunction
function! ale_linters#python#flake8#VersionCheck(buffer) abort
@@ -20,49 +27,112 @@ function! ale_linters#python#flake8#VersionCheck(buffer) abort
" If we have previously stored the version number in a cache, then
" don't look it up again.
- if has_key(s:version_cache, l:executable)
+ if ale#semver#HasVersion(l:executable)
" Returning an empty string skips this command.
return ''
endif
- return ale_linters#python#flake8#GetExecutable(a:buffer) . ' --version'
-endfunction
+ let l:executable = ale#Escape(l:executable)
+ let l:module_string = s:UsingModule(a:buffer) ? ' -m flake8' : ''
-" Get the flake8 version from the output, or the cache.
-function! s:GetVersion(buffer, version_output) abort
- let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer)
- let l:version = []
-
- " Get the version from the cache.
- if has_key(s:version_cache, l:executable)
- return s:version_cache[l:executable]
- endif
-
- if !empty(a:version_output)
- " Parse the version string, and store it in the cache.
- let l:version = ale#semver#Parse(a:version_output[0])
- let s:version_cache[l:executable] = l:version
- endif
-
- return l:version
-endfunction
-
-" flake8 versions 3 and up support the --stdin-display-name argument.
-function! s:SupportsDisplayName(version) abort
- return !empty(a:version) && ale#semver#GreaterOrEqual(a:version, [3, 0, 0])
+ return l:executable . l:module_string . ' --version'
endfunction
function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
- let l:version = s:GetVersion(a:buffer, a:version_output)
+ let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer)
+ let l:version = ale#semver#GetVersion(l:executable, a:version_output)
" Only include the --stdin-display-name argument if we can parse the
" flake8 version, and it is recent enough to support it.
- let l:display_name_args = s:SupportsDisplayName(l:version)
- \ ? '--stdin-display-name %s'
+ let l:display_name_args = ale#semver#GTE(l:version, [3, 0, 0])
+ \ ? ' --stdin-display-name %s'
\ : ''
- return ale_linters#python#flake8#GetExecutable(a:buffer)
- \ . ' ' . g:ale_python_flake8_args . ' ' . l:display_name_args . ' -'
+ let l:options = ale#Var(a:buffer, 'python_flake8_options')
+
+ return ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' --format=default'
+ \ . l:display_name_args . ' -'
+endfunction
+
+let s:end_col_pattern_map = {
+\ 'F405': '\(.\+\) may be undefined',
+\ 'F821': 'undefined name ''\([^'']\+\)''',
+\ 'F999': '^''\([^'']\+\)''',
+\ 'F841': 'local variable ''\([^'']\+\)''',
+\}
+
+function! ale_linters#python#flake8#Handle(buffer, lines) abort
+ for l:line in a:lines[:10]
+ if match(l:line, '^Traceback') >= 0
+ return [{
+ \ 'lnum': 1,
+ \ 'text': 'An exception was thrown. See :ALEDetail',
+ \ 'detail': join(a:lines, "\n"),
+ \}]
+ endif
+ endfor
+
+ " Matches patterns line the following:
+ "
+ " Matches patterns line the following:
+ "
+ " stdin:6:6: E111 indentation is not a multiple of four
+ let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):?(\d+)?: ([[:alnum:]]+) (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:code = l:match[3]
+
+ if (l:code is# 'W291' || l:code is# 'W293')
+ \ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ " Skip warnings for trailing whitespace if the option is off.
+ continue
+ endif
+
+ if l:code is# 'W391'
+ \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines')
+ " Skip warnings for trailing blank lines if the option is off
+ continue
+ endif
+
+ let l:item = {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[4],
+ \ 'code': l:code,
+ \ 'type': 'W',
+ \}
+
+ if l:code[:0] is# 'F'
+ if l:code isnot# 'F401'
+ let l:item.type = 'E'
+ endif
+ elseif l:code[:0] is# 'E'
+ let l:item.type = 'E'
+
+ if l:code isnot# 'E999' && l:code isnot# 'E112'
+ let l:item.sub_type = 'style'
+ endif
+ elseif l:code[:0] is# 'W'
+ let l:item.sub_type = 'style'
+ endif
+
+ let l:end_col_pattern = get(s:end_col_pattern_map, l:code, '')
+
+ if !empty(l:end_col_pattern)
+ let l:end_col_match = matchlist(l:match[4], l:end_col_pattern)
+
+ if !empty(l:end_col_match)
+ let l:item.end_col = l:item.col + len(l:end_col_match[1]) - 1
+ endif
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
endfunction
call ale#linter#Define('python', {
@@ -70,7 +140,7 @@ call ale#linter#Define('python', {
\ 'executable_callback': 'ale_linters#python#flake8#GetExecutable',
\ 'command_chain': [
\ {'callback': 'ale_linters#python#flake8#VersionCheck'},
-\ {'callback': 'ale_linters#python#flake8#GetCommand'},
+\ {'callback': 'ale_linters#python#flake8#GetCommand', 'output_stream': 'both'},
\ ],
-\ 'callback': 'ale#handlers#HandlePEP8Format',
+\ 'callback': 'ale_linters#python#flake8#Handle',
\})
diff --git a/ale_linters/python/mypy.vim b/ale_linters/python/mypy.vim
index 187bb83..c1c9174 100644
--- a/ale_linters/python/mypy.vim
+++ b/ale_linters/python/mypy.vim
@@ -1,23 +1,39 @@
" Author: Keith Smiley , w0rp
" Description: mypy support for optional python typechecking
-let g:ale_python_mypy_options = get(g:, 'ale_python_mypy_options', '')
+call ale#Set('python_mypy_executable', 'mypy')
+call ale#Set('python_mypy_ignore_invalid_syntax', 0)
+call ale#Set('python_mypy_options', '')
+call ale#Set('python_mypy_use_global', 0)
-function! g:ale_linters#python#mypy#GetCommand(buffer) abort
- let l:automatic_stubs_dir = ale#util#FindNearestDirectory(a:buffer, 'stubs')
- " TODO: Add Windows support
- let l:automatic_stubs_command = (has('unix') && !empty(l:automatic_stubs_dir))
- \ ? 'MYPYPATH=' . l:automatic_stubs_dir . ' '
- \ : ''
-
- return 'mypy --show-column-numbers '
- \ . g:ale_python_mypy_options
- \ . ' %t'
+function! ale_linters#python#mypy#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'python_mypy', ['mypy'])
endfunction
-let s:path_pattern = '[a-zA-Z]\?\\\?:\?[[:alnum:]/\.\-_]\+'
+" The directory to change to before running mypy
+function! s:GetDir(buffer) abort
+ let l:project_root = ale#python#FindProjectRoot(a:buffer)
-function! g:ale_linters#python#mypy#Handle(buffer, lines) abort
+ return !empty(l:project_root)
+ \ ? l:project_root
+ \ : expand('#' . a:buffer . ':p:h')
+endfunction
+
+function! ale_linters#python#mypy#GetCommand(buffer) abort
+ let l:dir = s:GetDir(a:buffer)
+ let l:executable = ale_linters#python#mypy#GetExecutable(a:buffer)
+
+ " We have to always switch to an explicit directory for a command so
+ " we can know with certainty the base path for the 'filename' keys below.
+ return ale#path#CdString(l:dir)
+ \ . ale#Escape(l:executable)
+ \ . ' --show-column-numbers '
+ \ . ale#Var(a:buffer, 'python_mypy_options')
+ \ . ' --shadow-file %s %t %s'
+endfunction
+
+function! ale_linters#python#mypy#Handle(buffer, lines) abort
+ let l:dir = s:GetDir(a:buffer)
" Look for lines like the following:
"
" file.py:4: error: No library stub file for module 'django.db'
@@ -25,37 +41,31 @@ function! g:ale_linters#python#mypy#Handle(buffer, lines) abort
" Lines like these should be ignored below:
"
" file.py:4: note: (Stub files are from https://github.com/python/typeshed)
- let l:pattern = '^' . s:path_pattern . ':\(\d\+\):\?\(\d\+\)\?: \([^:]\+\): \(.\+\)$'
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?: (error|warning): (.+)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
- endif
-
- if l:match[4] =~# 'Stub files are from'
- " The lines telling us where to get stub files from make it so
- " we can't read the actual errors, so exclude them.
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ " Skip invalid syntax errors if the option is on.
+ if l:match[5] is# 'invalid syntax'
+ \&& ale#Var(a:buffer, 'python_mypy_ignore_invalid_syntax')
continue
endif
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[1] + 0,
- \ 'col': l:match[2] + 0,
- \ 'text': l:match[4],
- \ 'type': l:match[3] =~# 'error' ? 'E' : 'W',
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
+ \ 'text': l:match[5],
\})
endfor
return l:output
endfunction
-call g:ale#linter#Define('python', {
+call ale#linter#Define('python', {
\ 'name': 'mypy',
-\ 'executable': 'mypy',
+\ 'executable_callback': 'ale_linters#python#mypy#GetExecutable',
\ 'command_callback': 'ale_linters#python#mypy#GetCommand',
\ 'callback': 'ale_linters#python#mypy#Handle',
\})
diff --git a/ale_linters/python/prospector.vim b/ale_linters/python/prospector.vim
new file mode 100644
index 0000000..66af598
--- /dev/null
+++ b/ale_linters/python/prospector.vim
@@ -0,0 +1,82 @@
+" Author: chocoelho
+" Description: prospector linter python files
+
+let g:ale_python_prospector_executable =
+\ get(g:, 'ale_python_prospector_executable', 'prospector')
+
+let g:ale_python_prospector_options =
+\ get(g:, 'ale_python_prospector_options', '')
+
+let g:ale_python_prospector_use_global = get(g:, 'ale_python_prospector_use_global', 0)
+
+function! ale_linters#python#prospector#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'python_prospector', ['prospector'])
+endfunction
+
+function! ale_linters#python#prospector#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#python#prospector#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'python_prospector_options')
+ \ . ' --messages-only --absolute-paths --zero-exit --output-format json'
+ \ . ' %s'
+endfunction
+
+function! ale_linters#python#prospector#Handle(buffer, lines) abort
+ let l:output = []
+
+ let l:prospector_error = json_decode(join(a:lines, ''))
+
+ for l:error in l:prospector_error.messages
+ if (l:error.code is# 'W291' || l:error.code is# 'W293' || l:error.code is# 'trailing-whitespace')
+ \ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ " Skip warnings for trailing whitespace if the option is off.
+ continue
+ endif
+
+ if l:error.code is# 'W391'
+ \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines')
+ " Skip warnings for trailing blank lines if the option is off
+ continue
+ endif
+
+ if l:error.source =~# '\v\[%(dodgy|mccabe|pep8|pep257|pyroma)\]$'
+ let l:sub_type = 'style'
+ else
+ let l:sub_type = ''
+ endif
+
+ if l:error.source =~# '\v\[pylint\]$'
+ let l:type = l:error.code =~? '\m^[CRW]' ? 'W' : 'E'
+ elseif l:error.source =~# '\v\[%(frosted|pep8)\]$'
+ let l:type = l:error.code =~? '\m^W' ? 'W' : 'E'
+ elseif l:error.source =~# '\v\[%(dodgy|pyroma|vulture)\]$'
+ let l:type = 'W'
+ else
+ let l:type = 'E'
+ endif
+
+ let l:item = {
+ \ 'lnum': l:error.location.line,
+ \ 'col': l:error.location.character + 1,
+ \ 'text': l:error.message,
+ \ 'code': printf('(%s) %s', l:error.source, l:error.code),
+ \ 'type': l:type,
+ \ 'sub_type': l:sub_type,
+ \}
+
+ if l:sub_type is# ''
+ unlet l:item.sub_type
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'prospector',
+\ 'executable_callback': 'ale_linters#python#prospector#GetExecutable',
+\ 'command_callback': 'ale_linters#python#prospector#GetCommand',
+\ 'callback': 'ale_linters#python#prospector#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim
new file mode 100644
index 0000000..19f05a5
--- /dev/null
+++ b/ale_linters/python/pycodestyle.vim
@@ -0,0 +1,63 @@
+" Author: Michael Thiesen
+" Description: pycodestyle linting for python files
+
+call ale#Set('python_pycodestyle_executable', 'pycodestyle')
+call ale#Set('python_pycodestyle_options', '')
+call ale#Set('python_pycodestyle_use_global', 0)
+
+function! ale_linters#python#pycodestyle#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'python_pycodestyle', ['pycodestyle'])
+endfunction
+
+function! ale_linters#python#pycodestyle#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#python#pycodestyle#GetExecutable(a:buffer))
+ \ . ' '
+ \ . ale#Var(a:buffer, 'python_pycodestyle_options')
+ \ . ' -'
+endfunction
+
+function! ale_linters#python#pycodestyle#Handle(buffer, lines) abort
+ let l:pattern = '\v^(\S*):(\d*):(\d*): ([EW]\d+) (.*)$'
+ let l:output = []
+
+ " lines are formatted as follows:
+ " file.py:21:26: W291 trailing whitespace
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ if(l:match[4] is# 'W291' || l:match[4] is# 'W293')
+ \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ " Skip warnings for trailing whitespace if the option is off.
+ continue
+ endif
+
+ if l:match[4] is# 'W391'
+ \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines')
+ " Skip warnings for trailing blank lines if the option is off
+ continue
+ endif
+
+ let l:item = {
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'type': l:match[4][0],
+ \ 'sub_type': 'style',
+ \ 'text': l:match[5],
+ \ 'code': l:match[4],
+ \}
+
+ " E999 and E112 are syntax errors.
+ if l:match[4] is# 'E999' || l:match[4] is# 'E112'
+ unlet l:item.sub_type
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'pycodestyle',
+\ 'executable_callback': 'ale_linters#python#pycodestyle#GetExecutable',
+\ 'command_callback': 'ale_linters#python#pycodestyle#GetCommand',
+\ 'callback': 'ale_linters#python#pycodestyle#Handle',
+\})
diff --git a/ale_linters/python/pyflakes.vim b/ale_linters/python/pyflakes.vim
new file mode 100644
index 0000000..b4a0b5f
--- /dev/null
+++ b/ale_linters/python/pyflakes.vim
@@ -0,0 +1,38 @@
+" Author: w0rp
+" Description: pyflakes for python files
+
+call ale#Set('python_pyflakes_executable', 'pyflakes')
+call ale#Set('python_pyflakes_use_global', 0)
+
+function! ale_linters#python#pyflakes#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'python_pyflakes', ['pyflakes'])
+endfunction
+
+function! ale_linters#python#pyflakes#GetCommand(buffer) abort
+ let l:executable = ale_linters#python#pyflakes#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable) . ' %t'
+endfunction
+
+function! ale_linters#python#pyflakes#Handle(buffer, lines) abort
+ let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+)?:? (.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[3],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'pyflakes',
+\ 'executable_callback': 'ale_linters#python#pyflakes#GetExecutable',
+\ 'command_callback': 'ale_linters#python#pyflakes#GetCommand',
+\ 'callback': 'ale_linters#python#pyflakes#Handle',
+\ 'output_stream': 'both',
+\})
diff --git a/ale_linters/python/pylint.vim b/ale_linters/python/pylint.vim
index 88dc4b4..e3e6624 100644
--- a/ale_linters/python/pylint.vim
+++ b/ale_linters/python/pylint.vim
@@ -7,20 +7,57 @@ let g:ale_python_pylint_executable =
let g:ale_python_pylint_options =
\ get(g:, 'ale_python_pylint_options', '')
+let g:ale_python_pylint_use_global = get(g:, 'ale_python_pylint_use_global', 0)
+
function! ale_linters#python#pylint#GetExecutable(buffer) abort
- return g:ale_python_pylint_executable
+ return ale#python#FindExecutable(a:buffer, 'python_pylint', ['pylint'])
endfunction
function! ale_linters#python#pylint#GetCommand(buffer) abort
- return ale_linters#python#pylint#GetExecutable(a:buffer)
- \ . ' ' . g:ale_python_pylint_options
+ return ale#Escape(ale_linters#python#pylint#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'python_pylint_options')
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n'
- \ . ' %t'
+ \ . ' %s'
+endfunction
+
+function! ale_linters#python#pylint#Handle(buffer, lines) abort
+ " Matches patterns like the following:
+ "
+ " test.py:4:4: W0101 (unreachable) Unreachable code
+ let l:pattern = '\v^[^:]+:(\d+):(\d+): ([[:alnum:]]+) \(([^(]*)\) (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ "let l:failed = append(0, l:match)
+ let l:code = l:match[3]
+
+ if (l:code is# 'C0303')
+ \ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ " Skip warnings for trailing whitespace if the option is off.
+ continue
+ endif
+
+ if l:code is# 'I0011'
+ " Skip 'Locally disabling' message
+ continue
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 1,
+ \ 'text': l:match[5],
+ \ 'code': l:match[4],
+ \ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
+ \})
+ endfor
+
+ return l:output
endfunction
call ale#linter#Define('python', {
\ 'name': 'pylint',
\ 'executable_callback': 'ale_linters#python#pylint#GetExecutable',
\ 'command_callback': 'ale_linters#python#pylint#GetCommand',
-\ 'callback': 'ale#handlers#HandlePEP8Format',
+\ 'callback': 'ale_linters#python#pylint#Handle',
+\ 'lint_file': 1,
\})
diff --git a/ale_linters/python/pyls.vim b/ale_linters/python/pyls.vim
new file mode 100644
index 0000000..9888853
--- /dev/null
+++ b/ale_linters/python/pyls.vim
@@ -0,0 +1,28 @@
+" Author: aurieh
+" Description: A language server for Python
+
+call ale#Set('python_pyls_executable', 'pyls')
+call ale#Set('python_pyls_use_global', 0)
+
+function! ale_linters#python#pyls#GetExecutable(buffer) abort
+ return ale#python#FindExecutable(a:buffer, 'python_pyls', ['pyls'])
+endfunction
+
+function! ale_linters#python#pyls#GetCommand(buffer) abort
+ let l:executable = ale_linters#python#pyls#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable)
+endfunction
+
+function! ale_linters#python#pyls#GetLanguage(buffer) abort
+ return 'python'
+endfunction
+
+call ale#linter#Define('python', {
+\ 'name': 'pyls',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale_linters#python#pyls#GetExecutable',
+\ 'command_callback': 'ale_linters#python#pyls#GetCommand',
+\ 'language_callback': 'ale_linters#python#pyls#GetLanguage',
+\ 'project_root_callback': 'ale#python#FindProjectRoot',
+\})
diff --git a/ale_linters/r/lintr.vim b/ale_linters/r/lintr.vim
new file mode 100644
index 0000000..51e5c56
--- /dev/null
+++ b/ale_linters/r/lintr.vim
@@ -0,0 +1,35 @@
+" Author: Michel Lang , w0rp ,
+" Fenner Macrae
+" Description: This file adds support for checking R code with lintr.
+
+let g:ale_r_lintr_options = get(g:, 'ale_r_lintr_options', 'with_defaults()')
+" A reasonable alternative default:
+" get(g:, 'ale_r_lintr_options', 'with_defaults(object_usage_linter = NULL)')
+
+
+let g:ale_r_lintr_lint_package = get(g:, 'ale_r_lintr_lint_package', 0)
+
+function! ale_linters#r#lintr#GetCommand(buffer) abort
+ if ale#Var(a:buffer, 'r_lintr_lint_package')
+ let l:lint_cmd = 'lint_package(cache = FALSE, linters = '
+ \ . ale#Var(a:buffer, 'r_lintr_options') . ')'
+ else
+ let l:lint_cmd = 'lint(cache = FALSE, commandArgs(TRUE), '
+ \ . ale#Var(a:buffer, 'r_lintr_options') . ')'
+ endif
+
+ let l:cmd_string = 'suppressPackageStartupMessages(library(lintr));'
+ \ . l:lint_cmd
+
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'Rscript -e '
+ \ . ale#Escape(l:cmd_string) . ' %t'
+endfunction
+
+call ale#linter#Define('r', {
+\ 'name': 'lintr',
+\ 'executable': 'Rscript',
+\ 'command_callback': 'ale_linters#r#lintr#GetCommand',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'output_stream': 'both',
+\})
diff --git a/ale_linters/reason/merlin.vim b/ale_linters/reason/merlin.vim
new file mode 100644
index 0000000..7bef7df
--- /dev/null
+++ b/ale_linters/reason/merlin.vim
@@ -0,0 +1,17 @@
+" Author: Andrey Popp -- @andreypopp
+" Description: Report errors in ReasonML code with Merlin
+
+if !exists('g:merlin')
+ finish
+endif
+
+function! ale_linters#reason#merlin#Handle(buffer, lines) abort
+ return merlin#ErrorLocList()
+endfunction
+
+call ale#linter#Define('reason', {
+\ 'name': 'merlin',
+\ 'executable': 'ocamlmerlin',
+\ 'command': 'true',
+\ 'callback': 'ale_linters#reason#merlin#Handle',
+\})
diff --git a/ale_linters/reason/ols.vim b/ale_linters/reason/ols.vim
new file mode 100644
index 0000000..b2cd5f7
--- /dev/null
+++ b/ale_linters/reason/ols.vim
@@ -0,0 +1,14 @@
+" Author: Michael Jungo
+" Description: A language server for Reason
+
+call ale#Set('reason_ols_executable', 'ocaml-language-server')
+call ale#Set('reason_ols_use_global', 0)
+
+call ale#linter#Define('reason', {
+\ 'name': 'ols',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale#handlers#ols#GetExecutable',
+\ 'command_callback': 'ale#handlers#ols#GetCommand',
+\ 'language_callback': 'ale#handlers#ols#GetLanguage',
+\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot',
+\})
diff --git a/ale_linters/review/redpen.vim b/ale_linters/review/redpen.vim
new file mode 100644
index 0000000..0006cab
--- /dev/null
+++ b/ale_linters/review/redpen.vim
@@ -0,0 +1,9 @@
+" Author: rhysd https://rhysd.github.io
+" Description: Redpen, a proofreading tool (http://redpen.cc)
+
+call ale#linter#Define('review', {
+\ 'name': 'redpen',
+\ 'executable': 'redpen',
+\ 'command': 'redpen -f review -r json %t',
+\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput',
+\})
diff --git a/ale_linters/rst/alex.vim b/ale_linters/rst/alex.vim
new file mode 100644
index 0000000..e637eae
--- /dev/null
+++ b/ale_linters/rst/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for rst files
+
+call ale#linter#Define('rst', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/rst/proselint.vim b/ale_linters/rst/proselint.vim
index 4e555da..018347a 100644
--- a/ale_linters/rst/proselint.vim
+++ b/ale_linters/rst/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('rst', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/rst/redpen.vim b/ale_linters/rst/redpen.vim
new file mode 100644
index 0000000..ac966c5
--- /dev/null
+++ b/ale_linters/rst/redpen.vim
@@ -0,0 +1,9 @@
+" Author: rhysd https://rhysd.github.io
+" Description: Redpen, a proofreading tool (http://redpen.cc)
+
+call ale#linter#Define('rst', {
+\ 'name': 'redpen',
+\ 'executable': 'redpen',
+\ 'command': 'redpen -f rest -r json %t',
+\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput',
+\})
diff --git a/ale_linters/rst/rstcheck.vim b/ale_linters/rst/rstcheck.vim
new file mode 100644
index 0000000..b660627
--- /dev/null
+++ b/ale_linters/rst/rstcheck.vim
@@ -0,0 +1,37 @@
+" Author: John Nduli https://github.com/jnduli
+" Description: Rstcheck for reStructuredText files
+"
+
+function! ale_linters#rst#rstcheck#Handle(buffer, lines) abort
+ " matches: 'bad_rst.rst:1: (SEVERE/4) Title overline & underline
+ " mismatch.'
+ let l:pattern = '\v^(.+):(\d*): \(([a-zA-Z]*)/\d*\) (.+)$'
+ let l:dir = expand('#' . a:buffer . ':p:h')
+ let l:output = []
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': 0,
+ \ 'type': l:match[3] is# 'SEVERE' ? 'E' : 'W',
+ \ 'text': l:match[4],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#rst#rstcheck#GetCommand(buffer) abort
+ return ale#path#BufferCdString(a:buffer)
+ \ . 'rstcheck'
+ \ . ' %t'
+endfunction
+
+
+call ale#linter#Define('rst', {
+\ 'name': 'rstcheck',
+\ 'executable': 'rstcheck',
+\ 'command_callback': 'ale_linters#rst#rstcheck#GetCommand',
+\ 'callback': 'ale_linters#rst#rstcheck#Handle',
+\ 'output_stream': 'both',
+\})
diff --git a/ale_linters/rst/vale.vim b/ale_linters/rst/vale.vim
new file mode 100644
index 0000000..2e654dc
--- /dev/null
+++ b/ale_linters/rst/vale.vim
@@ -0,0 +1,9 @@
+" Author: chew-z https://github.com/chew-z
+" Description: vale for RST files
+
+call ale#linter#Define('rst', {
+\ 'name': 'vale',
+\ 'executable': 'vale',
+\ 'command': 'vale --output=JSON %t',
+\ 'callback': 'ale#handlers#vale#Handle',
+\})
diff --git a/ale_linters/rst/write-good.vim b/ale_linters/rst/write-good.vim
new file mode 100644
index 0000000..12137db
--- /dev/null
+++ b/ale_linters/rst/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for reStructuredText files
+
+call ale#linter#Define('rst', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/ruby/brakeman.vim b/ale_linters/ruby/brakeman.vim
new file mode 100644
index 0000000..85cfc18
--- /dev/null
+++ b/ale_linters/ruby/brakeman.vim
@@ -0,0 +1,47 @@
+" Author: Eddie Lebow https://github.com/elebow
+" Description: Brakeman, a static analyzer for Rails security
+
+let g:ale_ruby_brakeman_options =
+\ get(g:, 'ale_ruby_brakeman_options', '')
+
+function! ale_linters#ruby#brakeman#Handle(buffer, lines) abort
+ let l:output = []
+ let l:json = ale#util#FuzzyJSONDecode(a:lines, {})
+ let l:sep = has('win32') ? '\' : '/'
+ " Brakeman always outputs paths relative to the Rails app root
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ for l:warning in get(l:json, 'warnings', [])
+ let l:text = l:warning.warning_type . ' ' . l:warning.message . ' (' . l:warning.confidence . ')'
+ let l:line = l:warning.line != v:null ? l:warning.line : 1
+
+ call add(l:output, {
+ \ 'filename': l:rails_root . l:sep . l:warning.file,
+ \ 'lnum': l:line,
+ \ 'type': 'W',
+ \ 'text': l:text,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#ruby#brakeman#GetCommand(buffer) abort
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ if l:rails_root is? ''
+ return ''
+ endif
+
+ return 'brakeman -f json -q '
+ \ . ale#Var(a:buffer, 'ruby_brakeman_options')
+ \ . ' -p ' . ale#Escape(l:rails_root)
+endfunction
+
+call ale#linter#Define('ruby', {
+\ 'name': 'brakeman',
+\ 'executable': 'brakeman',
+\ 'command_callback': 'ale_linters#ruby#brakeman#GetCommand',
+\ 'callback': 'ale_linters#ruby#brakeman#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/ruby/rails_best_practices.vim b/ale_linters/ruby/rails_best_practices.vim
new file mode 100644
index 0000000..107753c
--- /dev/null
+++ b/ale_linters/ruby/rails_best_practices.vim
@@ -0,0 +1,53 @@
+" Author: Eddie Lebow https://github.com/elebow
+" Description: rails_best_practices, a code metric tool for rails projects
+
+let g:ale_ruby_rails_best_practices_options =
+\ get(g:, 'ale_ruby_rails_best_practices_options', '')
+
+function! ale_linters#ruby#rails_best_practices#Handle(buffer, lines) abort
+ let l:output = []
+
+ for l:warning in ale#util#FuzzyJSONDecode(a:lines, [])
+ if !ale#path#IsBufferPath(a:buffer, l:warning.filename)
+ continue
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:warning.line_number + 0,
+ \ 'type': 'W',
+ \ 'text': l:warning.message,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort
+ let l:executable = ale#handlers#rails_best_practices#GetExecutable(a:buffer)
+ let l:exec_args = l:executable =~? 'bundle$'
+ \ ? ' exec rails_best_practices'
+ \ : ''
+
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ if l:rails_root is? ''
+ return ''
+ endif
+
+ let l:output_file = ale#Has('win32') ? '%t ' : '/dev/stdout '
+ let l:cat_file = ale#Has('win32') ? '; type %t' : ''
+
+ return ale#Escape(l:executable) . l:exec_args
+ \ . ' --silent -f json --output-file ' . l:output_file
+ \ . ale#Var(a:buffer, 'ruby_rails_best_practices_options')
+ \ . ale#Escape(l:rails_root)
+ \ . l:cat_file
+endfunction
+
+call ale#linter#Define('ruby', {
+\ 'name': 'rails_best_practices',
+\ 'executable_callback': 'ale#handlers#rails_best_practices#GetExecutable',
+\ 'command_callback': 'ale_linters#ruby#rails_best_practices#GetCommand',
+\ 'callback': 'ale_linters#ruby#rails_best_practices#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/ruby/reek.vim b/ale_linters/ruby/reek.vim
new file mode 100644
index 0000000..a11b9cf
--- /dev/null
+++ b/ale_linters/ruby/reek.vim
@@ -0,0 +1,45 @@
+" Author: Eddie Lebow https://github.com/elebow
+" Description: Reek, a code smell detector for Ruby files
+
+call ale#Set('ruby_reek_show_context', 0)
+call ale#Set('ruby_reek_show_wiki_link', 0)
+
+function! ale_linters#ruby#reek#Handle(buffer, lines) abort
+ let l:output = []
+
+ for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
+ for l:location in l:error.lines
+ call add(l:output, {
+ \ 'lnum': l:location,
+ \ 'type': 'W',
+ \ 'text': s:BuildText(a:buffer, l:error),
+ \ 'code': l:error.smell_type,
+ \})
+ endfor
+ endfor
+
+ return l:output
+endfunction
+
+function! s:BuildText(buffer, error) abort
+ let l:parts = []
+
+ if ale#Var(a:buffer, 'ruby_reek_show_context')
+ call add(l:parts, a:error.context)
+ endif
+
+ call add(l:parts, a:error.message)
+
+ if ale#Var(a:buffer, 'ruby_reek_show_wiki_link')
+ call add(l:parts, '[' . a:error.wiki_link . ']')
+ endif
+
+ return join(l:parts, ' ')
+endfunction
+
+call ale#linter#Define('ruby', {
+\ 'name': 'reek',
+\ 'executable': 'reek',
+\ 'command': 'reek -f json --no-progress --no-color',
+\ 'callback': 'ale_linters#ruby#reek#Handle',
+\})
diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim
index 7f6985d..777f457 100644
--- a/ale_linters/ruby/rubocop.vim
+++ b/ale_linters/ruby/rubocop.vim
@@ -1,52 +1,61 @@
-" Author: ynonp - https://github.com/ynonp
-" Description: rubocop for Ruby files
+" Author: ynonp - https://github.com/ynonp, Eddie Lebow https://github.com/elebow
+" Description: RuboCop, a code style analyzer for Ruby files
+
+function! ale_linters#ruby#rubocop#GetCommand(buffer) abort
+ let l:executable = ale#handlers#rubocop#GetExecutable(a:buffer)
+ let l:exec_args = l:executable =~? 'bundle$'
+ \ ? ' exec rubocop'
+ \ : ''
+
+ return ale#Escape(l:executable) . l:exec_args
+ \ . ' --format json --force-exclusion '
+ \ . ale#Var(a:buffer, 'ruby_rubocop_options')
+ \ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
+endfunction
function! ale_linters#ruby#rubocop#Handle(buffer, lines) abort
- " Matches patterns line the following:
- "
- " :83:29: C: Prefer single-quoted strings when you don't
- " need string interpolation or special symbols.
- let l:pattern = '\v:(\d+):(\d+): (.): (.+)'
+ try
+ let l:errors = json_decode(a:lines[0])
+ catch
+ return []
+ endtry
+
+ if !has_key(l:errors, 'summary')
+ \|| l:errors['summary']['offense_count'] == 0
+ \|| empty(l:errors['files'])
+ return []
+ endif
+
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:text = l:match[4]
- let l:type = l:match[3]
-
- " vcol is Needed to indicate that the column is a character.
+ for l:error in l:errors['files'][0]['offenses']
+ let l:start_col = l:error['location']['column'] + 0
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[1] + 0,
- \ 'col': l:match[2] + 0,
- \ 'text': l:text,
- \ 'type': index(['C', 'E'], l:type) != -1 ? 'E' : 'W',
+ \ 'lnum': l:error['location']['line'] + 0,
+ \ 'col': l:start_col,
+ \ 'end_col': l:start_col + l:error['location']['length'] - 1,
+ \ 'code': l:error['cop_name'],
+ \ 'text': l:error['message'],
+ \ 'type': ale_linters#ruby#rubocop#GetType(l:error['severity']),
\})
endfor
return l:output
endfunction
-function! ale_linters#ruby#rubocop#GetCommand(buffer) abort
- return 'rubocop --format emacs --force-exclusion ' .
- \ g:ale_ruby_rubocop_options .
- \ ' --stdin ' . bufname(a:buffer)
-endfunction
+function! ale_linters#ruby#rubocop#GetType(severity) abort
+ if a:severity is? 'convention'
+ \|| a:severity is? 'warning'
+ \|| a:severity is? 'refactor'
+ return 'W'
+ endif
-" Set this option to change Rubocop options.
-if !exists('g:ale_ruby_rubocop_options')
- " let g:ale_ruby_rubocop_options = '--lint'
- let g:ale_ruby_rubocop_options = ''
-endif
+ return 'E'
+endfunction
call ale#linter#Define('ruby', {
\ 'name': 'rubocop',
-\ 'executable': 'rubocop',
+\ 'executable_callback': 'ale#handlers#rubocop#GetExecutable',
\ 'command_callback': 'ale_linters#ruby#rubocop#GetCommand',
\ 'callback': 'ale_linters#ruby#rubocop#Handle',
\})
diff --git a/ale_linters/ruby/ruby.vim b/ale_linters/ruby/ruby.vim
new file mode 100644
index 0000000..1aa8885
--- /dev/null
+++ b/ale_linters/ruby/ruby.vim
@@ -0,0 +1,22 @@
+" Author: Brandon Roehl - https://github.com/BrandonRoehl
+" Description: Ruby MRI for Ruby files
+
+call ale#Set('ruby_ruby_executable', 'ruby')
+
+function! ale_linters#ruby#ruby#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'ruby_ruby_executable')
+endfunction
+
+function! ale_linters#ruby#ruby#GetCommand(buffer) abort
+ let l:executable = ale_linters#ruby#ruby#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable) . ' -w -c -T1 %t'
+endfunction
+
+call ale#linter#Define('ruby', {
+\ 'name': 'ruby',
+\ 'executable_callback': 'ale_linters#ruby#ruby#GetExecutable',
+\ 'command_callback': 'ale_linters#ruby#ruby#GetCommand',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#ruby#HandleSyntaxErrors',
+\})
diff --git a/ale_linters/rust/cargo.vim b/ale_linters/rust/cargo.vim
index 7f821d2..09f41bb 100644
--- a/ale_linters/rust/cargo.vim
+++ b/ale_linters/rust/cargo.vim
@@ -1,8 +1,14 @@
-" Author: Daniel Schemala
+" Author: Daniel Schemala ,
+" Ivan Petkov
" Description: rustc invoked by cargo for rust files
+call ale#Set('rust_cargo_use_check', 1)
+call ale#Set('rust_cargo_check_all_targets', 0)
+call ale#Set('rust_cargo_default_feature_behavior', 'default')
+call ale#Set('rust_cargo_include_features', '')
+
function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort
- if ale#util#FindNearestFile(a:bufnr, 'Cargo.toml') !=# ''
+ if ale#path#FindNearestFile(a:bufnr, 'Cargo.toml') isnot# ''
return 'cargo'
else
" if there is no Cargo.toml file, we don't use cargo even if it exists,
@@ -11,10 +17,52 @@ function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort
endif
endfunction
+function! ale_linters#rust#cargo#VersionCheck(buffer) abort
+ return !ale#semver#HasVersion('cargo')
+ \ ? 'cargo --version'
+ \ : ''
+endfunction
+
+function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort
+ let l:version = ale#semver#GetVersion('cargo', a:version_output)
+
+ let l:use_check = ale#Var(a:buffer, 'rust_cargo_use_check')
+ \ && ale#semver#GTE(l:version, [0, 17, 0])
+ let l:use_all_targets = l:use_check
+ \ && ale#Var(a:buffer, 'rust_cargo_check_all_targets')
+ \ && ale#semver#GTE(l:version, [0, 22, 0])
+
+ let l:include_features = ale#Var(a:buffer, 'rust_cargo_include_features')
+ if !empty(l:include_features)
+ let l:include_features = ' --features ' . ale#Escape(l:include_features)
+ endif
+
+ let l:default_feature_behavior = ale#Var(a:buffer, 'rust_cargo_default_feature_behavior')
+ if l:default_feature_behavior is# 'all'
+ let l:include_features = ''
+ let l:default_feature = ' --all-features'
+ elseif l:default_feature_behavior is# 'none'
+ let l:default_feature = ' --no-default-features'
+ else
+ let l:default_feature = ''
+ endif
+
+ return 'cargo '
+ \ . (l:use_check ? 'check' : 'build')
+ \ . (l:use_all_targets ? ' --all-targets' : '')
+ \ . ' --frozen --message-format=json -q'
+ \ . l:default_feature
+ \ . l:include_features
+endfunction
+
call ale#linter#Define('rust', {
\ 'name': 'cargo',
\ 'executable_callback': 'ale_linters#rust#cargo#GetCargoExecutable',
-\ 'command': 'cargo build --message-format=json -q',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#rust#cargo#VersionCheck'},
+\ {'callback': 'ale_linters#rust#cargo#GetCommand'},
+\ ],
\ 'callback': 'ale#handlers#rust#HandleRustErrors',
-\ 'output_stream': 'stdout',
+\ 'output_stream': 'both',
+\ 'lint_file': 1,
\})
diff --git a/ale_linters/rust/rls.vim b/ale_linters/rust/rls.vim
new file mode 100644
index 0000000..832fe3e
--- /dev/null
+++ b/ale_linters/rust/rls.vim
@@ -0,0 +1,36 @@
+" Author: w0rp
+" Description: A language server for Rust
+
+call ale#Set('rust_rls_executable', 'rls')
+call ale#Set('rust_rls_toolchain', 'nightly')
+
+function! ale_linters#rust#rls#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'rust_rls_executable')
+endfunction
+
+function! ale_linters#rust#rls#GetCommand(buffer) abort
+ let l:executable = ale_linters#rust#rls#GetExecutable(a:buffer)
+ let l:toolchain = ale#Var(a:buffer, 'rust_rls_toolchain')
+
+ return ale#Escape(l:executable)
+ \ . ' +' . ale#Escape(l:toolchain)
+endfunction
+
+function! ale_linters#rust#rls#GetLanguage(buffer) abort
+ return 'rust'
+endfunction
+
+function! ale_linters#rust#rls#GetProjectRoot(buffer) abort
+ let l:cargo_file = ale#path#FindNearestFile(a:buffer, 'Cargo.toml')
+
+ return !empty(l:cargo_file) ? fnamemodify(l:cargo_file, ':h') : ''
+endfunction
+
+call ale#linter#Define('rust', {
+\ 'name': 'rls',
+\ 'lsp': 'stdio',
+\ 'executable_callback': 'ale_linters#rust#rls#GetExecutable',
+\ 'command_callback': 'ale_linters#rust#rls#GetCommand',
+\ 'language_callback': 'ale_linters#rust#rls#GetLanguage',
+\ 'project_root_callback': 'ale_linters#rust#rls#GetProjectRoot',
+\})
diff --git a/ale_linters/rust/rustc.vim b/ale_linters/rust/rustc.vim
index 1d080b9..3cd401b 100644
--- a/ale_linters/rust/rustc.vim
+++ b/ale_linters/rust/rustc.vim
@@ -1,21 +1,27 @@
" Author: Daniel Schemala
" Description: rustc for rust files
-function! ale_linters#rust#rustc#RustcCommand(buffer_number) abort
+call ale#Set('rust_rustc_options', '-Z no-trans')
+
+function! ale_linters#rust#rustc#RustcCommand(buffer) abort
" Try to guess the library search path. If the project is managed by cargo,
" it's usually /target/debug/deps/ or
" /target/release/deps/
- let l:cargo_file = ale#util#FindNearestFile(a:buffer_number, 'Cargo.toml')
+ let l:cargo_file = ale#path#FindNearestFile(a:buffer, 'Cargo.toml')
- if l:cargo_file !=# ''
- let l:project_root = fnamemodify(l:cargo_file, ':h')
- let l:dependencies = '-L ' . l:project_root . '/target/debug/deps -L ' .
- \ l:project_root . '/target/release/deps'
+ if l:cargo_file isnot# ''
+ let l:root = fnamemodify(l:cargo_file, ':h')
+ let l:dependencies = ' -L ' . ale#Escape(ale#path#GetAbsPath(l:root, 'target/debug/deps'))
+ \ . ' -L ' . ale#Escape(ale#path#GetAbsPath(l:root, 'target/release/deps'))
else
let l:dependencies = ''
endif
- return 'rustc --error-format=json -Z no-trans ' . l:dependencies . ' -'
+ let l:options = ale#Var(a:buffer, 'rust_rustc_options')
+
+ return 'rustc --error-format=json'
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . l:dependencies . ' -'
endfunction
call ale#linter#Define('rust', {
diff --git a/ale_linters/sass/sasslint.vim b/ale_linters/sass/sasslint.vim
index 7d01190..bbe7125 100644
--- a/ale_linters/sass/sasslint.vim
+++ b/ale_linters/sass/sasslint.vim
@@ -4,5 +4,5 @@ call ale#linter#Define('sass', {
\ 'name': 'sasslint',
\ 'executable': 'sass-lint',
\ 'command': 'sass-lint -v -q -f compact %t',
-\ 'callback': 'ale#handlers#HandleCSSLintFormat',
+\ 'callback': 'ale#handlers#css#HandleCSSLintFormat',
\})
diff --git a/ale_linters/sass/stylelint.vim b/ale_linters/sass/stylelint.vim
index 0d85829..98c3725 100644
--- a/ale_linters/sass/stylelint.vim
+++ b/ale_linters/sass/stylelint.vim
@@ -1,21 +1,12 @@
" Author: diartyz
-let g:ale_sass_stylelint_executable =
-\ get(g:, 'ale_sass_stylelint_executable', 'stylelint')
-
-let g:ale_sass_stylelint_use_global =
-\ get(g:, 'ale_sass_stylelint_use_global', 0)
+call ale#Set('sass_stylelint_executable', 'stylelint')
+call ale#Set('sass_stylelint_use_global', 0)
function! ale_linters#sass#stylelint#GetExecutable(buffer) abort
- if g:ale_sass_stylelint_use_global
- return g:ale_sass_stylelint_executable
- endif
-
- return ale#util#ResolveLocalPath(
- \ a:buffer,
+ return ale#node#FindExecutable(a:buffer, 'sass_stylelint', [
\ 'node_modules/.bin/stylelint',
- \ g:ale_sass_stylelint_executable
- \)
+ \])
endfunction
function! ale_linters#sass#stylelint#GetCommand(buffer) abort
@@ -27,5 +18,5 @@ call ale#linter#Define('sass', {
\ 'name': 'stylelint',
\ 'executable_callback': 'ale_linters#sass#stylelint#GetExecutable',
\ 'command_callback': 'ale_linters#sass#stylelint#GetCommand',
-\ 'callback': 'ale#handlers#HandleStyleLintFormat',
+\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/scala/scalac.vim b/ale_linters/scala/scalac.vim
index 6cd4d24..584aee7 100644
--- a/ale_linters/scala/scalac.vim
+++ b/ale_linters/scala/scalac.vim
@@ -1,6 +1,26 @@
-" Author: Zoltan Kalmar - https://github.com/kalmiz
+" Author: Zoltan Kalmar - https://github.com/kalmiz,
+" w0rp
" Description: Basic scala support using scalac
+function! ale_linters#scala#scalac#GetExecutable(buffer) abort
+ if index(split(getbufvar(a:buffer, '&filetype'), '\.'), 'sbt') >= 0
+ " Don't check sbt files with scalac.
+ return ''
+ endif
+
+ return 'scalac'
+endfunction
+
+function! ale_linters#scala#scalac#GetCommand(buffer) abort
+ let l:executable = ale_linters#scala#scalac#GetExecutable(a:buffer)
+
+ if empty(l:executable)
+ return ''
+ endif
+
+ return ale#Escape(l:executable) . ' -Ystop-after:parser %t'
+endfunction
+
function! ale_linters#scala#scalac#Handle(buffer, lines) abort
" Matches patterns line the following:
"
@@ -18,20 +38,14 @@ function! ale_linters#scala#scalac#Handle(buffer, lines) abort
endif
let l:text = l:match[3]
- let l:type = l:match[2] ==# 'error' ? 'E' : 'W'
+ let l:type = l:match[2] is# '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,
\ 'col': l:col + 1,
\ 'text': l:text,
@@ -44,8 +58,8 @@ endfunction
call ale#linter#Define('scala', {
\ 'name': 'scalac',
-\ 'executable': 'scalac',
-\ 'output_stream': 'stderr',
-\ 'command': 'scalac -Ystop-after:parser %t',
+\ 'executable_callback': 'ale_linters#scala#scalac#GetExecutable',
+\ 'command_callback': 'ale_linters#scala#scalac#GetCommand',
\ 'callback': 'ale_linters#scala#scalac#Handle',
+\ 'output_stream': 'stderr',
\})
diff --git a/ale_linters/scala/scalastyle.vim b/ale_linters/scala/scalastyle.vim
new file mode 100644
index 0000000..f78fd74
--- /dev/null
+++ b/ale_linters/scala/scalastyle.vim
@@ -0,0 +1,94 @@
+" Author: Kevin Kays - https://github.com/okkays
+" Description: Support for the scalastyle checker.
+
+let g:ale_scala_scalastyle_options =
+\ get(g:, 'ale_scala_scalastyle_options', '')
+
+let g:ale_scalastyle_config_loc =
+\ get(g:, 'ale_scalastyle_config_loc', '')
+
+function! ale_linters#scala#scalastyle#Handle(buffer, lines) abort
+ " Look for help output from scalastyle first, which indicates that no
+ " configuration file was found.
+ for l:line in a:lines[:10]
+ if l:line =~# '-c, --config'
+ return [{
+ \ 'lnum': 1,
+ \ 'text': '(See :help ale-scala-scalastyle)'
+ \ . ' No scalastyle configuration file was found.',
+ \}]
+ endif
+ endfor
+
+ " Matches patterns like the following:
+ "
+ " warning file=/home/blurble/Doop.scala message=Missing or badly formed ScalaDoc: Extra @param foobles line=190
+ let l:patterns = [
+ \ '^\(.\+\) .\+ message=\(.\+\) line=\(\d\+\)$',
+ \ '^\(.\+\) .\+ message=\(.\+\) line=\(\d\+\) column=\(\d\+\)$',
+ \]
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:patterns)
+ let l:args = {
+ \ 'lnum': l:match[3] + 0,
+ \ 'type': l:match[1] =~? 'error' ? 'E' : 'W',
+ \ 'text': l:match[2]
+ \}
+
+ if !empty(l:match[4])
+ let l:args['col'] = l:match[4] + 1
+ endif
+
+ call add(l:output, l:args)
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#scala#scalastyle#GetCommand(buffer) abort
+ " Search for scalastyle config in parent directories.
+ let l:scalastyle_config = ''
+ let l:potential_configs = [
+ \ 'scalastyle_config.xml',
+ \ 'scalastyle-config.xml'
+ \]
+ for l:config in l:potential_configs
+ let l:scalastyle_config = ale#path#ResolveLocalPath(
+ \ a:buffer,
+ \ l:config,
+ \ ''
+ \)
+ if !empty(l:scalastyle_config)
+ break
+ endif
+ endfor
+
+ " If all else fails, try the global config.
+ if empty(l:scalastyle_config)
+ let l:scalastyle_config = get(g:, 'ale_scalastyle_config_loc', '')
+ endif
+
+ " Build the command using the config file and additional options.
+ let l:command = 'scalastyle'
+
+ if !empty(l:scalastyle_config)
+ let l:command .= ' --config ' . ale#Escape(l:scalastyle_config)
+ endif
+
+ if !empty(g:ale_scala_scalastyle_options)
+ let l:command .= ' ' . g:ale_scala_scalastyle_options
+ endif
+
+ let l:command .= ' %t'
+
+ return l:command
+endfunction
+
+call ale#linter#Define('scala', {
+\ 'name': 'scalastyle',
+\ 'executable': 'scalastyle',
+\ 'output_stream': 'stdout',
+\ 'command_callback': 'ale_linters#scala#scalastyle#GetCommand',
+\ 'callback': 'ale_linters#scala#scalastyle#Handle',
+\})
diff --git a/ale_linters/scss/sasslint.vim b/ale_linters/scss/sasslint.vim
index ee4fdbb..bd01646 100644
--- a/ale_linters/scss/sasslint.vim
+++ b/ale_linters/scss/sasslint.vim
@@ -4,5 +4,5 @@ call ale#linter#Define('scss', {
\ 'name': 'sasslint',
\ 'executable': 'sass-lint',
\ 'command': 'sass-lint -v -q -f compact %t',
-\ 'callback': 'ale#handlers#HandleCSSLintFormat',
+\ 'callback': 'ale#handlers#css#HandleCSSLintFormat',
\})
diff --git a/ale_linters/scss/scsslint.vim b/ale_linters/scss/scsslint.vim
index d8aeef0..7ce5724 100644
--- a/ale_linters/scss/scsslint.vim
+++ b/ale_linters/scss/scsslint.vim
@@ -8,25 +8,18 @@ function! ale_linters#scss#scsslint#Handle(buffer, lines) abort
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
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ if !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
+ \&& l:match[4] =~# '^TrailingWhitespace'
+ " Skip trailing whitespace warnings if that option is off.
continue
endif
- if !g:ale_warn_about_trailing_whitespace && l:match[4] =~# '^TrailingWhitespace'
- " Skip trailing whitespace warnings if that option is on.
- 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,
\ 'col': l:match[2] + 0,
\ 'text': l:match[4],
- \ 'type': l:match[3] ==# 'E' ? 'E' : 'W',
+ \ 'type': l:match[3] is# 'E' ? 'E' : 'W',
\})
endfor
diff --git a/ale_linters/scss/stylelint.vim b/ale_linters/scss/stylelint.vim
index 2f5da7c..00189a8 100644
--- a/ale_linters/scss/stylelint.vim
+++ b/ale_linters/scss/stylelint.vim
@@ -1,21 +1,12 @@
" Author: diartyz
-let g:ale_scss_stylelint_executable =
-\ get(g:, 'ale_scss_stylelint_executable', 'stylelint')
-
-let g:ale_scss_stylelint_use_global =
-\ get(g:, 'ale_scss_stylelint_use_global', 0)
+call ale#Set('scss_stylelint_executable', 'stylelint')
+call ale#Set('scss_stylelint_use_global', 0)
function! ale_linters#scss#stylelint#GetExecutable(buffer) abort
- if g:ale_scss_stylelint_use_global
- return g:ale_scss_stylelint_executable
- endif
-
- return ale#util#ResolveLocalPath(
- \ a:buffer,
+ return ale#node#FindExecutable(a:buffer, 'scss_stylelint', [
\ 'node_modules/.bin/stylelint',
- \ g:ale_scss_stylelint_executable
- \)
+ \])
endfunction
function! ale_linters#scss#stylelint#GetCommand(buffer) abort
@@ -27,5 +18,5 @@ call ale#linter#Define('scss', {
\ 'name': 'stylelint',
\ 'executable_callback': 'ale_linters#scss#stylelint#GetExecutable',
\ 'command_callback': 'ale_linters#scss#stylelint#GetCommand',
-\ 'callback': 'ale#handlers#HandleStyleLintFormat',
+\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
\})
diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim
index c7365ae..cf5e4e6 100644
--- a/ale_linters/sh/shell.vim
+++ b/ale_linters/sh/shell.vim
@@ -1,36 +1,33 @@
" Author: w0rp
" Description: Lints sh files using bash -n
+" Backwards compatibility
+if exists('g:ale_linters_sh_shell_default_shell')
+ let g:ale_sh_shell_default_shell = g:ale_linters_sh_shell_default_shell
+endif
+
" This option can be changed to change the default shell when the shell
" cannot be taken from the hashbang line.
-if !exists('g:ale_linters_sh_shell_default_shell')
- let g:ale_linters_sh_shell_default_shell = fnamemodify($SHELL, ':t')
+if !exists('g:ale_sh_shell_default_shell')
+ let g:ale_sh_shell_default_shell = fnamemodify($SHELL, ':t')
- if g:ale_linters_sh_shell_default_shell ==# ''
- let g:ale_linters_sh_shell_default_shell = 'bash'
+ if g:ale_sh_shell_default_shell is# '' || g:ale_sh_shell_default_shell is# 'fish'
+ let g:ale_sh_shell_default_shell = 'bash'
endif
endif
function! ale_linters#sh#shell#GetExecutable(buffer) abort
- let l:banglines = getbufline(a:buffer, 1)
+ let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
- " Take the shell executable from the hashbang, if we can.
- if len(l:banglines) == 1 && l:banglines[0] =~# '^#!'
- " Remove options like -e, etc.
- let l:line = substitute(l:banglines[0], '--\?[a-zA-Z0-9]\+', '', 'g')
-
- for l:possible_shell in ['bash', 'tcsh', 'csh', 'zsh', 'sh']
- if l:line =~# l:possible_shell . '\s*$'
- return l:possible_shell
- endif
- endfor
+ if !empty(l:shell_type)
+ return l:shell_type
endif
- return g:ale_linters_sh_shell_default_shell
+ return ale#Var(a:buffer, 'sh_shell_default_shell')
endfunction
function! ale_linters#sh#shell#GetCommand(buffer) abort
- return ale_linters#sh#shell#GetExecutable(a:buffer) . ' -n'
+ return ale_linters#sh#shell#GetExecutable(a:buffer) . ' -n %t'
endfunction
function! ale_linters#sh#shell#Handle(buffer, lines) abort
@@ -38,28 +35,13 @@ 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 = '^[^:]\+: \%(\w\+ \|\)\(\d\+\): \(.\+\)'
+ let l:pattern = '\v(line |: ?)(\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: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.
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type,
+ \ 'lnum': str2nr(l:match[2]),
+ \ 'text': l:match[3],
\})
endfor
diff --git a/ale_linters/sh/shellcheck.vim b/ale_linters/sh/shellcheck.vim
index 735a0cd..ac66892 100644
--- a/ale_linters/sh/shellcheck.vim
+++ b/ale_linters/sh/shellcheck.vim
@@ -2,39 +2,121 @@
" Description: This file adds support for using the shellcheck linter with
" shell scripts.
-" This global variable can be set with a string of comma-seperated error
+" This global variable can be set with a string of comma-separated error
" codes to exclude from shellcheck. For example:
"
-" let g:ale_linters_sh_shellcheck_exclusions = 'SC2002,SC2004'
-if !exists('g:ale_linters_sh_shellcheck_exclusions')
- let g:ale_linters_sh_shellcheck_exclusions = ''
-endif
+" let g:ale_sh_shellcheck_exclusions = 'SC2002,SC2004'
+let g:ale_sh_shellcheck_exclusions =
+\ get(g:, 'ale_sh_shellcheck_exclusions', get(g:, 'ale_linters_sh_shellcheck_exclusions', ''))
-if g:ale_linters_sh_shellcheck_exclusions !=# ''
- let s:exclude_option = '-e ' . g:ale_linters_sh_shellcheck_exclusions
-else
- let s:exclude_option = ''
-endif
+let g:ale_sh_shellcheck_executable =
+\ get(g:, 'ale_sh_shellcheck_executable', 'shellcheck')
-function! s:GetDialectArgument() abort
- if exists('b:is_bash') && b:is_bash
- return '-s bash'
- elseif exists('b:is_sh') && b:is_sh
- return '-s sh'
- elseif exists('b:is_kornshell') && b:is_kornshell
- return '-s ksh'
+let g:ale_sh_shellcheck_use_global =
+\ get(g:, 'ale_sh_shellcheck_use_global', 0)
+
+let g:ale_sh_shellcheck_options =
+\ get(g:, 'ale_sh_shellcheck_options', '')
+
+function! ale_linters#sh#shellcheck#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'sh_shellcheck', [
+ \ 'node_modules/.bin/shellcheck',
+ \])
+endfunction
+
+function! ale_linters#sh#shellcheck#GetDialectArgument(buffer) abort
+ let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
+
+ if !empty(l:shell_type)
+ " Use the dash dialect for /bin/ash, etc.
+ if l:shell_type is# 'ash'
+ return 'dash'
+ endif
+
+ return l:shell_type
+ endif
+
+ " If there's no hashbang, try using Vim's buffer variables.
+ if getbufvar(a:buffer, 'is_bash', 0)
+ return 'bash'
+ elseif getbufvar(a:buffer, 'is_sh', 0)
+ return 'sh'
+ elseif getbufvar(a:buffer, 'is_kornshell', 0)
+ return 'ksh'
endif
return ''
endfunction
-function! ale_linters#sh#shellcheck#GetCommand(buffer) abort
- return 'shellcheck ' . s:exclude_option . ' ' . s:GetDialectArgument() . ' -f gcc -'
+function! ale_linters#sh#shellcheck#VersionCheck(buffer) abort
+ let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer)
+
+ " Don't check the version again if we've already cached it.
+ return !ale#semver#HasVersion(l:executable)
+ \ ? ale#Escape(l:executable) . ' --version'
+ \ : ''
+endfunction
+
+function! ale_linters#sh#shellcheck#GetCommand(buffer, version_output) abort
+ let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer)
+ let l:version = ale#semver#GetVersion(l:executable, a:version_output)
+
+ let l:options = ale#Var(a:buffer, 'sh_shellcheck_options')
+ let l:exclude_option = ale#Var(a:buffer, 'sh_shellcheck_exclusions')
+ let l:dialect = ale_linters#sh#shellcheck#GetDialectArgument(a:buffer)
+ let l:external_option = ale#semver#GTE(l:version, [0, 4, 0]) ? ' -x' : ''
+
+ return ale#path#BufferCdString(a:buffer)
+ \ . ale#Escape(l:executable)
+ \ . (!empty(l:dialect) ? ' -s ' . l:dialect : '')
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . (!empty(l:exclude_option) ? ' -e ' . l:exclude_option : '')
+ \ . l:external_option
+ \ . ' -f gcc -'
+endfunction
+
+function! ale_linters#sh#shellcheck#Handle(buffer, lines) abort
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+) \[([^\]]+)\]$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ if l:match[4] is# 'error'
+ let l:type = 'E'
+ elseif l:match[4] is# 'note'
+ let l:type = 'I'
+ else
+ let l:type = 'W'
+ endif
+
+ let l:item = {
+ \ 'lnum': str2nr(l:match[2]),
+ \ 'type': l:type,
+ \ 'text': l:match[5],
+ \ 'code': l:match[6],
+ \}
+
+ if !empty(l:match[3])
+ let l:item.col = str2nr(l:match[3])
+ endif
+
+ " If the filename is something like , or -, then
+ " this is an error for the file we checked.
+ if l:match[1] isnot# '-' && l:match[1][0] isnot# '<'
+ let l:item['filename'] = l:match[1]
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
endfunction
call ale#linter#Define('sh', {
\ 'name': 'shellcheck',
-\ 'executable': 'shellcheck',
-\ 'command_callback': 'ale_linters#sh#shellcheck#GetCommand',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'executable_callback': 'ale_linters#sh#shellcheck#GetExecutable',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#sh#shellcheck#VersionCheck'},
+\ {'callback': 'ale_linters#sh#shellcheck#GetCommand'},
+\ ],
+\ 'callback': 'ale_linters#sh#shellcheck#Handle',
\})
diff --git a/ale_linters/slim/slimlint.vim b/ale_linters/slim/slimlint.vim
index 8613951..00c6b26 100644
--- a/ale_linters/slim/slimlint.vim
+++ b/ale_linters/slim/slimlint.vim
@@ -1,5 +1,25 @@
" Author: Markus Doits - https://github.com/doits
-" Description: slim-lint for Slim files, based on hamllint.vim
+" Description: slim-lint for Slim files
+
+function! ale_linters#slim#slimlint#GetCommand(buffer) abort
+ let l:command = 'slim-lint %t'
+
+ let l:rubocop_config = ale#path#FindNearestFile(a:buffer, '.rubocop.yml')
+
+ " Set SLIM_LINT_RUBOCOP_CONF variable as it is needed for slim-lint to
+ " pick up the rubocop config.
+ "
+ " See https://github.com/sds/slim-lint/blob/master/lib/slim_lint/linter/README.md#rubocop
+ if !empty(l:rubocop_config)
+ if ale#Has('win32')
+ let l:command = 'set SLIM_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config) . ' && ' . l:command
+ else
+ let l:command = 'SLIM_LINT_RUBOCOP_CONF=' . ale#Escape(l:rubocop_config) . ' ' . l:command
+ endif
+ endif
+
+ return l:command
+endfunction
function! ale_linters#slim#slimlint#Handle(buffer, lines) abort
" Matches patterns like the following:
@@ -7,19 +27,21 @@ function! ale_linters#slim#slimlint#Handle(buffer, lines) abort
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,
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:item = {
\ 'lnum': l:match[1] + 0,
\ 'type': l:match[2],
\ 'text': l:match[3]
- \})
+ \}
+
+ let l:code_match = matchlist(l:item.text, '\v^([^:]+): (.+)$')
+
+ if !empty(l:code_match)
+ let l:item.code = l:code_match[1]
+ let l:item.text = l:code_match[2]
+ endif
+
+ call add(l:output, l:item)
endfor
return l:output
@@ -28,6 +50,6 @@ endfunction
call ale#linter#Define('slim', {
\ 'name': 'slimlint',
\ 'executable': 'slim-lint',
-\ 'command': 'slim-lint %t',
+\ 'command_callback': 'ale_linters#slim#slimlint#GetCommand',
\ 'callback': 'ale_linters#slim#slimlint#Handle'
\})
diff --git a/ale_linters/sml/smlnj.vim b/ale_linters/sml/smlnj.vim
index c75f89b..f15579e 100644
--- a/ale_linters/sml/smlnj.vim
+++ b/ale_linters/sml/smlnj.vim
@@ -1,40 +1,9 @@
-" Author: Paulo Alem
-" Description: Rudimentary SML checking with smlnj compiler
+" Author: Paulo Alem , Jake Zimmerman
+" Description: Single-file SML checking with SML/NJ 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,
- \ 'col': 1,
- \ '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', {
+call ale#linter#Define('sml', {
\ 'name': 'smlnj',
-\ 'executable': 'sml',
+\ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjFile',
\ 'command': 'sml',
-\ 'callback': 'ale_linters#sml#smlnj#Handle',
+\ 'callback': 'ale#handlers#sml#Handle',
\})
diff --git a/ale_linters/sml/smlnj_cm.vim b/ale_linters/sml/smlnj_cm.vim
new file mode 100644
index 0000000..96cd7bd
--- /dev/null
+++ b/ale_linters/sml/smlnj_cm.vim
@@ -0,0 +1,19 @@
+" Author: Jake Zimmerman
+" Description: SML checking with SML/NJ Compilation Manager
+
+function! ale_linters#sml#smlnj_cm#GetCommand(buffer) abort
+ let l:cmfile = ale#handlers#sml#GetCmFile(a:buffer)
+ return 'sml -m ' . l:cmfile . ' < /dev/null'
+endfunction
+
+" Using CM requires that we set "lint_file: 1", since it reads the files
+" from the disk itself.
+call ale#linter#Define('sml', {
+\ 'name': 'smlnj-cm',
+\ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm',
+\ 'lint_file': 1,
+\ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand',
+\ 'callback': 'ale#handlers#sml#Handle',
+\})
+
+" vim:ts=4:sts=4:sw=4
diff --git a/ale_linters/solidity/solhint.vim b/ale_linters/solidity/solhint.vim
new file mode 100644
index 0000000..519fd49
--- /dev/null
+++ b/ale_linters/solidity/solhint.vim
@@ -0,0 +1,30 @@
+" Author: Franco Victorio - https://github.com/fvictorio
+" Description: Report errors in Solidity code with solhint
+
+function! ale_linters#solidity#solhint#Handle(buffer, lines) abort
+ " Matches patterns like the following:
+ " /path/to/file/file.sol: line 1, col 10, Error - 'addOne' is defined but never used. (no-unused-vars)
+
+ let l:pattern = '\v^[^:]+: line (\d+), col (\d+), (Error|Warning) - (.*) \((.*)\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:isError = l:match[3] is? 'error'
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[4],
+ \ 'code': l:match[5],
+ \ 'type': l:isError ? 'E' : 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('solidity', {
+\ 'name': 'solhint',
+\ 'executable': 'solhint',
+\ 'command': 'solhint --formatter compact %t',
+\ 'callback': 'ale_linters#solidity#solhint#Handle',
+\})
diff --git a/ale_linters/solidity/solium.vim b/ale_linters/solidity/solium.vim
new file mode 100644
index 0000000..61ab184
--- /dev/null
+++ b/ale_linters/solidity/solium.vim
@@ -0,0 +1,9 @@
+" Author: Jeff Sutherland - https://github.com/jdsutherland
+" Description: Report errors in Solidity code with solium
+
+call ale#linter#Define('solidity', {
+\ 'name': 'solium',
+\ 'executable': 'solium',
+\ 'command': 'solium --reporter gcc --file %t',
+\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\})
diff --git a/ale_linters/spec/rpmlint.vim b/ale_linters/spec/rpmlint.vim
new file mode 100644
index 0000000..f5308af
--- /dev/null
+++ b/ale_linters/spec/rpmlint.vim
@@ -0,0 +1,85 @@
+" Author: Jason Tibbitts
+" Description: Adds support for checking RPM spec files with rpmlint
+
+" rpmlint will produce varions types of output:
+"
+" Lines like the following are output when the file is simply not able to be
+" parsed by rpmspec -P:
+" apcupsd.spec: E: specfile-error warning: bogus date in %changelog: Mon Oct 1 2005 - Foo
+" apcupsd.spec: E: specfile-error error: %changelog not in descending chronological order
+" They do not contain a line number, and there's not a whole lot that can be
+" done to locate them besides grep for them. rpmlint is just passing the
+" output from rpm along with the filename, an error indicator, and an error
+" type.
+"
+" Lines like the following:
+" cyrus-imapd.spec:23: W: macro-in-comment %version
+" cyrus-imapd.spec:18: E: hardcoded-library-path in %_prefix/lib/%name
+" indicate warnings and errors, respectively. No column numbers are provided
+"
+" Lines like:
+" apcupsd.spec: I: checking
+" apcupsd.spec: I: checking-url https://downloads.sourceforge.net/apcupsd/apcupsd-3.14.14.tar.gz (timeout 10 seconds)
+" are merely informational and are only output when -v is passed. But they
+" may be useful in a log to know why things are taking so long.
+"
+" And this is always output at the end and should just be ignored:
+" 0 packages and 1 specfiles checked; 4 errors, 0 warnings.
+
+let g:ale_spec_rpmlint_executable =
+\ get(g:, 'ale_spec_rpmlint_executable', 'rpmlint')
+
+let g:ale_spec_rpmlint_options =
+\ get(g:, 'ale_spec_rpmlint_options', '')
+
+function! ale_linters#spec#rpmlint#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'spec_rpmlint_executable')
+endfunction
+
+function! ale_linters#spec#rpmlint#GetCommand(buffer) abort
+ return ale_linters#spec#rpmlint#GetExecutable(a:buffer)
+ \ . ' ' . ale#Var(a:buffer, 'spec_rpmlint_options')
+ \ . ' -o "NetworkEnabled False"'
+ \ . ' -v'
+ \ . ' %t'
+endfunction
+
+function! ale_linters#spec#rpmlint#Handle(buffer, lines) abort
+ " let l:pat_inform = '^.\+: I: \(.+\)'
+ let l:pat_errwarn = '^.\+:\(\d\+\): \([EW]\): \(.\+\)'
+ let l:pat_baderr = '^.\+: E: \(.\+\)'
+ let l:output = []
+
+ for l:line in a:lines
+ let l:match_errwarn = matchlist(l:line, l:pat_errwarn)
+ let l:match_baderr = matchlist(l:line, l:pat_baderr)
+
+ if len(l:match_errwarn) > 0
+ let l:text = l:match_errwarn[3]
+ let l:type = l:match_errwarn[2]
+ let l:lnum = l:match_errwarn[1] + 0
+ elseif len(l:match_baderr) > 0
+ let l:text = l:match_baderr[1]
+ let l:type = 'E'
+ let l:lnum = 1
+ else
+ continue
+ endif
+
+ call add(l:output, {
+ \ 'bufnr': a:buffer,
+ \ 'lnum': l:lnum,
+ \ 'text': l:text,
+ \ 'type': l:type,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('spec', {
+\ 'name': 'rpmlint',
+\ 'executable_callback': 'ale_linters#spec#rpmlint#GetExecutable',
+\ 'command_callback': 'ale_linters#spec#rpmlint#GetCommand',
+\ 'callback': 'ale_linters#spec#rpmlint#Handle',
+\})
diff --git a/ale_linters/sql/sqlint.vim b/ale_linters/sql/sqlint.vim
new file mode 100644
index 0000000..ca89372
--- /dev/null
+++ b/ale_linters/sql/sqlint.vim
@@ -0,0 +1,28 @@
+" Author: Adriaan Zonnenberg
+" Description: sqlint for SQL files
+
+function! ale_linters#sql#sqlint#Handle(buffer, lines) abort
+ " Matches patterns like the following:
+ "
+ " stdin:3:1:ERROR syntax error at or near "WIBBLE"
+ let l:pattern = '\v^[^:]+:(\d+):(\d+):(\u+) (.*)'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'type': l:match[3][0],
+ \ 'text': l:match[4],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('sql', {
+\ 'name': 'sqlint',
+\ 'executable': 'sqlint',
+\ 'command': 'sqlint',
+\ 'callback': 'ale_linters#sql#sqlint#Handle',
+\})
diff --git a/ale_linters/stylus/stylelint.vim b/ale_linters/stylus/stylelint.vim
new file mode 100644
index 0000000..2721529
--- /dev/null
+++ b/ale_linters/stylus/stylelint.vim
@@ -0,0 +1,24 @@
+" Author: diartyz , w0rp
+
+call ale#Set('stylus_stylelint_executable', 'stylelint')
+call ale#Set('stylus_stylelint_options', '')
+call ale#Set('stylus_stylelint_use_global', 0)
+
+function! ale_linters#stylus#stylelint#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'stylus_stylelint', [
+ \ 'node_modules/.bin/stylelint',
+ \])
+endfunction
+
+function! ale_linters#stylus#stylelint#GetCommand(buffer) abort
+ return ale_linters#stylus#stylelint#GetExecutable(a:buffer)
+ \ . ' ' . ale#Var(a:buffer, 'stylus_stylelint_options')
+ \ . ' --stdin-filename %s'
+endfunction
+
+call ale#linter#Define('stylus', {
+\ 'name': 'stylelint',
+\ 'executable_callback': 'ale_linters#stylus#stylelint#GetExecutable',
+\ 'command_callback': 'ale_linters#stylus#stylelint#GetCommand',
+\ 'callback': 'ale#handlers#css#HandleStyleLintFormat',
+\})
diff --git a/ale_linters/swift/swiftlint.vim b/ale_linters/swift/swiftlint.vim
index aaa77a9..697d246 100644
--- a/ale_linters/swift/swiftlint.vim
+++ b/ale_linters/swift/swiftlint.vim
@@ -1,9 +1,51 @@
" Author: David Mohundro
" Description: swiftlint for swift files
+
+function! ale_linters#swift#swiftlint#Handle(buffer, lines) abort
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:item = {
+ \ 'lnum': str2nr(l:match[2]),
+ \ 'type': l:match[4] is# 'error' ? 'E' : 'W',
+ \ 'text': l:match[5],
+ \}
+
+ if l:match[4] is# 'error'
+ let l:item.type = 'E'
+ elseif l:match[4] is# 'note'
+ let l:item.type = 'I'
+ endif
+
+ if !empty(l:match[3])
+ let l:item.col = str2nr(l:match[3])
+ endif
+
+ " If the filename is something like , or -, then
+ " this is an error for the file we checked.
+ if l:match[1] isnot# '-' && l:match[1][0] isnot# '<'
+ let l:item['filename'] = l:match[1]
+ endif
+
+ " Parse the code if it's there.
+ let l:code_match = matchlist(l:item.text, '\v^(.+) \(([^ (]+)\)$')
+
+ if !empty(l:code_match)
+ let l:item.text = l:code_match[1]
+ let l:item.code = l:code_match[2]
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
+
call ale#linter#Define('swift', {
\ 'name': 'swiftlint',
\ 'executable': 'swiftlint',
\ 'command': 'swiftlint lint --use-stdin',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'callback': 'ale_linters#swift#swiftlint#Handle',
\})
diff --git a/ale_linters/tcl/nagelfar.vim b/ale_linters/tcl/nagelfar.vim
new file mode 100644
index 0000000..13b7a54
--- /dev/null
+++ b/ale_linters/tcl/nagelfar.vim
@@ -0,0 +1,46 @@
+" Author: Nick James
+" Description: nagelfar linter for tcl files
+
+call ale#Set('tcl_nagelfar_executable', 'nagelfar.tcl')
+call ale#Set('tcl_nagelfar_options', '')
+
+function! ale_linters#tcl#nagelfar#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'tcl_nagelfar_executable')
+endfunction
+
+function! ale_linters#tcl#nagelfar#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'tcl_nagelfar_options')
+
+ return ale#Escape(ale_linters#tcl#nagelfar#GetExecutable(a:buffer))
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' %s'
+endfunction
+
+function! ale_linters#tcl#nagelfar#Handle(buffer, lines) abort
+ " Matches patterns like the following:
+ " Line 5: W Found constant "bepa" which is also a variable.
+ " Line 13: E Wrong number of arguments (3) to "set"
+ " Line 93: N Close brace not aligned with line 90 (4 0)
+
+ let l:pattern = '^Line\s\+\([0-9]\+\): \([NEW]\) \(.*\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'type': l:match[2] is# 'N' ? 'W' : l:match[2],
+ \ 'text': l:match[3],
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('tcl', {
+\ 'name': 'nagelfar',
+\ 'output_stream': 'stdout',
+\ 'executable_callback': 'ale_linters#tcl#nagelfar#GetExecutable',
+\ 'command_callback': 'ale_linters#tcl#nagelfar#GetCommand',
+\ 'callback': 'ale_linters#tcl#nagelfar#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/terraform/tflint.vim b/ale_linters/terraform/tflint.vim
new file mode 100644
index 0000000..93966ff
--- /dev/null
+++ b/ale_linters/terraform/tflint.vim
@@ -0,0 +1,62 @@
+" Author: Nat Williams
+" Description: tflint for Terraform files
+"
+" See: https://www.terraform.io/
+" https://github.com/wata727/tflint
+
+call ale#Set('terraform_tflint_options', '')
+call ale#Set('terraform_tflint_executable', 'tflint')
+
+function! ale_linters#terraform#tflint#Handle(buffer, lines) abort
+ let l:output = []
+
+ for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
+ if l:error.type is# 'ERROR'
+ let l:type = 'E'
+ elseif l:error.type is# 'NOTICE'
+ let l:type = 'I'
+ else
+ let l:type = 'W'
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:error.line,
+ \ 'text': l:error.message,
+ \ 'type': l:type,
+ \ 'code': l:error.detector,
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#terraform#tflint#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'terraform_tflint_executable')
+endfunction
+
+function! ale_linters#terraform#tflint#GetCommand(buffer) abort
+ let l:cmd = ale#Escape(ale#Var(a:buffer, 'terraform_tflint_executable'))
+
+ let l:config_file = ale#path#FindNearestFile(a:buffer, '.tflint.hcl')
+ if !empty(l:config_file)
+ let l:cmd .= ' --config ' . ale#Escape(l:config_file)
+ endif
+
+ let l:opts = ale#Var(a:buffer, 'terraform_tflint_options')
+ if !empty(l:opts)
+ let l:cmd .= ' ' . l:opts
+ endif
+
+ let l:cmd .= ' -f json %t'
+
+ return l:cmd
+endfunction
+
+call ale#linter#Define('terraform', {
+\ 'name': 'tflint',
+\ 'executable_callback': 'ale_linters#terraform#tflint#GetExecutable',
+\ 'command_callback': 'ale_linters#terraform#tflint#GetCommand',
+\ 'callback': 'ale_linters#terraform#tflint#Handle',
+\})
+
+" vim:sw=4
diff --git a/ale_linters/tex/alex.vim b/ale_linters/tex/alex.vim
new file mode 100644
index 0000000..78c530f
--- /dev/null
+++ b/ale_linters/tex/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for TeX files
+
+call ale#linter#Define('tex', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/tex/chktex.vim b/ale_linters/tex/chktex.vim
index 95fd2ba..7f1b0c7 100644
--- a/ale_linters/tex/chktex.vim
+++ b/ale_linters/tex/chktex.vim
@@ -8,49 +8,42 @@ let g:ale_tex_chktex_options =
\ get(g:, 'ale_tex_chktex_options', '-I')
function! ale_linters#tex#chktex#GetCommand(buffer) abort
- " Check for optional .chktexrc
- let l:chktex_config = ale#util#FindNearestFile(
- \ a:buffer,
- \ '.chktexrc')
+ " Check for optional .chktexrc
+ let l:chktex_config = ale#path#FindNearestFile(
+ \ a:buffer,
+ \ '.chktexrc')
- let l:command = g:ale_tex_chktex_executable
- " Avoid bug when used without -p (last warning has gibberish for a filename)
- let l:command .= ' -v0 -p stdin -q'
+ let l:command = ale#Var(a:buffer, 'tex_chktex_executable')
+ " Avoid bug when used without -p (last warning has gibberish for a filename)
+ let l:command .= ' -v0 -p stdin -q'
- if !empty(l:chktex_config)
- let l:command .= ' -l ' . fnameescape(l:chktex_config)
- endif
+ if !empty(l:chktex_config)
+ let l:command .= ' -l ' . ale#Escape(l:chktex_config)
+ endif
- let l:command .= ' ' . g:ale_tex_chktex_options
+ let l:command .= ' ' . ale#Var(a:buffer, 'tex_chktex_options')
- return l:command
+ return l:command
endfunction
function! ale_linters#tex#chktex#Handle(buffer, lines) abort
- " Mattes lines like:
- "
- " stdin:499:2:24:Delete this space to maintain correct pagereferences.
- " stdin:507:81:3:You should enclose the previous parenthesis with `{}'.
- let l:pattern = '^stdin:\(\d\+\):\(\d\+\):\(\d\+\):\(.\+\)$'
- let l:output = []
+ " Mattes lines like:
+ "
+ " stdin:499:2:24:Delete this space to maintain correct pagereferences.
+ " stdin:507:81:3:You should enclose the previous parenthesis with `{}'.
+ let l:pattern = '^stdin:\(\d\+\):\(\d\+\):\(\d\+\):\(.\+\)$'
+ let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[4] . ' (' . (l:match[3]+0) . ')',
+ \ 'type': 'W',
+ \})
+ endfor
- if len(l:match) == 0
- continue
- endif
-
- call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:match[1] + 0,
- \ 'col': l:match[2] + 0,
- \ 'text': l:match[4] . ' (' . (l:match[3]+0) . ')',
- \ 'type': 'W',
- \})
- endfor
-
- return l:output
+ return l:output
endfunction
call ale#linter#Define('tex', {
diff --git a/ale_linters/tex/lacheck.vim b/ale_linters/tex/lacheck.vim
index 94b79e6..e5a9632 100644
--- a/ale_linters/tex/lacheck.vim
+++ b/ale_linters/tex/lacheck.vim
@@ -5,46 +5,38 @@ 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
+ return ale#Var(a:buffer, 'tex_lacheck_executable')
endfunction
function! ale_linters#tex#lacheck#GetCommand(buffer) abort
- return g:ale_tex_lacheck_executable . ' %t'
+ return ale#Var(a:buffer, 'tex_lacheck_executable') . ' %t'
endfunction
function! ale_linters#tex#lacheck#Handle(buffer, lines) abort
- " Mattes lines like:
- "
- " "book.tex", line 37: possible unwanted space at "{"
- " "book.tex", line 38: missing `\ ' after "etc."
+ " Mattes lines like:
+ "
+ " "book.tex", line 37: possible unwanted space at "{"
+ " "book.tex", line 38: missing `\ ' after "etc."
- let l:pattern = '^".\+", line \(\d\+\): \(.\+\)$'
- let l:output = []
+ let l:pattern = '^".\+", line \(\d\+\): \(.\+\)$'
+ let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ " lacheck follows `\input{}` commands. If the cwd is not the same as the
+ " file in the buffer then it will fail to find the inputed items. We do not
+ " want warnings from those items anyway
+ if !empty(matchstr(l:match[2], '^Could not open ".\+"$'))
+ continue
+ endif
- if len(l:match) == 0
- continue
- endif
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'text': l:match[2],
+ \ 'type': 'W',
+ \})
+ endfor
- " lacheck follows `\input{}` commands. If the cwd is not the same as the
- " file in the buffer then it will fail to find the inputed items. We do not
- " want warnings from those items anyway
- if !empty(matchstr(l:match[2], '^Could not open ".\+"$'))
- 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
+ return l:output
endfunction
call ale#linter#Define('tex', {
diff --git a/ale_linters/tex/proselint.vim b/ale_linters/tex/proselint.vim
index b97fb3e..35e764e 100644
--- a/ale_linters/tex/proselint.vim
+++ b/ale_linters/tex/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('tex', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/tex/redpen.vim b/ale_linters/tex/redpen.vim
new file mode 100644
index 0000000..952a600
--- /dev/null
+++ b/ale_linters/tex/redpen.vim
@@ -0,0 +1,9 @@
+" Author: rhysd https://rhysd.github.io
+" Description: Redpen, a proofreading tool (http://redpen.cc)
+
+call ale#linter#Define('tex', {
+\ 'name': 'redpen',
+\ 'executable': 'redpen',
+\ 'command': 'redpen -f latex -r json %t',
+\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput',
+\})
diff --git a/ale_linters/tex/vale.vim b/ale_linters/tex/vale.vim
new file mode 100644
index 0000000..f64e72a
--- /dev/null
+++ b/ale_linters/tex/vale.vim
@@ -0,0 +1,9 @@
+" Author: chew-z https://github.com/chew-z
+" Description: vale for LaTeX files
+
+call ale#linter#Define('tex', {
+\ 'name': 'vale',
+\ 'executable': 'vale',
+\ 'command': 'vale --output=JSON %t',
+\ 'callback': 'ale#handlers#vale#Handle',
+\})
diff --git a/ale_linters/tex/write-good.vim b/ale_linters/tex/write-good.vim
new file mode 100644
index 0000000..dc59de2
--- /dev/null
+++ b/ale_linters/tex/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for TeX files
+
+call ale#linter#Define('tex', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/texinfo/alex.vim b/ale_linters/texinfo/alex.vim
new file mode 100644
index 0000000..4a88457
--- /dev/null
+++ b/ale_linters/texinfo/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for texinfo files
+
+call ale#linter#Define('texinfo', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/texinfo/proselint.vim b/ale_linters/texinfo/proselint.vim
index 7b26514..003e3a0 100644
--- a/ale_linters/texinfo/proselint.vim
+++ b/ale_linters/texinfo/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('texinfo', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/texinfo/write-good.vim b/ale_linters/texinfo/write-good.vim
new file mode 100644
index 0000000..8104c63
--- /dev/null
+++ b/ale_linters/texinfo/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for Texinfo files
+
+call ale#linter#Define('texinfo', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/text/alex.vim b/ale_linters/text/alex.vim
new file mode 100644
index 0000000..c696367
--- /dev/null
+++ b/ale_linters/text/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for text files
+
+call ale#linter#Define('text', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/text/proselint.vim b/ale_linters/text/proselint.vim
index c1b81e9..281b4ff 100644
--- a/ale_linters/text/proselint.vim
+++ b/ale_linters/text/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('text', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/text/redpen.vim b/ale_linters/text/redpen.vim
new file mode 100644
index 0000000..ec4433b
--- /dev/null
+++ b/ale_linters/text/redpen.vim
@@ -0,0 +1,9 @@
+" Author: rhysd https://rhysd.github.io
+" Description: Redpen, a proofreading tool (http://redpen.cc)
+
+call ale#linter#Define('text', {
+\ 'name': 'redpen',
+\ 'executable': 'redpen',
+\ 'command': 'redpen -f plain -r json %t',
+\ 'callback': 'ale#handlers#redpen#HandleRedpenOutput',
+\})
diff --git a/ale_linters/text/vale.vim b/ale_linters/text/vale.vim
new file mode 100644
index 0000000..cf37c2f
--- /dev/null
+++ b/ale_linters/text/vale.vim
@@ -0,0 +1,9 @@
+" Author: chew-z https://github.com/chew-z
+" Description: vale for text files
+
+call ale#linter#Define('text', {
+\ 'name': 'vale',
+\ 'executable': 'vale',
+\ 'command': 'vale --output=JSON %t',
+\ 'callback': 'ale#handlers#vale#Handle',
+\})
diff --git a/ale_linters/text/write-good.vim b/ale_linters/text/write-good.vim
new file mode 100644
index 0000000..ff76ce4
--- /dev/null
+++ b/ale_linters/text/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for text files
+
+call ale#linter#Define('text', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/thrift/thrift.vim b/ale_linters/thrift/thrift.vim
new file mode 100644
index 0000000..2f62570
--- /dev/null
+++ b/ale_linters/thrift/thrift.vim
@@ -0,0 +1,91 @@
+" Author: Jon Parise
+
+call ale#Set('thrift_thrift_executable', 'thrift')
+call ale#Set('thrift_thrift_generators', ['cpp'])
+call ale#Set('thrift_thrift_includes', [])
+call ale#Set('thrift_thrift_options', '-strict')
+
+function! ale_linters#thrift#thrift#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'thrift_thrift_executable')
+endfunction
+
+function! ale_linters#thrift#thrift#GetCommand(buffer) abort
+ let l:generators = ale#Var(a:buffer, 'thrift_thrift_generators')
+ let l:includes = ale#Var(a:buffer, 'thrift_thrift_includes')
+
+ " The thrift compiler requires at least one generator. If none are set,
+ " fall back to our default value to avoid silently failing. We could also
+ " `throw` here, but that seems even less helpful.
+ if empty(l:generators)
+ let l:generators = ['cpp']
+ endif
+
+ let l:output_dir = tempname()
+ call mkdir(l:output_dir)
+ call ale#engine#ManageDirectory(a:buffer, l:output_dir)
+
+ return ale#Escape(ale_linters#thrift#thrift#GetExecutable(a:buffer))
+ \ . ' ' . join(map(copy(l:generators), "'--gen ' . v:val"))
+ \ . ' ' . join(map(copy(l:includes), "'-I ' . v:val"))
+ \ . ' ' . ale#Var(a:buffer, 'thrift_thrift_options')
+ \ . ' -out ' . ale#Escape(l:output_dir)
+ \ . ' %t'
+endfunction
+
+function! ale_linters#thrift#thrift#Handle(buffer, lines) abort
+ " Matches lines like the following:
+ "
+ " [SEVERITY:/path/filename.thrift:31] Message text
+ " [ERROR:/path/filename.thrift:31] (last token was ';')
+ let l:pattern = '\v^\[(\u+):(.*):(\d+)\] (.*)$'
+
+ let l:index = 0
+ let l:output = []
+
+ " Roll our own output-matching loop instead of using ale#util#GetMatches
+ " because we need to support error messages that span multiple lines.
+ while l:index < len(a:lines)
+ let l:line = a:lines[l:index]
+
+ let l:match = matchlist(l:line, l:pattern)
+ if empty(l:match)
+ let l:index += 1
+ continue
+ endif
+
+ let l:severity = l:match[1]
+ if l:severity is# 'WARNING'
+ let l:type = 'W'
+ else
+ let l:type = 'E'
+ endif
+
+ " If our text looks like "(last token was ';')", the *next* line
+ " should contain a more descriptive error message.
+ let l:text = l:match[4]
+ if l:text =~# '\(last token was .*\)'
+ let l:index += 1
+ let l:text = get(a:lines, l:index, 'Unknown error ' . l:text)
+ endif
+
+ call add(l:output, {
+ \ 'lnum': l:match[3] + 0,
+ \ 'col': 0,
+ \ 'type': l:type,
+ \ 'text': l:text,
+ \})
+
+ let l:index += 1
+ endwhile
+
+ return l:output
+endfunction
+
+call ale#linter#Define('thrift', {
+\ 'name': 'thrift',
+\ 'executable': 'thrift',
+\ 'output_stream': 'both',
+\ 'executable_callback': 'ale_linters#thrift#thrift#GetExecutable',
+\ 'command_callback': 'ale_linters#thrift#thrift#GetCommand',
+\ 'callback': 'ale_linters#thrift#thrift#Handle',
+\})
diff --git a/ale_linters/typescript/eslint.vim b/ale_linters/typescript/eslint.vim
new file mode 100644
index 0000000..f1ae54e
--- /dev/null
+++ b/ale_linters/typescript/eslint.vim
@@ -0,0 +1,9 @@
+" Author: w0rp
+" Description: eslint for JavaScript files
+
+call ale#linter#Define('typescript', {
+\ 'name': 'eslint',
+\ 'executable_callback': 'ale#handlers#eslint#GetExecutable',
+\ 'command_callback': 'ale#handlers#eslint#GetCommand',
+\ 'callback': 'ale#handlers#eslint#Handle',
+\})
diff --git a/ale_linters/typescript/tslint.vim b/ale_linters/typescript/tslint.vim
index 8eeb98d..f4b4816 100644
--- a/ale_linters/typescript/tslint.vim
+++ b/ale_linters/typescript/tslint.vim
@@ -1,74 +1,85 @@
-" Author: Prashanth Chandra https://github.com/prashcr
+" Author: Prashanth Chandra , Jonathan Clem
" 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', '')
+call ale#Set('typescript_tslint_executable', 'tslint')
+call ale#Set('typescript_tslint_config_path', '')
+call ale#Set('typescript_tslint_rules_dir', '')
+call ale#Set('typescript_tslint_use_global', 0)
+call ale#Set('typescript_tslint_ignore_empty_files', 0)
function! ale_linters#typescript#tslint#GetExecutable(buffer) abort
- return ale#util#ResolveLocalPath(
- \ a:buffer,
- \ 'node_modules/.bin/tslint',
- \ g:ale_typescript_tslint_executable
- \)
+ return ale#node#FindExecutable(a:buffer, 'typescript_tslint', [
+ \ 'node_modules/.bin/tslint',
+ \])
endfunction
function! ale_linters#typescript#tslint#Handle(buffer, lines) abort
- " Matches patterns like the following:
- "
- " hello.ts[7, 41]: trailing whitespace
- " hello.ts[5, 1]: Forbidden 'var' keyword, use 'let' or 'const' instead
- "
- let l:ext = '.' . fnamemodify(bufname(a:buffer), ':e')
- let l:pattern = '.\+' . l:ext . '\[\(\d\+\), \(\d\+\)\]: \(.\+\)'
+ " Do not output any errors for empty files if the option is on.
+ if ale#Var(a:buffer, 'typescript_tslint_ignore_empty_files')
+ \&& getbufline(a:buffer, 1, '$') == ['']
+ return []
+ endif
+
+ let l:dir = expand('#' . a:buffer . ':p:h')
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
+ for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
+ if get(l:error, 'ruleName', '') is# 'no-implicit-dependencies'
continue
endif
- let l:line = l:match[1] + 0
- let l:column = l:match[2] + 0
- let l:type = 'E'
- let l:text = l:match[3]
+ let l:item = {
+ \ 'type': (get(l:error, 'ruleSeverity', '') is# 'WARNING' ? 'W' : 'E'),
+ \ 'text': l:error.failure,
+ \ 'lnum': l:error.startPosition.line + 1,
+ \ 'col': l:error.startPosition.character + 1,
+ \ 'end_lnum': l:error.endPosition.line + 1,
+ \ 'end_col': l:error.endPosition.character + 1,
+ \}
- " vcol is Needed to indicate that the column is a character.
- call add(l:output, {
- \ 'bufnr': a:buffer,
- \ 'lnum': l:line,
- \ 'col': l:column,
- \ 'text': l:text,
- \ 'type': l:type,
- \})
+ let l:filename = ale#path#GetAbsPath(l:dir, l:error.name)
+
+ " Assume temporary files are this file.
+ if !ale#path#IsTempName(l:filename)
+ let l:item.filename = l:filename
+ endif
+
+ if has_key(l:error, 'ruleName')
+ let l:item.code = l:error.ruleName
+ endif
+
+ call add(l:output, l:item)
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#GetCommand(buffer) abort
+ let l:tslint_config_path = ale#path#ResolveLocalPath(
+ \ a:buffer,
+ \ 'tslint.json',
+ \ ale#Var(a:buffer, 'typescript_tslint_config_path')
+ \)
+ let l:tslint_config_option = !empty(l:tslint_config_path)
+ \ ? ' -c ' . ale#Escape(l:tslint_config_path)
+ \ : ''
- let l:tslint_options =
- \ empty(g:ale_typescript_tslint_config_path) ?
- \ ''
- \ : '-c ' . fnameescape(g:ale_typescript_tslint_config_path)
+ let l:tslint_rules_dir = ale#Var(a:buffer, 'typescript_tslint_rules_dir')
+ let l:tslint_rules_option = !empty(l:tslint_rules_dir)
+ \ ? ' -r ' . ale#Escape(l:tslint_rules_dir)
+ \ : ''
- return ale_linters#typescript#tslint#GetExecutable(a:buffer)
- \ . ' ' . l:tslint_options
- \ . ' %t'
+ return ale#path#BufferCdString(a:buffer)
+ \ . ale_linters#typescript#tslint#GetExecutable(a:buffer)
+ \ . ' --format json'
+ \ . l:tslint_config_option
+ \ . l:tslint_rules_option
+ \ . ' %t'
endfunction
call ale#linter#Define('typescript', {
\ 'name': 'tslint',
\ 'executable_callback': 'ale_linters#typescript#tslint#GetExecutable',
-\ 'command_callback': 'ale_linters#typescript#tslint#BuildLintCommand',
+\ 'command_callback': 'ale_linters#typescript#tslint#GetCommand',
\ 'callback': 'ale_linters#typescript#tslint#Handle',
\})
diff --git a/ale_linters/typescript/tsserver.vim b/ale_linters/typescript/tsserver.vim
new file mode 100644
index 0000000..7a155bd
--- /dev/null
+++ b/ale_linters/typescript/tsserver.vim
@@ -0,0 +1,30 @@
+" Author: w0rp
+" Description: tsserver integration for ALE
+
+call ale#Set('typescript_tsserver_executable', 'tsserver')
+call ale#Set('typescript_tsserver_config_path', '')
+call ale#Set('typescript_tsserver_use_global', 0)
+
+" These functions need to be defined just to comply with the API for LSP.
+function! ale_linters#typescript#tsserver#GetProjectRoot(buffer) abort
+ return ''
+endfunction
+
+function! ale_linters#typescript#tsserver#GetLanguage(buffer) abort
+ return ''
+endfunction
+
+function! ale_linters#typescript#tsserver#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'typescript_tsserver', [
+ \ 'node_modules/.bin/tsserver',
+ \])
+endfunction
+
+call ale#linter#Define('typescript', {
+\ 'name': 'tsserver',
+\ 'lsp': 'tsserver',
+\ 'executable_callback': 'ale_linters#typescript#tsserver#GetExecutable',
+\ 'command_callback': 'ale_linters#typescript#tsserver#GetExecutable',
+\ 'project_root_callback': 'ale_linters#typescript#tsserver#GetProjectRoot',
+\ 'language_callback': 'ale_linters#typescript#tsserver#GetLanguage',
+\})
diff --git a/ale_linters/typescript/typecheck.vim b/ale_linters/typescript/typecheck.vim
index c5ba05f..2f18691 100644
--- a/ale_linters/typescript/typecheck.vim
+++ b/ale_linters/typescript/typecheck.vim
@@ -10,25 +10,15 @@ function! ale_linters#typescript#typecheck#Handle(buffer, lines) abort
let l:pattern = '.\+\.ts\[\(\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
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0
let l:column = l:match[2] + 0
- 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,
\ 'col': l:column,
\ 'text': l:text,
- \ 'type': l:type,
\})
endfor
diff --git a/ale_linters/verilog/iverilog.vim b/ale_linters/verilog/iverilog.vim
index 0a118f3..c64a3be 100644
--- a/ale_linters/verilog/iverilog.vim
+++ b/ale_linters/verilog/iverilog.vim
@@ -1,6 +1,14 @@
" Author: Masahiro H https://github.com/mshr-h
" Description: iverilog for verilog files
+call ale#Set('verilog_iverilog_options', '')
+
+function! ale_linters#verilog#iverilog#GetCommand(buffer) abort
+ return 'iverilog -t null -Wall '
+ \ . ale#Var(a:buffer, 'verilog_iverilog_options')
+ \ . ' %t'
+endfunction
+
function! ale_linters#verilog#iverilog#Handle(buffer, lines) abort
" Look for lines like the following.
"
@@ -11,21 +19,13 @@ function! ale_linters#verilog#iverilog#Handle(buffer, lines) abort
let l:pattern = '^[^:]\+:\(\d\+\): \(warning\|error\|syntax error\)\(: \(.\+\)\)\?'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
- endif
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0
let l:type = l:match[2] =~# 'error' ? 'E' : 'W'
- let l:text = l:match[2] ==# 'syntax error' ? 'syntax error' : l:match[4]
+ let l:text = l:match[2] is# 'syntax error' ? 'syntax error' : l:match[4]
call add(l:output, {
- \ 'bufnr': a:buffer,
\ 'lnum': l:line,
- \ 'col': 1,
\ 'text': l:text,
\ 'type': l:type,
\})
@@ -38,6 +38,6 @@ call ale#linter#Define('verilog', {
\ 'name': 'iverilog',
\ 'output_stream': 'stderr',
\ 'executable': 'iverilog',
-\ 'command': 'iverilog -t null -Wall %t',
+\ 'command_callback': 'ale_linters#verilog#iverilog#GetCommand',
\ 'callback': 'ale_linters#verilog#iverilog#Handle',
\})
diff --git a/ale_linters/verilog/verilator.vim b/ale_linters/verilog/verilator.vim
index b134438..6053da0 100644
--- a/ale_linters/verilog/verilator.vim
+++ b/ale_linters/verilog/verilator.vim
@@ -1,14 +1,22 @@
" Author: Masahiro H https://github.com/mshr-h
" Description: verilator for verilog files
+" Set this option to change Verilator lint options
+if !exists('g:ale_verilog_verilator_options')
+ let g:ale_verilog_verilator_options = ''
+endif
+
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)
+ let l:lines = getbufline(a:buffer, 1, '$')
+ call ale#util#Writefile(a:buffer, l:lines, l:filename)
- return 'verilator --lint-only -Wall -Wno-DECLFILENAME ' . fnameescape(l:filename)
+ return 'verilator --lint-only -Wall -Wno-DECLFILENAME '
+ \ . ale#Var(a:buffer, 'verilog_verilator_options') .' '
+ \ . ale#Escape(l:filename)
endfunction
function! ale_linters#verilog#verilator#Handle(buffer, lines) abort
@@ -23,23 +31,15 @@ function! ale_linters#verilog#verilator#Handle(buffer, lines) abort
let l:pattern = '^%\(Warning\|Error\)[^:]*:\([^:]\+\):\(\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
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[3] + 0
- let l:type = l:match[1] ==# 'Error' ? 'E' : 'W'
+ let l:type = l:match[1] is# 'Error' ? 'E' : 'W'
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,
- \ 'col': 1,
\ 'text': l:text,
\ 'type': l:type,
\})
diff --git a/ale_linters/vim/vint.vim b/ale_linters/vim/vint.vim
index fb177b9..dfa00dc 100644
--- a/ale_linters/vim/vint.vim
+++ b/ale_linters/vim/vint.vim
@@ -4,21 +4,64 @@
" This flag can be used to change enable/disable style issues.
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, [3, 0, 7])
let s:enable_neovim = has('nvim') ? ' --enable-neovim ' : ''
let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})"'
+function! ale_linters#vim#vint#VersionCommand(buffer) abort
+ " Check the Vint version if we haven't checked it already.
+ return !ale#semver#HasVersion('vint')
+ \ ? 'vint --version'
+ \ : ''
+endfunction
+
+function! ale_linters#vim#vint#GetCommand(buffer, version_output) abort
+ let l:version = ale#semver#GetVersion('vint', a:version_output)
+
+ let l:can_use_no_color_flag = empty(l:version)
+ \ || ale#semver#GTE(l:version, [0, 3, 7])
+
+ let l:warning_flag = ale#Var(a:buffer, 'vim_vint_show_style_issues') ? '-s' : '-w'
+
+ return 'vint '
+ \ . l:warning_flag . ' '
+ \ . (l:can_use_no_color_flag ? '--no-color ' : '')
+ \ . s:enable_neovim
+ \ . s:format
+ \ . ' %t'
+endfunction
+
+let s:word_regex_list = [
+\ '\v^Undefined variable: ([^ ]+)',
+\ '\v^Make the scope explicit like ...([^ ]+). ',
+\ '\v^.*start with a capital or contain a colon: ([^ ]+)',
+\ '\v.*instead of .(\=[=~]).',
+\]
+
+function! ale_linters#vim#vint#Handle(buffer, lines) abort
+ let l:loclist = ale#handlers#gcc#HandleGCCFormat(a:buffer, a:lines)
+
+ for l:item in l:loclist
+ let l:match = []
+
+ for l:regex in s:word_regex_list
+ let l:match = matchlist(l:item.text, l:regex)
+
+ if !empty(l:match)
+ let l:item.end_col = l:item.col + len(l:match[1]) - 1
+ break
+ endif
+ endfor
+ endfor
+
+ return l:loclist
+endfunction
+
call ale#linter#Define('vim', {
\ 'name': 'vint',
\ 'executable': 'vint',
-\ 'command': 'vint '
-\ . s:warning_flag . ' '
-\ . (s:has_no_color_support ? '--no-color ' : '')
-\ . s:enable_neovim
-\ . s:format
-\ . ' %t',
-\ 'callback': 'ale#handlers#HandleGCCFormat',
+\ 'command_chain': [
+\ {'callback': 'ale_linters#vim#vint#VersionCommand', 'output_stream': 'stderr'},
+\ {'callback': 'ale_linters#vim#vint#GetCommand', 'output_stream': 'stdout'},
+\ ],
+\ 'callback': 'ale_linters#vim#vint#Handle',
\})
diff --git a/ale_linters/xhtml/alex.vim b/ale_linters/xhtml/alex.vim
new file mode 100644
index 0000000..60a9a7c
--- /dev/null
+++ b/ale_linters/xhtml/alex.vim
@@ -0,0 +1,11 @@
+" Author: Johannes Wienke
+" Description: alex for XHTML files
+
+call ale#linter#Define('xhtml', {
+\ 'name': 'alex',
+\ 'executable': 'alex',
+\ 'command': 'alex %s -t',
+\ 'output_stream': 'stderr',
+\ 'callback': 'ale#handlers#alex#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/ale_linters/xhtml/proselint.vim b/ale_linters/xhtml/proselint.vim
index 07f7a29..dfad921 100644
--- a/ale_linters/xhtml/proselint.vim
+++ b/ale_linters/xhtml/proselint.vim
@@ -5,5 +5,5 @@ call ale#linter#Define('xhtml', {
\ 'name': 'proselint',
\ 'executable': 'proselint',
\ 'command': 'proselint %t',
-\ 'callback': 'ale#handlers#HandleUnixFormatAsWarning',
+\ 'callback': 'ale#handlers#unix#HandleAsWarning',
\})
diff --git a/ale_linters/xhtml/write-good.vim b/ale_linters/xhtml/write-good.vim
new file mode 100644
index 0000000..83d1863
--- /dev/null
+++ b/ale_linters/xhtml/write-good.vim
@@ -0,0 +1,9 @@
+" Author: Sumner Evans
+" Description: write-good for XHTML files
+
+call ale#linter#Define('xhtml', {
+\ 'name': 'write-good',
+\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
+\ 'command_callback': 'ale#handlers#writegood#GetCommand',
+\ 'callback': 'ale#handlers#writegood#Handle',
+\})
diff --git a/ale_linters/xml/xmllint.vim b/ale_linters/xml/xmllint.vim
new file mode 100644
index 0000000..63d7f76
--- /dev/null
+++ b/ale_linters/xml/xmllint.vim
@@ -0,0 +1,69 @@
+" Author: q12321q
+" Description: This file adds support for checking XML code with xmllint.
+
+" CLI options
+let g:ale_xml_xmllint_executable = get(g:, 'ale_xml_xmllint_executable', 'xmllint')
+let g:ale_xml_xmllint_options = get(g:, 'ale_xml_xmllint_options', '')
+
+function! ale_linters#xml#xmllint#GetExecutable(buffer) abort
+ return ale#Var(a:buffer, 'xml_xmllint_executable')
+endfunction
+
+function! ale_linters#xml#xmllint#GetCommand(buffer) abort
+ return ale#Escape(ale_linters#xml#xmllint#GetExecutable(a:buffer))
+ \ . ' ' . ale#Var(a:buffer, 'xml_xmllint_options')
+ \ . ' --noout -'
+endfunction
+
+function! ale_linters#xml#xmllint#Handle(buffer, lines) abort
+ " Matches patterns lines like the following:
+ " file/path:123: error level : error message
+ let l:pattern_message = '\v^([^:]+):(\d+):\s*(([^:]+)\s*:\s+.*)$'
+
+ " parse column token line like that:
+ " file/path:123: parser error : Opening and ending tag mismatch: foo line 1 and bar
+ "
+ " ^
+ let l:pattern_column_token = '\v^\s*\^$'
+
+ let l:output = []
+
+ for l:line in a:lines
+
+ " Parse error/warning lines
+ let l:match_message = matchlist(l:line, l:pattern_message)
+ if !empty(l:match_message)
+ let l:line = l:match_message[2] + 0
+ let l:type = l:match_message[4] =~? 'warning' ? 'W' : 'E'
+ let l:text = l:match_message[3]
+
+ call add(l:output, {
+ \ 'lnum': l:line,
+ \ 'text': l:text,
+ \ 'type': l:type,
+ \})
+
+ continue
+ endif
+
+ " Parse column position
+ let l:match_column_token = matchlist(l:line, l:pattern_column_token)
+ if !empty(l:output) && !empty(l:match_column_token)
+ let l:previous = l:output[len(l:output) - 1]
+ let l:previous['col'] = len(l:match_column_token[0])
+
+ continue
+ endif
+
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('xml', {
+\ 'name': 'xmllint',
+\ 'output_stream': 'stderr',
+\ 'executable_callback': 'ale_linters#xml#xmllint#GetExecutable',
+\ 'command_callback': 'ale_linters#xml#xmllint#GetCommand',
+\ 'callback': 'ale_linters#xml#xmllint#Handle',
+\ })
diff --git a/ale_linters/yaml/swaglint.vim b/ale_linters/yaml/swaglint.vim
new file mode 100644
index 0000000..75a496c
--- /dev/null
+++ b/ale_linters/yaml/swaglint.vim
@@ -0,0 +1,49 @@
+" Author: Matthew Turland
+" Description: This file adds support for linting Swagger / OpenAPI documents using swaglint
+
+call ale#Set('yaml_swaglint_executable', 'swaglint')
+call ale#Set('yaml_swaglint_use_global', 0)
+
+function! ale_linters#yaml#swaglint#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'yaml_swaglint', [
+ \ 'node_modules/.bin/swaglint',
+ \])
+endfunction
+
+function! ale_linters#yaml#swaglint#GetCommand(buffer) abort
+ return ale_linters#yaml#swaglint#GetExecutable(a:buffer)
+ \ . ' -r compact --stdin'
+endfunction
+
+function! ale_linters#yaml#swaglint#Handle(buffer, lines) abort
+ let l:pattern = ': \([^\s]\+\) @ \(\d\+\):\(\d\+\) - \(.\+\)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:obj = {
+ \ 'type': l:match[1] is# 'error' ? 'E' : 'W',
+ \ 'lnum': l:match[2] + 0,
+ \ 'col': l:match[3] + 0,
+ \ 'text': l:match[4],
+ \}
+
+ " Parse the code if it's there.
+ let l:code_match = matchlist(l:obj.text, '\v^(.+) \(([^ (]+)\)$')
+
+ if !empty(l:code_match)
+ let l:obj.text = l:code_match[1]
+ let l:obj.code = l:code_match[2]
+ endif
+
+ call add(l:output, l:obj)
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('yaml', {
+\ 'name': 'swaglint',
+\ 'executable_callback': 'ale_linters#yaml#swaglint#GetExecutable',
+\ 'command_callback': 'ale_linters#yaml#swaglint#GetCommand',
+\ 'callback': 'ale_linters#yaml#swaglint#Handle',
+\})
diff --git a/ale_linters/yaml/yamllint.vim b/ale_linters/yaml/yamllint.vim
index dcad70c..731f801 100644
--- a/ale_linters/yaml/yamllint.vim
+++ b/ale_linters/yaml/yamllint.vim
@@ -7,12 +7,12 @@ 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
+ return ale#Var(a:buffer, '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
+ \ . ' ' . ale#Var(a:buffer, 'yaml_yamllint_options')
\ . ' -f parsable %t'
endfunction
@@ -23,25 +23,17 @@ function! ale_linters#yaml#yamllint#Handle(buffer, lines) abort
let l:pattern = '^.*:\(\d\+\):\(\d\+\): \[\(error\|warning\)\] \(.\+\)$'
let l:output = []
- for l:line in a:lines
- let l:match = matchlist(l:line, l:pattern)
-
- if len(l:match) == 0
- continue
- endif
-
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0
let l:col = l:match[2] + 0
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,
\ 'col': l:col,
\ 'text': l:text,
- \ 'type': l:type ==# 'error' ? 'E' : 'W',
+ \ 'type': l:type is# 'error' ? 'E' : 'W',
\})
endfor
diff --git a/autoload/ale.vim b/autoload/ale.vim
index 830a281..f6c06cf 100644
--- a/autoload/ale.vim
+++ b/autoload/ale.vim
@@ -1,40 +1,130 @@
-" Author: w0rp
+" Author: w0rp , David Alexander
" Description: Primary code path for the plugin
" Manages execution of linters when requested by autocommands
let s:lint_timer = -1
+let s:queued_buffer_number = -1
let s:should_lint_file_for_buffer = {}
+let s:error_delay_ms = 1000 * 60 * 2
+
+let s:timestamp_map = {}
+
+" Given a key for a script variable for tracking the time to wait until
+" a given function should be called, a funcref for a function to call, and
+" a List of arguments, call the function and return whatever value it returns.
+"
+" If the function throws an exception, then the function will not be called
+" for a while, and 0 will be returned instead.
+function! ale#CallWithCooldown(timestamp_key, func, arglist) abort
+ let l:now = ale#util#ClockMilliseconds()
+
+ if l:now < get(s:timestamp_map, a:timestamp_key, -1)
+ return 0
+ endif
+
+ let s:timestamp_map[a:timestamp_key] = l:now + s:error_delay_ms
+
+ let l:return_value = call(a:func, a:arglist)
+
+ let s:timestamp_map[a:timestamp_key] = -1
+
+ return l:return_value
+endfunction
+
+" Return 1 if a file is too large for ALE to handle.
+function! ale#FileTooLarge() abort
+ let l:max = ale#Var(bufnr(''), 'maximum_file_size')
+
+ return l:max > 0 ? (line2byte(line('$') + 1) > l:max) : 0
+endfunction
+
+let s:getcmdwintype_exists = exists('*getcmdwintype')
" 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
+function! ale#ShouldDoNothing(buffer) abort
+ " The checks are split into separate if statements to make it possible to
+ " profile each check individually with Vim's profiling tools.
+
+ " Don't perform any checks when newer NeoVim versions are exiting.
+ if get(v:, 'exiting', v:null) isnot v:null
+ return 1
+ endif
+
" Do nothing for blacklisted files
- " OR if ALE is running in the sandbox
- return index(g:ale_filetype_blacklist, &filetype) >= 0
- \ || ale#util#InSandbox()
+ if index(g:ale_filetype_blacklist, getbufvar(a:buffer, '&filetype')) >= 0
+ return 1
+ endif
+
+ " Do nothing if running from command mode
+ if s:getcmdwintype_exists && !empty(getcmdwintype())
+ return 1
+ endif
+
+ let l:filename = fnamemodify(bufname(a:buffer), ':t')
+
+ if l:filename is# '.'
+ return 1
+ endif
+
+ " Do nothing if running in the sandbox
+ if ale#util#InSandbox()
+ return 1
+ endif
+
+ " Do nothing if ALE is disabled.
+ if !ale#Var(a:buffer, 'enabled')
+ return 1
+ endif
+
+ " Do nothing if the file is too large.
+ if ale#FileTooLarge()
+ return 1
+ endif
+
+ " Do nothing from CtrlP buffers with CtrlP-funky.
+ if exists(':CtrlPFunky') is 2
+ \&& getbufvar(a:buffer, '&l:statusline') =~# 'CtrlPMode.*funky'
+ return 1
+ endif
+
+ return 0
endfunction
-" (delay, [linting_flag])
+" (delay, [linting_flag, buffer_number])
function! ale#Queue(delay, ...) abort
- if len(a:0) > 1
+ if a:0 > 2
throw 'too many arguments!'
endif
" Default linting_flag to ''
let l:linting_flag = get(a:000, 0, '')
+ let l:buffer = get(a:000, 1, bufnr(''))
- if l:linting_flag !=# '' && l:linting_flag !=# 'lint_file'
+ return ale#CallWithCooldown(
+ \ 'dont_queue_until',
+ \ function('s:ALEQueueImpl'),
+ \ [a:delay, l:linting_flag, l:buffer],
+ \)
+endfunction
+
+function! s:ALEQueueImpl(delay, linting_flag, buffer) abort
+ if a:linting_flag isnot# '' && a:linting_flag isnot# 'lint_file'
throw "linting_flag must be either '' or 'lint_file'"
endif
- if ale#ShouldDoNothing()
+ if type(a:buffer) != type(0)
+ throw 'buffer_number must be a Number'
+ endif
+
+ if ale#ShouldDoNothing(a:buffer)
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
+ if a:linting_flag is# 'lint_file'
+ let s:should_lint_file_for_buffer[a:buffer] = 1
endif
if s:lint_timer != -1
@@ -42,56 +132,156 @@ function! ale#Queue(delay, ...) abort
let s:lint_timer = -1
endif
- let l:linters = ale#linter#Get(&filetype)
- if len(l:linters) == 0
- " There are no linters to lint with, so stop here.
+ let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
+
+ " Don't set up buffer data and so on if there are no linters to run.
+ if empty(l:linters)
+ " If we have some previous buffer data, then stop any jobs currently
+ " running and clear everything.
+ if has_key(g:ale_buffer_info, a:buffer)
+ call ale#engine#RunLinters(a:buffer, [], 1)
+ endif
+
return
endif
if a:delay > 0
+ let s:queued_buffer_number = a:buffer
let s:lint_timer = timer_start(a:delay, function('ale#Lint'))
else
- call ale#Lint()
+ call ale#Lint(-1, a:buffer)
endif
endfunction
function! ale#Lint(...) abort
- if ale#ShouldDoNothing()
+ if a:0 > 1
+ " Use the buffer number given as the optional second argument.
+ let l:buffer = a:2
+ elseif a:0 > 0 && a:1 == s:lint_timer
+ " Use the buffer number for the buffer linting was queued for.
+ let l:buffer = s:queued_buffer_number
+ else
+ " Use the current buffer number.
+ let l:buffer = bufnr('')
+ endif
+
+ return ale#CallWithCooldown(
+ \ 'dont_lint_until',
+ \ function('s:ALELintImpl'),
+ \ [l:buffer],
+ \)
+endfunction
+
+function! s:ALELintImpl(buffer) abort
+ if ale#ShouldDoNothing(a:buffer)
return
endif
- let l:buffer = bufnr('%')
- let l:linters = ale#linter#Get(&filetype)
+ " Use the filetype from the buffer
+ let l:linters = ale#linter#Get(getbufvar(a:buffer, '&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
+ if has_key(s:should_lint_file_for_buffer, a:buffer)
+ unlet s:should_lint_file_for_buffer[a:buffer]
+ " Lint files if they exist.
+ let l:should_lint_file = filereadable(expand('#' . a:buffer . ':p'))
endif
- " Initialise the buffer information if needed.
- call ale#engine#InitBufferInfo(l:buffer)
-
- " 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
- call ale#engine#Invoke(l:buffer, l:linter)
- endfor
+ call ale#engine#RunLinters(a:buffer, l:linters, l:should_lint_file)
endfunction
" Reset flags indicating that files should be checked for all buffers.
function! ale#ResetLintFileMarkers() abort
let s:should_lint_file_for_buffer = {}
endfunction
+
+function! ale#ResetErrorDelays() abort
+ let s:timestamp_map = {}
+endfunction
+
+let g:ale_has_override = get(g:, 'ale_has_override', {})
+
+" Call has(), but check a global Dictionary so we can force flags on or off
+" for testing purposes.
+function! ale#Has(feature) abort
+ return get(g:ale_has_override, a:feature, has(a:feature))
+endfunction
+
+" Given a buffer number and a variable name, look for that variable in the
+" buffer scope, then in global scope. If the name does not exist in the global
+" scope, an exception will be thrown.
+"
+" Every variable name will be prefixed with 'ale_'.
+function! ale#Var(buffer, variable_name) abort
+ let l:nr = str2nr(a:buffer)
+ let l:full_name = 'ale_' . a:variable_name
+
+ if bufexists(l:nr)
+ let l:vars = getbufvar(l:nr, '')
+ elseif has_key(g:, 'ale_fix_buffer_data')
+ let l:vars = get(g:ale_fix_buffer_data, l:nr, {'vars': {}}).vars
+ else
+ let l:vars = {}
+ endif
+
+ return get(l:vars, l:full_name, g:[l:full_name])
+endfunction
+
+" Initialize a variable with a default value, if it isn't already set.
+"
+" Every variable name will be prefixed with 'ale_'.
+function! ale#Set(variable_name, default) abort
+ let l:full_name = 'ale_' . a:variable_name
+ let l:value = get(g:, l:full_name, a:default)
+ let g:[l:full_name] = l:value
+
+ return l:value
+endfunction
+
+" Escape a string suitably for each platform.
+" shellescape does not work on Windows.
+function! ale#Escape(str) abort
+ if fnamemodify(&shell, ':t') is? 'cmd.exe'
+ " If the string contains spaces, it will be surrounded by quotes.
+ " Otherwise, special characters will be escaped with carets (^).
+ return substitute(
+ \ a:str =~# ' '
+ \ ? '"' . substitute(a:str, '"', '""', 'g') . '"'
+ \ : substitute(a:str, '\v([&|<>^])', '^\1', 'g'),
+ \ '%',
+ \ '%%',
+ \ 'g',
+ \)
+ endif
+
+ return shellescape (a:str)
+endfunction
+
+" Get the loclist item message according to a given format string.
+"
+" See `:help g:ale_loclist_msg_format` and `:help g:ale_echo_msg_format`
+function! ale#GetLocItemMessage(item, format_string) abort
+ let l:msg = a:format_string
+ let l:severity = g:ale_echo_msg_warning_str
+ let l:code = get(a:item, 'code', '')
+ let l:type = get(a:item, 'type', 'E')
+ let l:linter_name = get(a:item, 'linter_name', '')
+ let l:code_repl = !empty(l:code) ? '\=submatch(1) . l:code . submatch(2)' : ''
+
+ if l:type is# 'E'
+ let l:severity = g:ale_echo_msg_error_str
+ elseif l:type is# 'I'
+ let l:severity = g:ale_echo_msg_info_str
+ endif
+
+ " Replace special markers with certain information.
+ " \=l:variable is used to avoid escaping issues.
+ let l:msg = substitute(l:msg, '\V%severity%', '\=l:severity', 'g')
+ let l:msg = substitute(l:msg, '\V%linter%', '\=l:linter_name', 'g')
+ let l:msg = substitute(l:msg, '\v\%([^\%]*)code([^\%]*)\%', l:code_repl, 'g')
+ " Replace %s with the text.
+ let l:msg = substitute(l:msg, '\V%s', '\=a:item.text', 'g')
+
+ return l:msg
+endfunction
diff --git a/autoload/ale/balloon.vim b/autoload/ale/balloon.vim
new file mode 100644
index 0000000..41fa95f
--- /dev/null
+++ b/autoload/ale/balloon.vim
@@ -0,0 +1,21 @@
+" Author: w0rp
+" Description: balloonexpr support for ALE.
+
+function! ale#balloon#MessageForPos(bufnr, lnum, col) abort
+ let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist
+ let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col)
+
+ return l:index >= 0 ? l:loclist[l:index].text : ''
+endfunction
+
+function! ale#balloon#Expr() abort
+ return ale#balloon#MessageForPos(v:beval_bufnr, v:beval_lnum, v:beval_col)
+endfunction
+
+function! ale#balloon#Disable() abort
+ set noballooneval
+endfunction
+
+function! ale#balloon#Enable() abort
+ set ballooneval balloonexpr=ale#balloon#Expr()
+endfunction
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
new file mode 100644
index 0000000..f6ad7de
--- /dev/null
+++ b/autoload/ale/c.vim
@@ -0,0 +1,93 @@
+" Author: gagbo , w0rp
+" Description: Functions for integrating with C-family linters.
+
+let s:sep = has('win32') ? '\' : '/'
+
+function! ale#c#FindProjectRoot(buffer) abort
+ for l:project_filename in ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
+ let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename)
+
+ if !empty(l:full_path)
+ let l:path = fnamemodify(l:full_path, ':h')
+
+ " Correct .git path detection.
+ if fnamemodify(l:path, ':t') is# '.git'
+ let l:path = fnamemodify(l:path, ':h')
+ endif
+
+ return l:path
+ endif
+ endfor
+
+ return ''
+endfunction
+
+" Given a buffer number, search for a project root, and output a List
+" of directories to include based on some heuristics.
+"
+" For projects with headers in the project root, the project root will
+" be returned.
+"
+" For projects with an 'include' directory, that directory will be returned.
+function! ale#c#FindLocalHeaderPaths(buffer) abort
+ let l:project_root = ale#c#FindProjectRoot(a:buffer)
+
+ if empty(l:project_root)
+ return []
+ endif
+
+ " See if we can find .h files directory in the project root.
+ " If we can, that's our include directory.
+ if !empty(globpath(l:project_root, '*.h', 0))
+ return [l:project_root]
+ endif
+
+ " Look for .hpp files too.
+ if !empty(globpath(l:project_root, '*.hpp', 0))
+ return [l:project_root]
+ endif
+
+ " If we find an 'include' directory in the project root, then use that.
+ if isdirectory(l:project_root . '/include')
+ return [ale#path#Simplify(l:project_root . s:sep . 'include')]
+ endif
+
+ return []
+endfunction
+
+" Given a List of include paths, create a string containing the -I include
+" options for those paths, with the paths escaped for use in the shell.
+function! ale#c#IncludeOptions(include_paths) abort
+ let l:option_list = []
+
+ for l:path in a:include_paths
+ call add(l:option_list, '-I' . ale#Escape(l:path))
+ endfor
+
+ if empty(l:option_list)
+ return ''
+ endif
+
+ return ' ' . join(l:option_list) . ' '
+endfunction
+
+let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [
+\ 'build',
+\ 'bin',
+\])
+
+" Given a buffer number, find the build subdirectory with compile commands
+" The subdirectory is returned without the trailing /
+function! ale#c#FindCompileCommands(buffer) abort
+ for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
+ for l:dirname in ale#Var(a:buffer, 'c_build_dir_names')
+ let l:c_build_dir = l:path . s:sep . l:dirname
+
+ if filereadable(l:c_build_dir . '/compile_commands.json')
+ return l:c_build_dir
+ endif
+ endfor
+ endfor
+
+ return ''
+endfunction
diff --git a/autoload/ale/cleanup.vim b/autoload/ale/cleanup.vim
deleted file mode 100644
index 3b0b1d9..0000000
--- a/autoload/ale/cleanup.vim
+++ /dev/null
@@ -1,20 +0,0 @@
-" Author: w0rp
-" Description: Utility functions related to cleaning state.
-
-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)
- endif
-
- call remove(g:ale_buffer_info, a:buffer)
- endif
-endfunction
diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim
new file mode 100644
index 0000000..558fe23
--- /dev/null
+++ b/autoload/ale/command.vim
@@ -0,0 +1,57 @@
+" Author: w0rp
+" Description: Special command formatting for creating temporary files and
+" passing buffer filenames easily.
+
+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, /
+ " 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#command#FormatCommand(buffer, command, pipe_file_if_needed) abort
+ let l:temporary_file = ''
+ 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, '%%', '<>', 'g')
+
+ " Replace all %s occurrences 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', '\=ale#Escape(l:filename)', 'g')
+ endif
+
+ if l:command =~# '%t'
+ " Create a temporary filename, /
+ " 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', '\=ale#Escape(l:temporary_file)', 'g')
+ endif
+
+ " Finish formatting so %% becomes %.
+ let l:command = substitute(l:command, '<>', '%', 'g')
+
+ if a:pipe_file_if_needed && 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(a:buffer)
+ let l:command = l:command . ' < ' . ale#Escape(l:temporary_file)
+ endif
+
+ return [l:temporary_file, l:command]
+endfunction
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
new file mode 100644
index 0000000..7ad7f9d
--- /dev/null
+++ b/autoload/ale/completion.vim
@@ -0,0 +1,477 @@
+" Author: w0rp
+" Description: Completion support for LSP linters
+
+let s:timer_id = -1
+let s:last_done_pos = []
+
+" CompletionItemKind values from the LSP protocol.
+let s:LSP_COMPLETION_TEXT_KIND = 1
+let s:LSP_COMPLETION_METHOD_KIND = 2
+let s:LSP_COMPLETION_FUNCTION_KIND = 3
+let s:LSP_COMPLETION_CONSTRUCTOR_KIND = 4
+let s:LSP_COMPLETION_FIELD_KIND = 5
+let s:LSP_COMPLETION_VARIABLE_KIND = 6
+let s:LSP_COMPLETION_CLASS_KIND = 7
+let s:LSP_COMPLETION_INTERFACE_KIND = 8
+let s:LSP_COMPLETION_MODULE_KIND = 9
+let s:LSP_COMPLETION_PROPERTY_KIND = 10
+let s:LSP_COMPLETION_UNIT_KIND = 11
+let s:LSP_COMPLETION_VALUE_KIND = 12
+let s:LSP_COMPLETION_ENUM_KIND = 13
+let s:LSP_COMPLETION_KEYWORD_KIND = 14
+let s:LSP_COMPLETION_SNIPPET_KIND = 15
+let s:LSP_COMPLETION_COLOR_KIND = 16
+let s:LSP_COMPLETION_FILE_KIND = 17
+let s:LSP_COMPLETION_REFERENCE_KIND = 18
+
+" Regular expressions for checking the characters in the line before where
+" the insert cursor is. If one of these matches, we'll check for completions.
+let s:should_complete_map = {
+\ '': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$',
+\}
+
+" Regular expressions for finding the start column to replace with completion.
+let s:omni_start_map = {
+\ '': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$',
+\}
+
+" A map of exact characters for triggering LSP completions.
+let s:trigger_character_map = {
+\ '': ['.'],
+\}
+
+function! s:GetFiletypeValue(map, filetype) abort
+ for l:part in reverse(split(a:filetype, '\.'))
+ let l:regex = get(a:map, l:part, [])
+
+ if !empty(l:regex)
+ return l:regex
+ endif
+ endfor
+
+ " Use the default regex for other files.
+ return a:map['']
+endfunction
+
+" Check if we should look for completions for a language.
+function! ale#completion#GetPrefix(filetype, line, column) abort
+ let l:regex = s:GetFiletypeValue(s:should_complete_map, a:filetype)
+ " The column we're using completions for is where we are inserting text,
+ " like so:
+ " abc
+ " ^
+ " So we need check the text in the column before that position.
+ return matchstr(getline(a:line)[: a:column - 2], l:regex)
+endfunction
+
+function! ale#completion#GetTriggerCharacter(filetype, prefix) abort
+ let l:char_list = s:GetFiletypeValue(s:trigger_character_map, a:filetype)
+
+ if index(l:char_list, a:prefix) >= 0
+ return a:prefix
+ endif
+
+ return ''
+endfunction
+
+function! ale#completion#Filter(suggestions, prefix) abort
+ " For completing...
+ " foo.
+ " ^
+ " We need to include all of the given suggestions.
+ if a:prefix is# '.'
+ return a:suggestions
+ endif
+
+ let l:filtered_suggestions = []
+
+ " Filter suggestions down to those starting with the prefix we used for
+ " finding suggestions in the first place.
+ "
+ " Some completion tools will include suggestions which don't even start
+ " with the characters we have already typed.
+ for l:item in a:suggestions
+ " A List of String values or a List of completion item Dictionaries
+ " is accepted here.
+ let l:word = type(l:item) == type('') ? l:item : l:item.word
+
+ " Add suggestions if the suggestion starts with a case-insensitive
+ " match for the prefix.
+ if l:word[: len(a:prefix) - 1] is? a:prefix
+ call add(l:filtered_suggestions, l:item)
+ endif
+ endfor
+
+ return l:filtered_suggestions
+endfunction
+
+function! s:ReplaceCompleteopt() abort
+ if !exists('b:ale_old_completopt')
+ let b:ale_old_completopt = &l:completeopt
+ endif
+
+ let &l:completeopt = 'menu,menuone,preview,noselect,noinsert'
+endfunction
+
+function! ale#completion#OmniFunc(findstart, base) abort
+ if a:findstart
+ let l:line = b:ale_completion_info.line
+ let l:column = b:ale_completion_info.column
+ let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype)
+ let l:up_to_column = getline(l:line)[: l:column - 2]
+ let l:match = matchstr(l:up_to_column, l:regex)
+
+ return l:column - len(l:match) - 1
+ else
+ " Parse a new response if there is one.
+ if exists('b:ale_completion_response')
+ \&& exists('b:ale_completion_parser')
+ let l:response = b:ale_completion_response
+ let l:parser = b:ale_completion_parser
+
+ unlet b:ale_completion_response
+ unlet b:ale_completion_parser
+
+ let b:ale_completion_result = function(l:parser)(l:response)
+ endif
+
+ call s:ReplaceCompleteopt()
+
+ return get(b:, 'ale_completion_result', [])
+ endif
+endfunction
+
+function! ale#completion#Show(response, completion_parser) abort
+ " Remember the old omnifunc value, if there is one.
+ " If we don't store an old one, we'll just never reset the option.
+ " This will stop some random exceptions from appearing.
+ if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
+ let b:ale_old_omnifunc = &l:omnifunc
+ endif
+
+ " Set the list in the buffer, temporarily replace omnifunc with our
+ " function, and then start omni-completion.
+ let b:ale_completion_response = a:response
+ let b:ale_completion_parser = a:completion_parser
+ let &l:omnifunc = 'ale#completion#OmniFunc'
+ call s:ReplaceCompleteopt()
+ call ale#util#FeedKeys("\\", 'n')
+endfunction
+
+function! s:CompletionStillValid(request_id) abort
+ let [l:line, l:column] = getcurpos()[1:2]
+
+ return has_key(b:, 'ale_completion_info')
+ \&& b:ale_completion_info.request_id == a:request_id
+ \&& b:ale_completion_info.line == l:line
+ \&& b:ale_completion_info.column == l:column
+endfunction
+
+function! ale#completion#ParseTSServerCompletions(response) abort
+ let l:names = []
+
+ for l:suggestion in a:response.body
+ call add(l:names, l:suggestion.name)
+ endfor
+
+ return l:names
+endfunction
+
+function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
+ let l:results = []
+
+ for l:suggestion in a:response.body
+ let l:displayParts = []
+
+ for l:part in l:suggestion.displayParts
+ call add(l:displayParts, l:part.text)
+ endfor
+
+ " Each one of these parts has 'kind' properties
+ let l:documentationParts = []
+
+ for l:part in get(l:suggestion, 'documentation', [])
+ call add(l:documentationParts, l:part.text)
+ endfor
+
+ if l:suggestion.kind is# 'clasName'
+ let l:kind = 'f'
+ elseif l:suggestion.kind is# 'parameterName'
+ let l:kind = 'f'
+ else
+ let l:kind = 'v'
+ endif
+
+ " See :help complete-items
+ call add(l:results, {
+ \ 'word': l:suggestion.name,
+ \ 'kind': l:kind,
+ \ 'icase': 1,
+ \ 'menu': join(l:displayParts, ''),
+ \ 'info': join(l:documentationParts, ''),
+ \})
+ endfor
+
+ return l:results
+endfunction
+
+function! ale#completion#ParseLSPCompletions(response) abort
+ let l:item_list = []
+
+ if type(get(a:response, 'result')) is type([])
+ let l:item_list = a:response.result
+ elseif type(get(a:response, 'result')) is type({})
+ \&& type(get(a:response.result, 'items')) is type([])
+ let l:item_list = a:response.result.items
+ endif
+
+ let l:results = []
+
+ for l:item in l:item_list
+ " See :help complete-items for Vim completion kinds
+ if l:item.kind is s:LSP_COMPLETION_METHOD_KIND
+ let l:kind = 'm'
+ elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND
+ let l:kind = 'm'
+ elseif l:item.kind is s:LSP_COMPLETION_FUNCTION_KIND
+ let l:kind = 'f'
+ elseif l:item.kind is s:LSP_COMPLETION_CLASS_KIND
+ let l:kind = 'f'
+ elseif l:item.kind is s:LSP_COMPLETION_INTERFACE_KIND
+ let l:kind = 'f'
+ else
+ let l:kind = 'v'
+ endif
+
+ call add(l:results, {
+ \ 'word': l:item.label,
+ \ 'kind': l:kind,
+ \ 'icase': 1,
+ \ 'menu': l:item.detail,
+ \ 'info': l:item.documentation,
+ \})
+ endfor
+
+ return l:results
+endfunction
+
+function! ale#completion#HandleTSServerResponse(conn_id, response) abort
+ if !s:CompletionStillValid(get(a:response, 'request_seq'))
+ return
+ endif
+
+ if !has_key(a:response, 'body')
+ return
+ endif
+
+ let l:command = get(a:response, 'command', '')
+
+ if l:command is# 'completions'
+ let l:names = ale#completion#Filter(
+ \ ale#completion#ParseTSServerCompletions(a:response),
+ \ b:ale_completion_info.prefix,
+ \)[: g:ale_completion_max_suggestions - 1]
+
+ if !empty(l:names)
+ let b:ale_completion_info.request_id = ale#lsp#Send(
+ \ b:ale_completion_info.conn_id,
+ \ ale#lsp#tsserver_message#CompletionEntryDetails(
+ \ bufnr(''),
+ \ b:ale_completion_info.line,
+ \ b:ale_completion_info.column,
+ \ l:names,
+ \ ),
+ \)
+ endif
+ elseif l:command is# 'completionEntryDetails'
+ call ale#completion#Show(
+ \ a:response,
+ \ 'ale#completion#ParseTSServerCompletionEntryDetails',
+ \)
+ endif
+endfunction
+
+
+function! ale#completion#HandleLSPResponse(conn_id, response) abort
+ if !s:CompletionStillValid(get(a:response, 'id'))
+ return
+ endif
+
+ call ale#completion#Show(
+ \ a:response,
+ \ 'ale#completion#ParseLSPCompletions',
+ \)
+endfunction
+
+function! s:GetLSPCompletions(linter) abort
+ let l:buffer = bufnr('')
+ let l:Callback = a:linter.lsp is# 'tsserver'
+ \ ? function('ale#completion#HandleTSServerResponse')
+ \ : function('ale#completion#HandleLSPResponse')
+
+ let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback)
+
+ if empty(l:lsp_details)
+ return 0
+ endif
+
+ let l:id = l:lsp_details.connection_id
+ let l:root = l:lsp_details.project_root
+
+ if a:linter.lsp is# 'tsserver'
+ let l:message = ale#lsp#tsserver_message#Completions(
+ \ l:buffer,
+ \ b:ale_completion_info.line,
+ \ b:ale_completion_info.column,
+ \ b:ale_completion_info.prefix,
+ \)
+ else
+ " Send a message saying the buffer has changed first, otherwise
+ " completions won't know what text is nearby.
+ call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root)
+
+ " For LSP completions, we need to clamp the column to the length of
+ " the line. python-language-server and perhaps others do not implement
+ " this correctly.
+ let l:message = ale#lsp#message#Completion(
+ \ l:buffer,
+ \ b:ale_completion_info.line,
+ \ min([
+ \ b:ale_completion_info.line_length,
+ \ b:ale_completion_info.column,
+ \ ]),
+ \ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
+ \)
+ endif
+
+ let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
+
+ if l:request_id
+ let b:ale_completion_info.conn_id = l:id
+ let b:ale_completion_info.request_id = l:request_id
+ endif
+endfunction
+
+function! ale#completion#GetCompletions() abort
+ if !g:ale_completion_enabled
+ return
+ endif
+
+ let [l:line, l:column] = getcurpos()[1:2]
+
+ let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
+
+ if empty(l:prefix)
+ return
+ endif
+
+ let l:line_length = len(getline('.'))
+
+ let b:ale_completion_info = {
+ \ 'line': l:line,
+ \ 'line_length': l:line_length,
+ \ 'column': l:column,
+ \ 'prefix': l:prefix,
+ \ 'conn_id': 0,
+ \ 'request_id': 0,
+ \}
+
+ for l:linter in ale#linter#Get(&filetype)
+ if !empty(l:linter.lsp)
+ if l:linter.lsp is# 'tsserver'
+ \|| get(g:, 'ale_completion_experimental_lsp_support', 0)
+ call s:GetLSPCompletions(l:linter)
+ endif
+ endif
+ endfor
+endfunction
+
+function! s:TimerHandler(...) abort
+ let s:timer_id = -1
+
+ let [l:line, l:column] = getcurpos()[1:2]
+
+ " When running the timer callback, we have to be sure that the cursor
+ " hasn't moved from where it was when we requested completions by typing.
+ if s:timer_pos == [l:line, l:column]
+ call ale#completion#GetCompletions()
+ endif
+endfunction
+
+" Stop any completion timer that is queued. This is useful for tests.
+function! ale#completion#StopTimer() abort
+ if s:timer_id != -1
+ call timer_stop(s:timer_id)
+ endif
+
+ let s:timer_id = -1
+endfunction
+
+function! ale#completion#Queue() abort
+ if !g:ale_completion_enabled
+ return
+ endif
+
+ let s:timer_pos = getcurpos()[1:2]
+
+ if s:timer_pos == s:last_done_pos
+ " Do not ask for completions if the cursor rests on the position we
+ " last completed on.
+ return
+ endif
+
+ " If we changed the text again while we're still waiting for a response,
+ " then invalidate the requests before the timer ticks again.
+ if exists('b:ale_completion_info')
+ let b:ale_completion_info.request_id = 0
+ endif
+
+ call ale#completion#StopTimer()
+
+ let s:timer_id = timer_start(g:ale_completion_delay, function('s:TimerHandler'))
+endfunction
+
+function! ale#completion#Done() abort
+ silent! pclose
+
+ " Reset settings when completion is done.
+ if exists('b:ale_old_omnifunc')
+ if b:ale_old_omnifunc isnot# 'pythoncomplete#Complete'
+ let &l:omnifunc = b:ale_old_omnifunc
+ endif
+
+ unlet b:ale_old_omnifunc
+ endif
+
+ if exists('b:ale_old_completopt')
+ let &l:completeopt = b:ale_old_completopt
+ unlet b:ale_old_completopt
+ endif
+
+ let s:last_done_pos = getcurpos()[1:2]
+endfunction
+
+function! s:Setup(enabled) abort
+ augroup ALECompletionGroup
+ autocmd!
+
+ if a:enabled
+ autocmd TextChangedI * call ale#completion#Queue()
+ autocmd CompleteDone * call ale#completion#Done()
+ endif
+ augroup END
+
+ if !a:enabled
+ augroup! ALECompletionGroup
+ endif
+endfunction
+
+function! ale#completion#Enable() abort
+ let g:ale_completion_enabled = 1
+ call s:Setup(1)
+endfunction
+
+function! ale#completion#Disable() abort
+ let g:ale_completion_enabled = 0
+ call s:Setup(0)
+endfunction
diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim
index ad580b9..50b1fb5 100644
--- a/autoload/ale/cursor.vim
+++ b/autoload/ale/cursor.vim
@@ -1,61 +1,44 @@
" Author: w0rp
" Description: Echoes lint message for the current line, if any
-" Return a formatted message according to g:ale_echo_msg_format variable
-function! s:GetMessage(linter, type, text) abort
- let l:msg = g:ale_echo_msg_format
- let l:type = a:type ==# 'E'
- \ ? g:ale_echo_msg_error_str
- \ : g:ale_echo_msg_warning_str
- " Capitalize the 1st character
- let l:text = toupper(a:text[0]) . a:text[1:-1]
+let s:cursor_timer = -1
+let s:last_pos = [0, 0, 0]
- " Replace handlers if they exist
- for [l:k, l:v] in items({'linter': a:linter, 'severity': l:type})
- let l:msg = substitute(l:msg, '\V%' . l:k . '%', l:v, '')
- endfor
-
- 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
+function! ale#cursor#TruncatedEcho(original_message) abort
+ let l:message = a:original_message
" Change tabs to spaces.
let l:message = substitute(l:message, "\t", ' ', 'g')
" Remove any newlines in the message.
let l:message = substitute(l:message, "\n", '', 'g')
- call s:EchoWithShortMess('on', l:message)
+ " We need to remember the setting for shortmess and reset it again.
+ let l:shortmess_options = &l:shortmess
+
+ try
+ let l:cursor_position = getcurpos()
+
+ " The message is truncated and saved to the history.
+ setlocal shortmess+=T
+ exec "norm! :echomsg l:message\n"
+
+ " Reset the cursor position if we moved off the end of the line.
+ " Using :norm and :echomsg can move the cursor off the end of the
+ " line.
+ if l:cursor_position != getcurpos()
+ call setpos('.', l:cursor_position)
+ endif
+ finally
+ let &l:shortmess = l:shortmess_options
+ endtry
endfunction
function! s:FindItemAtCursor() abort
- let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
+ let l:buf = bufnr('')
+ let l:info = get(g:ale_buffer_info, l:buf, {})
+ let l:loclist = get(l:info, '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] : {}
+ let l:index = ale#util#BinarySearch(l:loclist, l:buf, l:pos[1], l:pos[2])
+ let l:loc = l:index >= 0 ? l:loclist[l:index] : {}
return [l:info, l:loc]
endfunction
@@ -68,30 +51,46 @@ function! s:StopCursorTimer() abort
endfunction
function! ale#cursor#EchoCursorWarning(...) abort
- " Only echo the warnings in normal mode, otherwise we will get problems.
- if mode() !=# 'n'
+ return ale#CallWithCooldown('dont_echo_until', function('s:EchoImpl'), [])
+endfunction
+
+function! s:EchoImpl() abort
+ if !g:ale_echo_cursor
return
endif
+ " Only echo the warnings in normal mode, otherwise we will get problems.
+ if mode() isnot# 'n'
+ return
+ endif
+
+ if ale#ShouldDoNothing(bufnr(''))
+ return
+ endif
+
+ let l:buffer = bufnr('')
let [l:info, l:loc] = s:FindItemAtCursor()
if !empty(l:loc)
- let l:msg = s:GetMessage(l:loc.linter_name, l:loc.type, l:loc.text)
+ let l:format = ale#Var(l:buffer, 'echo_msg_format')
+ let l:msg = ale#GetLocItemMessage(l:loc, l:format)
call ale#cursor#TruncatedEcho(l:msg)
let l:info.echoed = 1
elseif get(l:info, 'echoed')
" We'll only clear the echoed message when moving off errors once,
" so we don't continually clear the echo line.
- echo
+ execute 'echo'
let l:info.echoed = 0
endif
endfunction
-let s:cursor_timer = -1
-let s:last_pos = [0, 0, 0]
-
function! ale#cursor#EchoCursorWarningWithDelay() abort
- if ale#ShouldDoNothing()
+ if !g:ale_echo_cursor
+ return
+ endif
+
+ " Only echo the warnings in normal mode, otherwise we will get problems.
+ if mode() isnot# 'n'
return
endif
@@ -104,14 +103,23 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort
" we should echo something. Otherwise we can end up doing processing
" the echo message far too frequently.
if l:pos != s:last_pos
+ let l:delay = ale#Var(bufnr(''), 'echo_delay')
+
let s:last_pos = l:pos
- let s:cursor_timer = timer_start(10, function('ale#cursor#EchoCursorWarning'))
+ let s:cursor_timer = timer_start(
+ \ l:delay,
+ \ 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'
+ if mode() isnot# 'n'
+ return
+ endif
+
+ if ale#ShouldDoNothing(bufnr(''))
return
endif
@@ -122,9 +130,7 @@ function! ale#cursor#ShowCursorDetail() abort
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
+ call ale#preview#Show(split(l:message, "\n"))
+ execute 'echo'
endif
endfunction
diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim
index 60c1b37..cb93ec1 100644
--- a/autoload/ale/debugging.vim
+++ b/autoload/ale/debugging.vim
@@ -2,31 +2,63 @@
" Description: This file implements debugging information for ALE
let s:global_variable_list = [
+\ 'ale_cache_executable_check_failures',
+\ 'ale_change_sign_column_color',
+\ 'ale_command_wrapper',
+\ 'ale_completion_delay',
+\ 'ale_completion_enabled',
+\ 'ale_completion_max_suggestions',
\ 'ale_echo_cursor',
\ 'ale_echo_msg_error_str',
\ 'ale_echo_msg_format',
+\ 'ale_echo_msg_info_str',
\ 'ale_echo_msg_warning_str',
\ 'ale_enabled',
+\ 'ale_fix_on_save',
+\ 'ale_fixers',
+\ 'ale_history_enabled',
+\ 'ale_history_log_output',
\ 'ale_keep_list_window_open',
\ 'ale_lint_delay',
\ 'ale_lint_on_enter',
+\ 'ale_lint_on_filetype_changed',
\ 'ale_lint_on_save',
\ 'ale_lint_on_text_changed',
+\ 'ale_lint_on_insert_leave',
\ 'ale_linter_aliases',
\ 'ale_linters',
+\ 'ale_linters_explicit',
+\ 'ale_list_window_size',
+\ 'ale_list_vertical',
+\ 'ale_loclist_msg_format',
+\ 'ale_max_buffer_history_size',
+\ 'ale_max_signs',
+\ 'ale_maximum_file_size',
\ 'ale_open_list',
+\ 'ale_pattern_options',
+\ 'ale_pattern_options_enabled',
+\ 'ale_set_balloons',
\ 'ale_set_highlights',
\ 'ale_set_loclist',
\ 'ale_set_quickfix',
\ 'ale_set_signs',
\ 'ale_sign_column_always',
\ 'ale_sign_error',
+\ 'ale_sign_info',
\ 'ale_sign_offset',
+\ 'ale_sign_style_error',
+\ 'ale_sign_style_warning',
\ 'ale_sign_warning',
\ 'ale_statusline_format',
+\ 'ale_type_map',
+\ 'ale_warn_about_trailing_blank_lines',
\ 'ale_warn_about_trailing_whitespace',
\]
+function! s:Echo(message) abort
+ execute 'echo a:message'
+endfunction
+
function! s:GetLinterVariables(filetype, linter_names) abort
let l:variable_list = []
let l:filetype_parts = split(a:filetype, '\.')
@@ -50,49 +82,87 @@ endfunction
function! s:EchoLinterVariables(variable_list) abort
for l:key in a:variable_list
- echom 'let g:' . l:key . ' = ' . string(g:[l:key])
+ call s:Echo('let g:' . l:key . ' = ' . string(g:[l:key]))
+
+ if has_key(b:, l:key)
+ call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key]))
+ endif
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))
+ call s:Echo('let g:' . l:key . ' = ' . string(get(g:, l:key, v:null)))
+
+ if has_key(b:, l:key)
+ call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key]))
+ endif
endfor
endfunction
+" Echo a command that was run.
+function! s:EchoCommand(item) abort
+ let l:status_message = a:item.status
+
+ " Include the exit code in output if we have it.
+ if a:item.status is# 'finished'
+ let l:status_message .= ' - exit code ' . a:item.exit_code
+ endif
+
+ call s:Echo('(' . l:status_message . ') ' . string(a:item.command))
+
+ if g:ale_history_log_output && has_key(a:item, 'output')
+ if empty(a:item.output)
+ call s:Echo('')
+ call s:Echo('<<>>')
+ call s:Echo('')
+ else
+ call s:Echo('')
+ call s:Echo('<<