Compare commits

..

227 Commits

Author SHA1 Message Date
w0rp
08295ce174 Bump the ALE version to 3.0.0 2020-09-09 22:06:38 +01:00
w0rp
4ddf742643 Close #2522 - Check pylint on the fly
Newer versions of pylint will now check your code as you type. Older
versions will still only check the file on disk.

Co-authored-by: Oliver Wiegers <oliver.wiegers@gmail.com>
2020-09-09 21:45:15 +01:00
w0rp
78fa93bd55 Add tests for covering the coming Vint version 2020-09-09 20:34:27 +01:00
w0rp
62fd83f1dd Merge pull request #3108 from timbedard/vint-add-stdin
Add stdin option for supported vint versions
2020-09-09 20:15:54 +01:00
w0rp
6a367e44aa Close #3003 - Show ignored linters in :ALEInfo 2020-09-08 22:19:13 +01:00
w0rp
7d90ff56d9 Close #3333 - Add an ALECompletePost event
Add an `ALECompletePost` event along with everything needed to make it
useful for its primary purpose: fixing code after inserting completions.

* `ALEFix` can now be called with a bang (`!`) to suppress errors.
* A new `ALELintStop` command lets you stop linting, and start it later.
2020-09-08 21:40:10 +01:00
w0rp
b4b75126f9 Fix a completion error 2020-09-07 10:01:18 +01:00
Horacio Sanson
fc2a34e9d8 Merge pull request #3337 from sodapopcan/patch-1
Fix typo
2020-09-07 16:30:42 +09:00
Horacio Sanson
8950f6b812 Merge pull request #3334 from remi6397/bugfix/asciidoc-languagetool-typo
Fix asciidoc languagetool integration
2020-09-07 15:37:04 +09:00
Andrew Haust
417761b415 Fix typo 2020-09-07 02:24:39 -04:00
w0rp
c36053d4cc Close #3268 - Implement :ALEImport
A new command, `:ALEImport`, has been added, which lets you import words
at your cursor if a completion provider can provide a completion for
that word which includes some additional text changes.
2020-09-06 22:37:37 +01:00
w0rp
5bc49d2047 Fix #3183 - Escape filename characters from LSP/tsserver 2020-09-05 21:46:39 +01:00
w0rp
152b2cb691 Clean up embertemplatelint code
Alias ember-template-lint to embertemplatelint so users can use either
string to enable the linter.
2020-09-05 19:06:07 +01:00
Jeremiasz Nelz
0b55098bd0 Fix asciidoc languagetool integration 2020-09-04 22:52:29 +02:00
w0rp
844febb9fb Fix #3322 - Apply rename changes correctly 2020-09-04 09:37:33 +01:00
Cyrille David
d4a14746cd feat(template-lint): Read from stdin (#2622)
* ember-template-lint: Lint from stdin
  * This feature has recently been implemented in ember-template-lint.
* Refactor ember-template-lint executable
* Fallback on a temporary file for old template-lint

Co-authored-by: w0rp <w0rp@users.noreply.github.com>
2020-08-31 09:26:33 +01:00
w0rp
ac2100d410 Fix flake8 cd logic for invalid options 2020-08-31 09:12:36 +01:00
w0rp
cdaeca452e Add tests for \r removal 2020-08-31 09:01:44 +01:00
w0rp
31942c99b9 Merge pull request #2858 from kalekseev/patch-flake8
Provide configuration option to run flake8 from project root.
2020-08-31 09:01:16 +01:00
w0rp
4a91f92f28 Merge pull request #3078 from jgehrig/jg-issue2958
Issue 2958: Addtional ^M characters on Windows
2020-08-31 08:31:44 +01:00
Konstantin Alekseev
1462de6685 Run flake8 from project root by default.
Option `per-file-ignores` was introduced in flake8 version 3.7.0.
It allows to ignore specific errors in specific files using glob syntax.
For example `per-file-ignores = src/generated/*.py:F401` will
ignore `F401` error in all python files in `src/generated`.
Thus ale has to run flake8 from project root where .flake8 config
is placed otherwise glob won't match linted file.
2020-08-30 22:56:10 +03:00
w0rp
0989da4a38 Merge pull request #3194 from maxwell-k/vint
vim/vint: show policy name
2020-08-29 20:54:09 +01:00
w0rp
33202a39f5 #2107 - Recommend <C-c> instead, which avoids autocmd functions 2020-08-29 20:45:45 +01:00
w0rp
303bed6ec1 #2107 - Document completion fallbacks and insert-completion trick 2020-08-29 20:40:50 +01:00
w0rp
25b572b3bf Close #3205 - Avoid inserting completions by default 2020-08-29 17:36:25 +01:00
w0rp
6e2e51b154 Fix #2971 - Disable automatic completion while 'paste' is active 2020-08-29 17:27:53 +01:00
w0rp
6888ca46a7 Fix retrying on some test failures 2020-08-29 16:32:17 +01:00
w0rp
f1ecc2e068 Fix psalm options. Retry on some test failures 2020-08-29 16:31:47 +01:00
w0rp
7e0cdb53ec Fix #3247 - Use --always-make for make -n by default 2020-08-29 16:05:49 +01:00
w0rp
bc3a843e10 Add a missing augroup END line 2020-08-29 14:23:58 +01:00
w0rp
6b138b965d Merge pull request #3327 from Kimplul/master
Improved macro handling in gcc
2020-08-29 13:57:16 +01:00
Kimplul
d9a7d6bc23 Improved macro handling in gcc 2020-08-29 15:17:20 +03:00
w0rp
06264c264f typo 2020-08-29 12:35:40 +01:00
w0rp
dd9ad9b5be #3319 - Try to modify buffers later for ALEFix 2020-08-29 12:33:17 +01:00
w0rp
7d4ce4e6aa Close #3325 - Apply new formatting where possible 2020-08-28 19:50:36 +01:00
w0rp
3d5a2690ce #3325 - ale#path#BufferCdString now generates %s:h 2020-08-28 17:46:43 +01:00
w0rp
34e409ea21 Close #3285 - lint_file is now dynamic
`lint_file` can now be computed dynamically with a callback function,
which can return a deferred result, as per `ale#command#Run`. This
allows linters to dynamically switch between checking files on disk,
or checking code on the fly.

Some tests have been fixed on Windows.
2020-08-28 14:02:05 +01:00
w0rp
b8c0ac2e61 Close #3309 - Add b:ale_lint_delay 2020-08-28 09:54:43 +01:00
w0rp
6874120405 Fix #3323 - Set default for g:ale_filename_mappings 2020-08-28 09:33:09 +01:00
w0rp
36e959a466 Add sql-lint to supported tools 2020-08-28 09:25:40 +01:00
w0rp
47fdc02fc7 Merge pull request #2988 from joereynolds/master
Add sql-lint as linter
2020-08-28 08:43:10 +01:00
w0rp
369e3876f0 #3324 - Enable rls by default 2020-08-28 08:23:10 +01:00
Sorin Iclanzan
80bd2e18d6 Set prettier working directory to where .prettierignore is (#3101)
Prettier does not use `.prettierignore` unless the current directory is the root where the `.prettierignore` file resides.

* Update Prettier tests
* Look for prettierignore to determine project root
2020-08-28 08:14:50 +01:00
w0rp
ecd7abecc0 Fix #3319 - Force modifications to buffers 2020-08-27 21:29:13 +01:00
w0rp
7545b18ba1 Fix #3318 - Escape macros when parsing C flags 2020-08-27 21:17:24 +01:00
w0rp
6d843715f3 Fix C flag parsing and tests on Windows 2020-08-27 20:18:13 +01:00
w0rp
6074720dc2 Mention --fast, and document running Windows tests locally 2020-08-27 19:39:16 +01:00
w0rp
af177d7825 #3318 Refactor C flag parsing to set up for quoting arguments 2020-08-27 19:33:43 +01:00
w0rp
719f3c62b0 #3266 - Catch echo visual selection errors 2020-08-27 13:57:20 +01:00
w0rp
5d3d33626c Merge pull request #3310 from pbrisbin/master
Add dhall-format as a Fixer
2020-08-27 13:39:30 +01:00
w0rp
571cff932d Label the test cases more clearly 2020-08-27 13:15:04 +01:00
w0rp
17605777d6 Fix #3317 - Parse -include from C flags 2020-08-27 13:05:50 +01:00
w0rp
66ff00c420 Fix #3316 - Repeat -relative for ALERepeatSelection 2020-08-27 12:41:07 +01:00
w0rp
f5aa0e8457 Fix #3307 - Handle compile_commands paths better
ALE now converts paths from compile_commands.json files into absolute
paths and prefers matching against absolute file and directory names for
determining which flags to use for files. As a result, parsing
compile_commands.json to determine flags should work for a lot more C
and C++ projects.
2020-08-27 11:44:35 +01:00
w0rp
686f42a2e1 Fix a typo 2020-08-27 09:18:10 +01:00
w0rp
c9377e9baf #3314 - Tell people how to make new plug mappings 2020-08-27 09:17:14 +01:00
w0rp
a955f5dfa8 #3312 - Just check if additionalTextEdits is non-empty 2020-08-27 08:57:12 +01:00
w0rp
396fba7cca Fix #3312 - Fix a false positive for auto imports
ALE was incorrectly detecting completion results from servers such as
rust-analyzer as wanting to add import lines when additionalTextEdits
was present, but empty.

Now ALE only filters out completion results if the autoimport setting is
off, and one of the additionalTextEdits starts on some line other than
the current line. If any additionalTextEdits happen to be identical to
the change from completion anyway, ALE will skip them.
2020-08-27 08:44:43 +01:00
patrick brisbin
447aea4af0 Add dhall-format as a Fixer
https://github.com/dhall-lang/dhall-lang
2020-08-25 09:34:34 -04:00
w0rp
3e2abe3f25 #2556 - Support modifiers for formatted filenames 2020-08-24 09:33:07 +01:00
Horacio Sanson
1a7366067a Merge pull request #3302 from khaveesh/master
latexindent: Run fixer from stdin instead of a temporary file
2020-08-24 09:16:35 +09:00
w0rp
33eb03ea71 Yes, try again to fix Windows tests 2020-08-23 20:19:22 +01:00
w0rp
d4583f1a63 Try to fix Windows tests again 2020-08-23 20:14:08 +01:00
w0rp
c0566db1d2 Try to fix Windows tests 2020-08-23 20:05:09 +01:00
w0rp
ba3dd0d027 Close #2556 - Support filename mapping
ALE now supports mapping files between different systems for running
linters and fixers with Docker, in virtual machines, in servers, etc.
2020-08-23 19:55:42 +01:00
Khaveesh N
76801743cf Fixed tests 2020-08-21 17:29:43 +05:30
Khaveesh N
4bece27bd4 refactor(Fixer): Change latexindent to read from stdin instead of temporary file
This is a better strategy as it avoids creating temporary files and the delay that follows
2020-08-21 17:19:22 +05:30
w0rp
2b785688ea #3299 Merge gcc and clang into a cc linter
Users can easily be confused when they set some options for a C or C++
compiler, and another compiler is run with different options, which
still reports errors. To remedy this, the existing `gcc` and `clang`
linters have been replaced with a `cc` linter that will run either
compiler.

This is a breaking change for ALE v3.0.0.
2020-08-20 01:49:14 +01:00
awang
4d42ebc160 Keep -iframework if present in parsed C/C++ flags (#3057)
* Keep -iframework if present in parsed C/C++ flags
* Add test to make sure -iframework is parsed

Co-authored-by: Alex Wang <ts826848@gmail.com>
2020-08-20 00:09:02 +01:00
w0rp
90abb7e7ef Try to fix Windows tests again 2020-08-19 01:22:43 +01:00
w0rp
de7b0567b1 Try to fix tests on Windows 2020-08-19 01:10:33 +01:00
w0rp
361027eac6 Fix #3200 - Do not use -fstack-usage from parsed flags 2020-08-19 01:04:08 +01:00
w0rp
794224aafa #3056 Add tests for parsed C/C++ -std flag handling 2020-08-19 00:47:39 +01:00
w0rp
92cada9913 Merge pull request #3056 from ts826848/remove-ale-std-if-already-in-GetCFlags
[WIP] Avoid overriding parsed C/C++ -std=* flag
2020-08-19 00:21:22 +01:00
w0rp
e27d4377b5 Merge pull request #3178 from sudobash1/expand_at
Fixes #3092 - Implement loading `@file` c arguments
2020-08-19 00:15:34 +01:00
w0rp
f8b3a43aab Merge pull request #3229 from hsanson/3220-enable-languagetool-for-asciidoc
Enable languagetool for asciidoctor files.
2020-08-18 23:22:01 +01:00
w0rp
5eda1df0a9 Remove features deprecated in previous versions 2020-08-18 23:03:43 +01:00
w0rp
4df352eee5 Fix #3294 - Fix hover off by 1, handle LSP server crashes 2020-08-18 01:48:07 +01:00
w0rp
bc6304bdb0 Merge pull request #3007 from klaaspieter/swift-format
Add Apple's swift-format as a linter
2020-08-17 23:37:23 +01:00
w0rp
ac56574b55 Merge pull request #3046 from hsanson/2816-fix-standard-fix-doesnt-work
Fix 2816 - Standard fix does not work.
2020-08-17 21:44:19 +01:00
w0rp
6d33417b15 Merge pull request #2759 from kgrzywacz/master
credo now recognizes umbrella projects
2020-08-17 21:34:10 +01:00
w0rp
eb864730e2 Merge pull request #2906 from elebow/shelldetect-fall-back-to-filetype-if-no-hashbang
ShellDetect falls back to filetype if no hashbang (fixes #2886)
2020-08-17 21:29:16 +01:00
w0rp
514e5a8baa Merge pull request #2940 from davidsierradz/add-markdownlint-options
allow passing custom options to markdownlint
2020-08-17 10:21:36 +01:00
w0rp
cfda549056 Tell stale bot to wait longer before closing PRs 2020-08-17 10:20:41 +01:00
blyoa
d5c1d84230 Add remark-lint for a markdown fixer (#2836) 2020-08-17 10:14:38 +01:00
w0rp
5c778e1ae7 Make it easier to run tests locally
Certain tests could break if you ran them separately from other tests.
They have been patched.

`run-tests` now has a `--fast` option which runs tests with only the
fastest Vim version ALE tests with, and the custom checks.
2020-08-16 21:20:08 +01:00
Horacio Sanson
5ceda0164c Merge pull request #3288 from nibocn/master
fix(eclipselsp): Get jar file path failed
2020-08-14 13:49:09 +09:00
w0rp
5a4fad6172 Fix #2899 - Handle tsserver default import completion 2020-08-14 01:55:54 +01:00
w0rp
2b2403a20d Merge pull request #3144 from jamescdavis/dont_append_newline_when_noeol
don't append a newline to temp file when buffer is noeol and nofixeol is set
2020-08-14 00:32:28 +01:00
w0rp
8151e3e8fa Merge pull request #2920 from ConradIrwin/prettier-standard-stdin
Fix prettier_standard to respect the configuration file
2020-08-14 00:27:41 +01:00
w0rp
94a968ef39 Merge pull request #2924 from patrick96/vlog-filename
verilog: Add filename to vlog linter output
2020-08-14 00:20:57 +01:00
w0rp
ed69b074ba Merge pull request #3164 from mathstuf/cargo-separate-target-dir
rust/cargo: add support for a custom target directory
2020-08-13 16:29:19 +01:00
NiBo
a4f70163eb fix(eclipselsp): Get jar file path failed
Error: Unable to access jarfile
2020-08-13 23:12:12 +08:00
Ben Boeckel
506a8532d0 rust/cargo: add support for a custom target directory
This can avoid having to wait for ALE or ALE being blocked on other
cargo actions within the same crate.
2020-08-13 10:22:33 -04:00
w0rp
0b77766337 Merge pull request #3010 from kevinoid/ps1-powershell
Alias ps1 filetype to powershell
2020-08-13 13:45:49 +01:00
w0rp
979062235a Fix #3286 - Fix duplicate tags 2020-08-13 13:44:22 +01:00
Horacio Sanson
8e6c7bff9a Fix 1695 - Change rubocop fixer to use stdin (#3230)
* Fix 1695 - Change rubocop fixer to use stdin
* Update test_rubocop_fixer_callback.vader

Co-authored-by: Horacio Sanson <horacio@allm.inc>
Co-authored-by: w0rp <w0rp@users.noreply.github.com>
2020-08-13 11:25:07 +01:00
w0rp
34adb997c6 Configure the stale bot 2020-08-13 02:37:54 +01:00
w0rp
64d4c951ae Fix #3281 - Stop CursorHold flickering 2020-08-13 02:22:21 +01:00
w0rp
7c4b1d8444 Close #3274 - Handle basic LSP markdown formatting 2020-08-12 22:11:45 +01:00
Nihad Abbasov
d5912b53dd Restore old behavior of ALEFix command for Rubocop (#3237)
* Restore old behavior of ALEFix command for Rubocop

Since RuboCop 0.60 ALEFix command stopped to fix all found offenses. This change restores the 
previous behavior by allowing rubocop to fix all detected offenses.

* Fix tests
* Allow to configure auto-correct option for Rubocop
2020-08-12 14:04:54 +01:00
w0rp
d3f410b6f7 Fix #1608 - Treat .h as C++ files for C++ clang-tidy 2020-08-10 02:33:50 +01:00
w0rp
2237f9b462 #2919 Use compile_commands.json for headers 2020-08-10 02:14:23 +01:00
w0rp
05210846e4 Fix #3278 - Handle UTF-8 in URI encoding/decoding 2020-08-10 02:03:41 +01:00
w0rp
bf3c3e9438 #3276 - Update README for make -n changes 2020-08-09 20:13:21 +01:00
w0rp
9e1f511003 #3276 - Disable make -n by default again 2020-08-09 20:08:42 +01:00
w0rp
affeed7a87 Enable C flag parsing by default
The options for parsing `make -n` and `compile_commands.json` flags
are now enabled by default, so people can start getting better flags
for their files by default.

`compile_commands.json` flags are now preferred over `make -n` results,
to make the options work better by default.
2020-08-09 04:23:32 +01:00
w0rp
681ca5fee8 Explain how to use ALE and coc.nvim together 2020-08-09 03:18:21 +01:00
w0rp
2ed09f51b6 Move :ALEInfo higher up in the FAQ 2020-08-09 02:50:25 +01:00
w0rp
6d502233d8 Close #3267 - Add a general autoimport setting 2020-08-09 02:32:47 +01:00
w0rp
8bfb5c6407 Merge pull request #2849 from DonnieWest/excludeTsserverWarnings
Allow the user to remove warnings from completions
2020-08-09 01:32:27 +01:00
w0rp
2d174db5b2 Merge pull request #3107 from daliusd/tslintfix
Fix tslint fixer not working issue (temporary file not found)
2020-08-07 19:52:18 +01:00
w0rp
f741245f11 Fix #3273 - Handle missing keys in hover information 2020-08-07 12:34:45 +01:00
w0rp
388049dbea Try to fix gopls tests again 2020-08-07 12:30:50 +01:00
w0rp
96d525a287 Try to fix gopls tests 2020-08-07 12:27:26 +01:00
w0rp
b9a1c0e6d3 Fix order of supported-tools for pyright 2020-08-07 12:24:59 +01:00
w0rp
97d887ae22 It's the Current Year 2020-08-07 12:22:25 +01:00
w0rp
7cefc80f1b Fix a failing test 2020-08-07 12:21:22 +01:00
w0rp
19229e8e27 Close #2472 - Add support for pyright 2020-08-07 12:16:13 +01:00
w0rp
9bdabce8df Fix #2907 - Handle dictionaries for capabilities 2020-08-07 10:54:38 +01:00
w0rp
667618b399 Merge pull request #3191 from mostfunkyduck/master
Fixes govet linter for go 1.13+, with tests
2020-08-07 08:54:17 +01:00
tsjordan-eng
f17b74679f fix cppcheck for 1.89+, and add column support (#3030)
* fix cppcheck for 1.89+, and add column support

In cppcheck 1.89 the output changed to be more like GCC. This commit
forces any version of cppcheck to output in that same format. This also
allows for ALE to pick up the linter's column information

* Add parameters to tests. Vader passes.

* Fix c cppcheck for v1.89
2020-08-06 20:50:44 +01:00
w0rp
4044196047 #2804 Add hdl-checker to supported tools 2020-08-06 13:24:20 +01:00
Andre Souto
5b3da60cea Adds hdl_checker LSP support (#2804)
* Added hdl_checker support
* Added hdl_checker tests

HDL Checker searches for files when no config file is found, which could lead to very long searches when the user is not really on a project setting
2020-08-06 13:20:54 +01:00
w0rp
711c90c523 Merge pull request #3123 from liskin/ccls-build-dir
ccls: Detect build dir and set compilationDatabaseDirectory
2020-08-06 13:12:56 +01:00
w0rp
cdd8d38e2f Fix #3266 Part 2: Fix Harder 2020-08-06 09:36:00 +01:00
w0rp
fa3a927ca3 Fix #3266 - Truncate hover messages for LSP too 2020-08-05 13:46:08 +01:00
w0rp
1bd7b3e4ad Merge pull request #3196 from jeremija/autoimport-langserver-pr
Add autoimport and rename support for langservers
2020-08-05 13:35:48 +01:00
w0rp
86d5cb81bd Use more American English 2020-08-04 20:11:49 +01:00
w0rp
acf892c4d1 #1532 - Display hover information on CursorHold 2020-08-04 20:11:49 +01:00
Horacio Sanson
316c7c7372 Merge pull request #3259 from sblask/support-markdownlint-rules-with-multiple-slashes
Support markdownlint rules with multiple slashes
2020-08-01 13:22:22 +09:00
Horacio Sanson
9894e92553 Merge pull request #3257 from jhlink/fix_astyle_config_detection_with_source
Fix Astyle Project Option Detection when Placed with Source Files
2020-08-01 13:21:16 +09:00
jhlink
e2d1e54eb5 doc: Meaningless comment to kick CI from hang 2020-07-30 21:54:41 -04:00
Horacio Sanson
fc7b4585e6 Merge pull request #3246 from lgmsantos/patch-1
Update ale-java.txt
2020-07-31 08:16:11 +09:00
Sebastian Blask
fbfeae0587 Support markdownlint rules with multiple slashes 2020-07-31 11:15:13 +12:00
Horacio Sanson
275b5248e5 Merge pull request #3258 from jmreicha/patch-3
bashate typo
2020-07-31 08:13:04 +09:00
jhlink
84a413350c fix: Replace hardcoded quotes with ale#Escape 2020-07-30 10:07:53 -04:00
Josh Reichardt
72f1d730fd Typo 2020-07-30 09:00:20 -05:00
jhlink
ac2ebafadd fix: Force add .astylerc in test_cpp_project 2020-07-30 09:58:14 -04:00
jhlink
ab6aed17ab test: Find .astylerc placed with src for astyle 2020-07-30 09:43:41 -04:00
jhlink
d49b06e030 fix: Find proj_options in same dir for astyle 2020-07-30 09:29:33 -04:00
Horacio Sanson
e03e24c091 Merge pull request #3253 from sblask/support-markdownlint-0.19.0-and-0.22.0
Support markdownlint 0.19.0 and 0.22.0
2020-07-30 09:45:13 +09:00
Sebastian Blask
7cada95683 Support markdownlint 0.19.0 and 0.22.0
Fixes #2965
2020-07-30 09:57:46 +12:00
Horacio Sanson
5338dbf680 Merge pull request #3231 from jhlink/add-astyle-for-c-formatting
Add astyle for C/C++ formatting
2020-07-29 22:08:46 +09:00
jhlink
cef64424ce style: Resolve lint issue 2020-07-29 01:52:36 -04:00
jhlink
428c5f94da fix: Use ALE to reliably find project options 2020-07-29 01:36:45 -04:00
jhlink
f4cff3bc06 test: Set empty values for astyle project_options 2020-07-28 23:29:19 -04:00
jhlink
7d40d0a16f chore: Distinguish test value name 2020-07-28 23:28:25 -04:00
jhlink
a9b2f6362d chore: Delete dead test code 2020-07-28 23:26:49 -04:00
jhlink
5377272d20 fix: Change _options to _project_options 2020-07-28 19:48:27 -04:00
jhlink
0e6578cf65 fix: Remove explicit calls to file path in astyle 2020-07-26 22:05:14 -04:00
jhlink
75723e4522 doc: Add C++ desc in astyle entry in registry 2020-07-26 22:05:10 -04:00
jhlink
028485b4de doc: Add ale_cpp_astyle_options in ale-cpp 2020-07-26 21:22:17 -04:00
jhlink
c38b2a6524 doc: Add ale_c_astyle_options in ale-c 2020-07-26 21:21:38 -04:00
jhlink
78295024e1 feat: Add project option file support for astyle 2020-07-26 21:11:37 -04:00
jhlink
b25bb64a4d feat: Use stdin/redirection for astyle 2020-07-26 20:51:41 -04:00
jhlink
a9799fe90e test: Add cpp test case for astyle 2020-07-25 21:25:11 -04:00
jhlink
b3ad7bb3d0 Merge branch into add-astyle-for-c-formatting 2020-07-25 20:38:21 -04:00
jhlink
f26b2e7f01 doc: Add astyle to ale-cpp-options in ale.txt 2020-07-25 20:38:15 -04:00
jhlink
4fc59ccace doc: Add astyle in c++ section in supported-tool 2020-07-25 20:38:12 -04:00
jhlink
9316b7b3f2 doc: Add astyle to c++ in supported lang & tools 2020-07-25 20:38:09 -04:00
jhlink
ee466de733 feat: Add astyle in c++ section in supported-tool 2020-07-25 20:35:28 -04:00
jhlink
2987ebcfae feat: Add astyle to c++ in supported lang & tools 2020-07-25 20:34:03 -04:00
jhlink
a2d172c457 doc: Add astyle to ale-cpp 2020-07-25 20:32:06 -04:00
jhlink
488df0fa41 feat: Add cpp to registry.vim 2020-07-25 20:22:10 -04:00
jhlink
076f9efbd9 feat: Add cpp support to astyle fixer 2020-07-25 20:20:47 -04:00
Luiz Gustavo
9fe208a01f Update ale-java.txt 2020-07-25 02:32:10 -03:00
w0rp
68b484a49f Merge pull request #2914 from hsanson/2913-fix-checkstyle-config-with-options
Fix 2913 - checkstyle config file ignored.
2020-07-21 12:49:55 +01:00
w0rp
f31182beee Merge pull request #3060 from hsanson/345-update-javalsp-documentation
Fix 345 - update java language server docs.
2020-07-21 12:48:57 +01:00
w0rp
f4668c751f Merge pull request #3070 from hsanson/2732-add-bashate-support
Fix 2732 - Add bashate support
2020-07-21 12:47:33 +01:00
w0rp
70ab831001 Merge pull request #3119 from hsanson/2269-ktlint-stdin-support
Fix 2269 - use ktlint stdin.
2020-07-21 12:45:26 +01:00
w0rp
c1c3bd9227 Merge pull request #3137 from hsanson/3132-fix-eclipselsp-command
Fix eclipselsp command function.
2020-07-21 12:43:28 +01:00
Horacio Sanson
54aeeec97f Merge pull request #3225 from markeganfuller/puppet_parser_error_at_end
Handling for puppet parser error at end of input.
2020-07-15 21:08:42 +09:00
Mark Egan-Fuller
d38b92c957 Handling for puppet parser error at end of input.
Add handling for `Syntax error at end of input` which doesn't give a
line or column.

Fixes #2656
2020-07-15 12:10:34 +01:00
Horacio Sanson
26571fa050 Merge pull request #3232 from CherryMan/master
Zig support using zls.
2020-07-11 11:40:52 +09:00
Sheheryar Parvaz
23c58e63d4 Support zls language server for zig 2020-07-10 22:33:37 -04:00
jhlink
d0b7a6e71f doc: Remove c++ from astyle 2020-07-10 19:06:22 -04:00
jhlink
57423f13ec style: Adjust spacing 2020-07-10 18:59:29 -04:00
jhlink
fecffeee91 test: Add astyle vader test 2020-07-10 18:26:15 -04:00
jhlink
56b92544c2 doc: Add astyle to supported lang & tools 2020-07-10 18:16:12 -04:00
jhlink
5dbbd2d18b doc: Add astyle to supported-tools.md 2020-07-10 17:59:14 -04:00
jhlink
8c0b9ecdfd doc: Add astyle to ale-c-options in ale-contents 2020-07-10 17:56:02 -04:00
jhlink
4394084d99 doc: Add astyle entry to ALE c integration 2020-07-10 17:54:27 -04:00
jhlink
6c58164094 feat: Add astyle to fix/register.vim 2020-07-10 17:47:01 -04:00
jhlink
7f881f66a8 feat: Add ALE fixer for astyle 2020-07-10 17:46:01 -04:00
Horacio Sanson
c136061b3f Enable languagetool for asciidoctor files. 2020-07-09 10:46:33 +09:00
Kevin Locke
106c27644b eslint: Use cwd from executable location to fix nested projects (#3222)
* Split FindNearestExecutable from FindExecutable

The path searching in ale#node#FindExecutable() will be useful for
eslint.  Refactor it into a separate function so it can be used without
regard for the state of the _use_global and _executable variables.

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>

* eslint: Set project root from local executable

Using the nearest directory with node_modules does not work correctly
for nested projects where the eslint dependencies are in the outer
project.  For example:
https://github.com/dense-analysis/ale/issues/3143#issuecomment-652452362

Adopt the behavior of SublimeLinter, which runs from project_root
determined by the presence of the eslint executable in node_modules/.bin
(or eslint in dependencies/devDependencies of package.json, which we can
add later as necessary).  See [NodeLinter#find_local_executable].

[NodeLinter#find_local_executable]: https://github.com/SublimeLinter/SublimeLinter/blob/056e6f6/lint/base_linter/node_linter.py#L109

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2020-07-08 14:42:01 +01:00
Horacio Sanson
fd399c527f Merge pull request #3223 from cspeterson/master
Puppet handler regex fix, plus new test
2020-07-08 22:24:28 +09:00
Christopher Peterson
1fcb9d1e1a Tests/puppet: add new test case for a heretofore unaccounted-for version of error message 2020-07-02 12:44:39 -04:00
Christopher Peterson
9b8ec86d7f Puppet handler: make error-parsing regex more robust 2020-07-02 12:42:44 -04:00
Kevin Locke
b3c6db173a Run ESLint fixer from project root, where possible (#3096)
* Split eslint#GetCdString from eslint#GetCommand

Move the code for finding the project root and building the cd string
into a separate function so that it can be reused in the eslint fixer.

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>

* Run ESLint fixer from project root dir

To match the ESLint linter, as changed in 9ee57d43 (which I forgot to
apply to the fixer, whoops).

Fixes: #3094
Closes: #3095

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2020-07-01 17:00:21 +01:00
Horacio Sanson
834d6f9c48 Merge pull request #3158 from ulidtko/improve-dockerfile_lint
Improve :ALEDetail for dockerfile_lint [READY TO REVIEW]
2020-06-20 20:58:42 +09:00
Horacio Sanson
4f25498806 Merge pull request #3172 from 0xMH/patch-1
More docs for beginners to use cloudformation linter
2020-06-19 22:26:45 +09:00
Jerko Steiner
b29e9867e8 Add test for LSP autoimport 2020-05-31 11:46:10 +02:00
Jerko Steiner
b339a8bfa0 Add support for rename (documentChanges) 2020-05-31 11:00:53 +02:00
Jerko Steiner
4062b05669 Fix completion with langserver (autoimport in go) 2020-05-31 11:00:53 +02:00
Keith Maxwell
53bfe41841 vim/vint: show policy name
So that I can find the relevant information in the vint
linting policy summary and policies can be easily configured
https://github.com/Vimjas/vint/wiki/Vint-linting-policy-summary

Before this change an example warning message appears as:

    autocmd should execute in an augroup or execute with a group (see :help :autocmd)

After this change the same example appears as:

    ProhibitAutocmdWithNoGroup - autocmd should execute in an augroup or execute with a group (see :help :autocmd)
2020-05-30 08:38:07 +01:00
mostfunkyduck
35c7c297b6 Fixes govet linter for go 1.13+, with tests 2020-05-25 11:46:16 -04:00
Stephen Robinson
b209315714 Fixes #3092 - Implement loading @file c arguments 2020-05-20 18:15:52 -07:00
Mohamed hamza
6b8f08f7fc Added linter installing methods 2020-05-15 03:40:46 +02:00
NoNE
345daf683b More docs for beginners to use cloudformation linter 2020-05-15 00:35:15 +02:00
max ulidtko
c0d74b8094 Fix style and test 2020-05-06 14:05:39 +03:00
max ulidtko
5f4103fb35 Improve :ALEDetail for dockerfile_lint
1. The often longish `description` moved away from (supposedly short)
statusline `message` into the `detail` section.

2. dockerfile_lint sends `reference_url` pointing to issue explanations.
Use that.
2020-05-06 13:07:08 +03:00
James C. Davis
15d590ee5e fix: don't append newline when buffer is noeol and nofixeol 2020-04-30 15:00:10 -04:00
Klaas Pieter Annema
2548763d04 Add Apple's swift-format as a linter 2020-04-29 14:33:13 +02:00
Horacio Sanson
e641fe0c5d Fix eclipselsp command function.
Changes in eclipselsp and java caused the command needed to run the
language server fail to build properly.

Thi PR fixes those issues.
2020-04-25 15:21:09 +09:00
Tomas Janousek
4fbfcc9dec ccls: Detect build dir and set compilationDatabaseDirectory
Fixes #2621
2020-04-18 13:57:57 +02:00
Horacio Sanson
5035281cb9 Fix 2269 - use ktlint stdin.
Use stdin flag instead of temporary files. This allows ktlint to work
with .editorconfig files.
2020-04-18 15:27:11 +09:00
Dalius Dobravolskas
891852de71 Fix tslint fixer not working issue (temporary file not found) 2020-04-15 17:30:19 +03:00
Tim Bedard
b800d24212 add stdin option for supported vint versions 2020-04-02 22:52:47 -05:00
John Gehrig
1c6b9354a8 Issue 2598: Addtional ^M characters on Windows
Windows may insert carriage return line endings, which ALE does not handle
well. These characters should not be displayed.

Adds a line to remove these characters for all messages.
2020-03-26 09:25:18 -04:00
Horacio Sanson
7b9855f1fe Fix 2732 - Add bashate support 2020-03-23 12:18:35 +09:00
Horacio Sanson
c2aa01199e Fix 345 - update java language server docs. 2020-03-19 18:00:13 +09:00
Alex Wang
fc6677d405 Avoid overriding parsed C/C++ -std=* flag
ALE appends flags from {c,cpp}_{clang,gcc}_options after those found by
parsing compile_commands.json or Makefile output. If -std=* flags are
present in both the ALE flags and parsed flags, the last one present
(i.e., ALE's -std=* flag) will determine the mode the compiler works in.
This can result in errors showing up in vim but not in the actual build
or vice-versa.

For example, say you have foo.cpp:

    #include <type_traits>
    int main() {
        return std::is_same_v<float, int>;
    }

If cpp_clang_options contains -std=c++17 and -std=c++14 is parsed from
compile_commands.json, then ALE would end up running something like:

    clang++ -S -x c++ -fsyntax-only -std=c++14 -std=c++17 - < foo.cpp

This would result in no errors showing up in Vim, but the actual build
would fail with:

    <stdin>:3:14: error: no template named 'is_same_v' in namespace 'std'; did you mean 'is_same'?
            return std::is_same_v<float, int>;
                   ~~~~~^~~~~~~~~
                        is_same
    /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/type_traits:872:61: note: 'is_same' declared here
    template <class _Tp, class _Up> struct _LIBCPP_TEMPLATE_VIS is_same           : public false_type {};
                                                                ^
    <stdin>:3:35: error: expected '(' for function-style cast or type construction
            return std::is_same_v<float, int>;
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~^
    2 errors generated.

as the actual build would not have the -std=c++17 flag added by ALE.

If cpp_clang_options contains -std=c++14 and -std=c++17 is parsed from
compile_commands.json, then the opposite problem would occur. ALE would
end up running something like:

    clang++ -S -x c++ -fsyntax-only -std=c++17 -std=c++14 - < foo.cpp

and would show an error on line 3 of foo.cpp:

    [clang] No template named 'is_same_v' in namespace 'std'; did you mean 'is_same'? (fix available)

The actual build, on the other hand, would succeed without any
complaints.

Removing -std=* from ALE's flags if it is already present in the parsed
flags ensures that the wrong -std=* flag is not used.

An alternative would have been to switch the order in which parsed flags
and ALE flags were concatenated when producing the command to execute,
but that could prevent a user from intentionally using ALE's flags to
override some other flags, e.g.  -W* flags to enable/disable warnings in
a project whose flags are not under the developer's control.

-std=* flags are also present in cuda/nvcc.vim, objc/clang.vim,
objcpp/clang.vim, and vhdl/ghdl.vim, but none of those linters appear to
parse compile_commands.json or `make` output.
2020-03-17 22:51:54 -04:00
Horacio Sanson
c207d6a550 Fix 2816 - Standard fix does not work.
The standard linter --fix fails if the file being input is not relative
to the project root (https://github.com/standard/standard/issues/1384).

This MR attempts to fix this by changing the command so the input file
is relative to the project root and the output is to a temporary file.

Preliminary tests with toy javascript projects seem to indicate this
works fine.
2020-03-12 16:29:46 +09:00
Kevin Locke
8c0b0f085f Alias ps1 filetype to powershell
Rather than requiring users to alias ps1 to powershell themselves,
include it in s:default_ale_linter_aliases.  Since [vim-ps1] is a
popular (the only?) PowerShell ftplugin and there do not appear to be
any other uses of ft=ps1 on vim.org, this seems like a safe and
reasonable default.

[vim-ps1]: http://www.vim.org/scripts/script.php?script_id=1327

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
2020-02-16 14:03:41 -07:00
Joe Reynolds
07080066e4 Add sql-lint as linter 2020-01-31 11:11:12 +00:00
davidsierradz
0cea55924b allow passing custom options to markdownlint 2019-12-19 13:11:25 -05:00
patrick96
1e9f870b74 verilog: Add filename to vlog linter output 2019-12-05 19:35:34 +01:00
Conrad Irwin
efe120ce9a Fix prettier_standard to respect the configuration file
Before this change, prettier_standard would run and ignore any
.prettierrc, now it will respect the configuration of the file being
linted.

This change relies on prettier-standard 16.1.0 for the --stdin-filepath
flag, but is backward compatible: older versions of prettier-standard
will ignore the unknown flag and continue to run with no configuration
file.
2019-12-04 23:15:41 -08:00
Horacio Sanson
8fbcba0091 Fix 2913 - checkstyle config file ignored.
If checkstyle is configured with custom options that contain "-c" then
the checkstyle config file option is ignored. This PR modifies the
regular expression when creating the checkstyle command to avoid this.
2019-11-27 23:19:44 +09:00
Eddie Lebow
ece229c06f shell: Make description more accurate
The shell linter does more than just bash.
2019-11-23 00:34:40 -05:00
Eddie Lebow
1997a8f7e2 ShellDetect: fall back to filetype if no hashbang
Some files lack a hashbang line but still have an unambiguous filetype.
For example, the file `.zshrc` has the filetype `zsh`.

Augment ale#handlers#sh#GetShellType to fall back to the filetype if
no hashbang line can be found.
2019-11-23 00:34:40 -05:00
Donnie West
4222f32cc4 Include documentation item for removing warnings 2019-11-08 16:41:39 -06:00
Donnie West
4bdde36661 Check kind safely 2019-11-08 16:41:38 -06:00
Donnie West
db5fe5659f Allow the user to remove warnings from completions 2019-11-08 16:41:37 -06:00
Kamil Grzywacz
e5e39a939a credo now recognizes umbrella projects
Use ale#handlers#elixir#FindMixUmbrellaRoot to determine project root
instead of ale#handlers#elixir#FindMixProjectRoot
2019-09-07 15:40:35 +02:00
267 changed files with 7068 additions and 2474 deletions

View File

@@ -19,6 +19,9 @@ init:
# Stop git from changing newlines
- git config --global core.autocrlf input
# NOTE: If you change the Vim or Vader versions here, please also update the
# instructions for running tests on Windows in ale-development.txt
install:
# Download and unpack Vim
- ps: >-

17
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
---
# This configuration closes stale PRs after 28 + 7 days.
# That's 4 weeks until stale bot complains, and a week until it closes a PR.
# Issues in ALE are never, ever stale. They are either resolved or not.
only: pulls
daysUntilStale: 28
daysUntilClose: 7
exemptLabels: []
staleLabel: stale
markComment: >
This pull request has been automatically marked as stale because it has not
been updated recently. Make sure to write tests and document your changes.
See `:help ale-dev` for information on writing tests.
If your pull request is good to merge, bother w0rp or another maintainer
again, and get them to merge it.
closeComment: false

5
.gitignore vendored
View File

@@ -1,11 +1,12 @@
!.editorconfig
*.obj
*.pyc
# Ignore all hidden files everywhere.
# Use `git add -f` to add hidden files.
.*
__pycache__
*.pyc
/doc/tags
/init.vim
/test/ale-info-test-file
/vader_output
__pycache__
tags

View File

@@ -1,4 +1,4 @@
Copyright (c) 2016-2019, w0rp <devw0rp@gmail.com>
Copyright (c) 2016-2020, w0rp <devw0rp@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without

155
README.md
View File

@@ -61,23 +61,25 @@ other content at [w0rp.com](https://w0rp.com).
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-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. [How can I check Vue files with ESLint?](#faq-vue-eslint)
14. [Will this plugin eat all of my laptop battery power?](#faq-my-battery-is-sad)
15. [How can I configure my C or C++ project?](#faq-c-configuration)
16. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration)
17. [How can I configure the height of the list in which ALE displays errors?](#faq-list-window-height)
18. [How can I see what ALE has configured for the current file?](#faq-get-info)
2. [How can I see what ALE has configured for the current file?](#faq-get-info)
3. [How can I use ALE and coc.nvim together?](#faq-coc-nvim)
4. [How can I keep the sign gutter open?](#faq-keep-signs)
5. [How can I change the signs ALE uses?](#faq-change-signs)
6. [How can I change or disable the highlights ALE uses?](#faq-change-highlights)
7. [How can I show errors or warnings in my statusline?](#faq-statusline)
8. [How can I show errors or warnings in my lightline?](#faq-lightline)
9. [How can I change the format for echo messages?](#faq-echo-format)
10. [How can I execute some code when ALE starts or stops linting?](#faq-autocmd)
11. [How can I navigate between errors quickly?](#faq-navigation)
12. [How can I run linters only when I save files?](#faq-lint-on-save)
13. [How can I use the quickfix list instead of the loclist?](#faq-quickfix)
14. [How can I check JSX files with both stylelint and eslint?](#faq-jsx-stylelint-eslint)
15. [How can I check Vue files with ESLint?](#faq-vue-eslint)
16. [Will this plugin eat all of my laptop battery power?](#faq-my-battery-is-sad)
17. [How can I configure my C or C++ project?](#faq-c-configuration)
18. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration)
19. [How can I configure the height of the list in which ALE displays errors?](#faq-list-window-height)
20. [How can I run linters or fixers via Docker or a VM?](#faq-vm)
<a name="supported-languages"></a>
@@ -102,7 +104,7 @@ programs for checking the syntax and semantics of your programs. By default,
linters will be re-run in the background to check your syntax when you open
new buffers or as you make edits to your files.
The behaviour of linting can be configured with a variety of options,
The behavior of linting can be configured with a variety of options,
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-integration-options` for options specified to particular linters.
@@ -193,12 +195,11 @@ completion manually with `<C-x><C-o>`.
set omnifunc=ale#completion#OmniFunc
```
When working with TypeScript files, ALE supports automatic imports from
external modules. This behavior is disabled by default and can be enabled by
setting:
ALE supports automatic imports from external modules. This behavior is disabled
by default and can be enabled by setting:
```vim
let g:ale_completion_tsserver_autoimport = 1
let g:ale_completion_autoimport = 1
```
See `:help ale-completion` for more information.
@@ -231,6 +232,9 @@ ALE supports "hover" information for printing brief information about symbols at
the cursor taken from Language Server Protocol linters and `tsserver` with the
`ALEHover` command.
Truncated information will be displayed when the cursor rests on a symbol by
default, as long as there are no problems on the same line.
The information can be displayed in a `balloon` tooltip in Vim or GVim by
hovering your mouse over symbols. Mouse hovering is enabled by default in GVim,
and needs to be configured for Vim 8.1+ in terminals.
@@ -413,9 +417,56 @@ This plugin will look for linters in the [`ale_linters`](ale_linters) directory.
Each directory within corresponds to a particular filetype in Vim, and each file
in each directory corresponds to the name of a particular linter.
<a name="faq-get-info"></a>
### 5.ii. How can I see what ALE has configured for the current file?
Run the following to see what is currently configured:
```vim
:ALEInfo
```
<a name="faq-coc-nvim"></a>
### 5.iii. How can I use ALE and coc.nvim together?
[coc.nvim](https://github.com/neoclide/coc.nvim) is a popular Vim plugin written
in TypeScript and dependent on the [npm](https://www.npmjs.com/) ecosystem for
providing full IDE features to Vim. Both ALE and coc.nvim implement
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/)
(LSP) clients for supporting diagnostics (linting with a live server), and other
features like auto-completion, and others listed above.
ALE is primarily focused on integrating with external programs through virtually
any means, provided the plugin remains almost entirely written in Vim script.
coc.nvim is primarily focused on bringing IDE features to Vim. If you want to
run external programs on your files to check for errors, and also use the most
advanced IDE features, you might want to use both plugins at the same time.
The easiest way to get both plugins to work together is to configure coc.nvim to
send diagnostics to ALE, so ALE controls how all problems are presented to you,
and to disable all LSP features in ALE, so ALE doesn't try to provide LSP
features already provided by coc.nvim, such as auto-completion.
1. Open your coc.nvim configuration file with `:CocConfig` and add
`"diagnostic.displayByAle": true` to your settings.
2. Add `let g:ale_disable_lsp = 1` to your vimrc file, before plugins are
loaded.
You can also use `b:ale_disable_lsp` in your ftplugin files to enable or disable
LSP features in ALE for different filetypes. After you configure coc.nvim and
ALE this way, you can further configure how problems appear to you by using all
of the settings mentioned in ALE's help file, including how often diagnostics
are requested. See `:help ale-lint`.
The integration between ALE and coc.nvim works using an API ALE offers for
letting any other plugin integrate with ALE. If you are interested in writing a
similar integration, see `:help ale-lint-other-sources`.
<a name="faq-keep-signs"></a>
### 5.ii. How can I keep the sign gutter open?
### 5.iv. How can I keep the sign gutter open?
You can keep the sign gutter open at all times by setting the
`g:ale_sign_column_always` to 1
@@ -426,7 +477,7 @@ let g:ale_sign_column_always = 1
<a name="faq-change-signs"></a>
### 5.iii. How can I change the signs ALE uses?
### 5.v. How can I change the signs ALE uses?
Use these options to specify what text should be used for signs:
@@ -446,7 +497,7 @@ highlight clear ALEWarningSign
<a name="faq-change-highlights"></a>
### 5.iv. How can I change or disable the highlights ALE uses?
### 5.vi. 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
@@ -472,7 +523,7 @@ See `:help ale-highlights` for more information.
<a name="faq-statusline"></a>
### 5.v. How can I show errors or warnings in my statusline?
### 5.vii. How can I show errors or warnings in my statusline?
[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
@@ -521,7 +572,7 @@ for more information.
<a name="faq-lightline"></a>
### 5.vi. How can I show errors or warnings in my lightline?
### 5.viii. 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).
@@ -530,7 +581,7 @@ For more information, check out the sources of that plugin, `:help ale#statuslin
<a name="faq-echo-format"></a>
### 5.vii. How can I change the format for echo messages?
### 5.ix. How can I change the format for echo messages?
There are 3 global options that allow customizing the echoed message.
@@ -559,7 +610,7 @@ See `:help g:ale_echo_msg_format` for more information.
<a name="faq-autocmd"></a>
### 5.viii. How can I execute some code when ALE starts or stops linting?
### 5.x. How can I execute some code when ALE starts or stops linting?
ALE runs its own [autocmd](http://vimdoc.sourceforge.net/htmldoc/autocmd.html)
events when a lint or fix cycle are started and stopped. There is also an event
@@ -582,7 +633,7 @@ augroup END
<a name="faq-navigation"></a>
### 5.ix. How can I navigate between errors quickly?
### 5.xi. How can I navigate between errors quickly?
ALE offers some commands with `<Plug>` keybinds for moving between warnings and
errors quickly. You can map the keys Ctrl+j and Ctrl+k to moving between errors
@@ -598,7 +649,7 @@ For more information, consult the online documentation with
<a name="faq-lint-on-save"></a>
### 5.x. How can I run linters only when I save files?
### 5.xii. 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. This option is enabled by default. If you only
@@ -619,7 +670,7 @@ files, you can set `g:ale_lint_on_save` to `0`.
<a name="faq-quickfix"></a>
### 5.xi. How can I use the quickfix list instead of the loclist?
### 5.xiii. 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
@@ -649,7 +700,7 @@ instead of the default horizontally.
<a name="faq-jsx-stylelint-eslint"></a>
### 5.xii. How can I check JSX files with both stylelint and eslint?
### 5.xiv. 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.
@@ -691,7 +742,7 @@ no linter will be run twice for the same file.
<a name="faq-vue-eslint"></a>
### 5.xiii. How can I check Vue files with ESLint?
### 5.xv. How can I check Vue files with ESLint?
To check Vue files with ESLint, your ESLint project configuration file must be
configured to use the [Vue plugin](https://github.com/vuejs/eslint-plugin-vue).
@@ -722,7 +773,7 @@ let g:ale_linters = {'vue': ['eslint', 'vls']}
<a name="faq-my-battery-is-sad"></a>
### 5.xiv. Will this plugin eat all of my laptop battery power?
### 5.xvi. 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
@@ -735,7 +786,7 @@ while you type. ALE uses a timeout which is cancelled and reset every time you
type, and this delay can be increased so linters are run less often. See
`:help g:ale_lint_delay` for more information.
If you don't wish to run linters while you type, you can disable that behaviour.
If you don't wish to run linters while you type, you can disable that behavior.
Set `g:ale_lint_on_text_changed` to `never`. 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.
@@ -746,7 +797,7 @@ including the option `g:ale_lint_on_enter`, and you can run ALE manually with
<a name="faq-c-configuration"></a>
### 5.xv. How can I configure my C or C++ project?
### 5.xvii. 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
@@ -766,13 +817,24 @@ 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.
ALE will attempt to parse `compile_commands.json` files to discover compiler
flags to use when linting code. See `:help g:ale_c_parse_compile_commands` for
more information. See Clang's documentation for
[compile_commands.json files](https://clang.llvm.org/docs/JSONCompilationDatabase.html).
You should strongly consider generating them in your builds, which is easy to do
with CMake.
You can also configure ALE to automatically run `make -n` to run dry runs on
`Makefile`s to discover compiler flags. This can execute arbitrary code, so the
option is disabled by default. See `:help g:ale_c_parse_makefile`.
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.
<a name="faq-buffer-configuration"></a>
### 5.xvi. How can I configure ALE differently for different buffers?
### 5.xviii. 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
@@ -808,7 +870,7 @@ Buffer-local variables for settings always override the global settings.
<a name="faq-list-window-height"></a>
### 5.xvii. How can I configure the height of the list in which ALE displays errors?
### 5.xix. How can I configure the height of the list in which ALE displays errors?
To set a default height for the error list, use the `g:ale_list_window_size` variable.
@@ -817,12 +879,13 @@ To set a default height for the error list, use the `g:ale_list_window_size` var
let g:ale_list_window_size = 5
```
<a name="faq-get-info"></a>
<a name="faq-vm"></a>
### 5.xviii. How can I see what ALE has configured for the current file?
### 5.xx. How can I run linters or fixers via Docker or a VM?
Run the following to see what is currently configured:
```vim
:ALEInfo
```
ALE supports running linters or fixers via Docker, virtual machines, or in
combination with any remote machine with a different file system, so long as the
tools are well-integrated with ALE, and ALE is properly configured to run the
correct commands and map filename paths between different file systems. See
`:help ale-lint-other-machines` for the full documentation on how to configure
ALE to support this.

View File

@@ -18,7 +18,7 @@ function! ale_linters#ada#gcc#GetCommand(buffer) abort
" -gnatc: Check syntax and semantics only (no code generation attempted)
return '%e -x ada -c -gnatc'
\ . ' -o ' . ale#Escape(l:out_file)
\ . ' -I ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ' -I %s:h'
\ . ale#Pad(ale#Var(a:buffer, 'ada_gcc_options'))
\ . ' %t'
endfunction

View File

@@ -0,0 +1,5 @@
" Author: Horacio Sanson (hsanson [ät] gmail.com)
" Description: languagetool for asciidoc files, copied from markdown.
call ale#handlers#languagetool#DefineLinter('asciidoc')

View File

@@ -9,7 +9,7 @@ function! ale_linters#asm#gcc#GetCommand(buffer) abort
" -fsyntax-only doesn't catch everything.
return '%e -x assembler'
\ . ' -o ' . g:ale#util#nul_file
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . '-iquote %s:h'
\ . ' ' . ale#Var(a:buffer, 'asm_gcc_options') . ' -'
endfunction

53
ale_linters/c/cc.vim Normal file
View File

@@ -0,0 +1,53 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: A C compiler linter for C files with gcc/clang, etc.
call ale#Set('c_cc_executable', '<auto>')
call ale#Set('c_cc_options', '-std=c11 -Wall')
function! ale_linters#c#cc#GetExecutable(buffer) abort
let l:executable = ale#Var(a:buffer, 'c_cc_executable')
" Default to either clang or gcc.
if l:executable is# '<auto>'
if ale#engine#IsExecutable(a:buffer, 'clang')
let l:executable = 'clang'
else
let l:executable = 'gcc'
endif
endif
return l:executable
endfunction
function! ale_linters#c#cc#GetCommand(buffer, output) abort
let l:cflags = ale#c#GetCFlags(a:buffer, a:output)
let l:ale_flags = ale#Var(a:buffer, 'c_cc_options')
if l:cflags =~# '-std='
let l:ale_flags = substitute(
\ l:ale_flags,
\ '-std=\(c\|gnu\)[0-9]\{2\}',
\ '',
\ 'g')
endif
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
"
" `-o /dev/null` or `-o null` is needed to catch all errors,
" -fsyntax-only doesn't catch everything.
return '%e -S -x c'
\ . ' -o ' . g:ale#util#nul_file
\ . ' -iquote %s:h'
\ . ale#Pad(l:cflags)
\ . ale#Pad(l:ale_flags) . ' -'
endfunction
call ale#linter#Define('c', {
\ 'name': 'cc',
\ 'aliases': ['gcc', 'clang'],
\ 'output_stream': 'stderr',
\ 'executable': function('ale_linters#c#cc#GetExecutable'),
\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#c#cc#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})

View File

@@ -3,6 +3,7 @@
call ale#Set('c_ccls_executable', 'ccls')
call ale#Set('c_ccls_init_options', {})
call ale#Set('c_build_dir', '')
call ale#linter#Define('c', {
\ 'name': 'ccls',
@@ -10,5 +11,5 @@ call ale#linter#Define('c', {
\ 'executable': {b -> ale#Var(b, 'c_ccls_executable')},
\ 'command': '%e',
\ 'project_root': function('ale#handlers#ccls#GetProjectRoot'),
\ 'initialization_options': {b -> ale#Var(b, 'c_ccls_init_options')},
\ 'initialization_options': {b -> ale#handlers#ccls#GetInitOpts(b, 'c_ccls_init_options')},
\})

View File

@@ -1,24 +0,0 @@
" Author: Masahiro H https://github.com/mshr-h
" Description: clang linter for c files
call ale#Set('c_clang_executable', 'clang')
call ale#Set('c_clang_options', '-std=c11 -Wall')
function! ale_linters#c#clang#GetCommand(buffer, output) abort
let l:cflags = ale#c#GetCFlags(a:buffer, a:output)
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
return '%e -S -x c -fsyntax-only'
\ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ale#Pad(l:cflags)
\ . ale#Pad(ale#Var(a:buffer, 'c_clang_options')) . ' -'
endfunction
call ale#linter#Define('c', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
\ 'executable': {b -> ale#Var(b, 'c_clang_executable')},
\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#c#clang#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})

View File

@@ -10,9 +10,11 @@ function! ale_linters#c#cppcheck#GetCommand(buffer) abort
let l:buffer_path_include = empty(l:compile_commands_option)
\ ? ale#handlers#cppcheck#GetBufferPathIncludeOptions(a:buffer)
\ : ''
let l:template = ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}'''
return l:cd_command
\ . '%e -q --language=c'
\ . l:template
\ . ale#Pad(l:compile_commands_option)
\ . ale#Pad(ale#Var(a:buffer, 'c_cppcheck_options'))
\ . l:buffer_path_include

View File

@@ -1,28 +0,0 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: gcc linter for c files
call ale#Set('c_gcc_executable', 'gcc')
call ale#Set('c_gcc_options', '-std=c11 -Wall')
function! ale_linters#c#gcc#GetCommand(buffer, output) abort
let l:cflags = ale#c#GetCFlags(a:buffer, a:output)
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
"
" `-o /dev/null` or `-o null` is needed to catch all errors,
" -fsyntax-only doesn't catch everything.
return '%e -S -x c'
\ . ' -o ' . g:ale#util#nul_file
\ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ale#Pad(l:cflags)
\ . ale#Pad(ale#Var(a:buffer, 'c_gcc_options')) . ' -'
endfunction
call ale#linter#Define('c', {
\ 'name': 'gcc',
\ 'output_stream': 'stderr',
\ 'executable': {b -> ale#Var(b, 'c_gcc_executable')},
\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#c#gcc#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})

53
ale_linters/cpp/cc.vim Normal file
View File

@@ -0,0 +1,53 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: A C++ compiler linter for C++ files with gcc/clang, etc.
call ale#Set('cpp_cc_executable', '<auto>')
call ale#Set('cpp_cc_options', '-std=c++14 -Wall')
function! ale_linters#cpp#cc#GetExecutable(buffer) abort
let l:executable = ale#Var(a:buffer, 'cpp_cc_executable')
" Default to either clang++ or gcc.
if l:executable is# '<auto>'
if ale#engine#IsExecutable(a:buffer, 'clang++')
let l:executable = 'clang++'
else
let l:executable = 'gcc'
endif
endif
return l:executable
endfunction
function! ale_linters#cpp#cc#GetCommand(buffer, output) abort
let l:cflags = ale#c#GetCFlags(a:buffer, a:output)
let l:ale_flags = ale#Var(a:buffer, 'cpp_cc_options')
if l:cflags =~# '-std='
let l:ale_flags = substitute(
\ l:ale_flags,
\ '-std=\(c\|gnu\)++[0-9]\{2\}',
\ '',
\ 'g')
endif
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
"
" `-o /dev/null` or `-o null` is needed to catch all errors,
" -fsyntax-only doesn't catch everything.
return '%e -S -x c++'
\ . ' -o ' . g:ale#util#nul_file
\ . ' -iquote %s:h'
\ . ale#Pad(l:cflags)
\ . ale#Pad(l:ale_flags) . ' -'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'cc',
\ 'aliases': ['gcc', 'clang', 'g++', 'clang++'],
\ 'output_stream': 'stderr',
\ 'executable': function('ale_linters#cpp#cc#GetExecutable'),
\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#cpp#cc#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})

View File

@@ -3,6 +3,7 @@
call ale#Set('cpp_ccls_executable', 'ccls')
call ale#Set('cpp_ccls_init_options', {})
call ale#Set('c_build_dir', '')
call ale#linter#Define('cpp', {
\ 'name': 'ccls',
@@ -10,5 +11,5 @@ call ale#linter#Define('cpp', {
\ 'executable': {b -> ale#Var(b, 'cpp_ccls_executable')},
\ 'command': '%e',
\ 'project_root': function('ale#handlers#ccls#GetProjectRoot'),
\ 'initialization_options': {b -> ale#Var(b, 'cpp_ccls_init_options')},
\ 'initialization_options': {b -> ale#handlers#ccls#GetInitOpts(b, 'cpp_ccls_init_options')},
\})

View File

@@ -1,24 +0,0 @@
" Author: Tomota Nakamura <https://github.com/tomotanakamura>
" Description: clang linter for cpp files
call ale#Set('cpp_clang_executable', 'clang++')
call ale#Set('cpp_clang_options', '-std=c++14 -Wall')
function! ale_linters#cpp#clang#GetCommand(buffer, output) abort
let l:cflags = ale#c#GetCFlags(a:buffer, a:output)
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
return '%e -S -x c++ -fsyntax-only'
\ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ale#Pad(l:cflags)
\ . ale#Pad(ale#Var(a:buffer, 'cpp_clang_options')) . ' -'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'clang',
\ 'output_stream': 'stderr',
\ 'executable': {b -> ale#Var(b, 'cpp_clang_executable')},
\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#cpp#clang#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})

View File

@@ -25,6 +25,11 @@ function! ale_linters#cpp#clangtidy#GetCommand(buffer, output) abort
let l:options .= !empty(l:options) ? ale#Pad(l:cflags) : l:cflags
endif
" Tell clang-tidy a .h header with a C++ filetype in Vim is a C++ file.
if expand('#' . a:buffer) =~# '\.h$'
let l:options .= !empty(l:options) ? ' -x c++' : '-x c++'
endif
" Get the options to pass directly to clang-tidy
let l:extra_options = ale#Var(a:buffer, 'cpp_clangtidy_extra_options')

View File

@@ -10,9 +10,11 @@ function! ale_linters#cpp#cppcheck#GetCommand(buffer) abort
let l:buffer_path_include = empty(l:compile_commands_option)
\ ? ale#handlers#cppcheck#GetBufferPathIncludeOptions(a:buffer)
\ : ''
let l:template = ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}'''
return l:cd_command
\ . '%e -q --language=c++'
\ . l:template
\ . ale#Pad(l:compile_commands_option)
\ . ale#Pad(ale#Var(a:buffer, 'cpp_cppcheck_options'))
\ . l:buffer_path_include

View File

@@ -1,29 +0,0 @@
" Author: geam <mdelage@student.42.fr>
" Description: gcc linter for cpp files
"
call ale#Set('cpp_gcc_executable', 'gcc')
call ale#Set('cpp_gcc_options', '-std=c++14 -Wall')
function! ale_linters#cpp#gcc#GetCommand(buffer, output) abort
let l:cflags = ale#c#GetCFlags(a:buffer, a:output)
" -iquote with the directory the file is in makes #include work for
" headers in the same directory.
"
" `-o /dev/null` or `-o null` is needed to catch all errors,
" -fsyntax-only doesn't catch everything.
return '%e -S -x c++'
\ . ' -o ' . g:ale#util#nul_file
\ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . ale#Pad(l:cflags)
\ . ale#Pad(ale#Var(a:buffer, 'cpp_gcc_options')) . ' -'
endfunction
call ale#linter#Define('cpp', {
\ 'name': 'gcc',
\ 'aliases': ['g++'],
\ 'output_stream': 'stderr',
\ 'executable': {b -> ale#Var(b, 'cpp_gcc_executable')},
\ 'command': {b -> ale#c#RunMakeCommand(b, function('ale_linters#cpp#gcc#GetCommand'))},
\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\})

View File

@@ -5,9 +5,6 @@ call ale#Set('cuda_nvcc_executable', 'nvcc')
call ale#Set('cuda_nvcc_options', '-std=c++11')
function! ale_linters#cuda#nvcc#GetCommand(buffer) abort
" Unused: use ale#util#nul_file
" let l:output_file = ale#util#Tempname() . '.ii'
" call ale#command#ManageFile(a:buffer, l:output_file)
return '%e -cuda'
\ . ale#Pad(ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer)))
\ . ale#Pad(ale#Var(a:buffer, 'cuda_nvcc_options'))

View File

@@ -32,14 +32,29 @@ function! ale_linters#dockerfile#dockerfile_lint#Handle(buffer, lines) abort
let l:line = get(l:object, 'line', -1)
let l:message = l:object['message']
if get(l:object, 'description', 'None') isnot# 'None'
let l:message = l:message . '. ' . l:object['description']
let l:link = get(l:object, 'reference_url', '')
if type(l:link) == v:t_list
" Somehow, reference_url is returned as two-part list.
" Anchor markers in that list are sometimes duplicated.
" See https://github.com/projectatomic/dockerfile_lint/issues/134
let l:link = join(l:link, '')
let l:link = substitute(l:link, '##', '#', '')
endif
let l:detail = l:message
if get(l:object, 'description', 'None') isnot# 'None'
let l:detail .= "\n\n" . l:object['description']
endif
let l:detail .= "\n\n" . l:link
call add(l:messages, {
\ 'lnum': l:line,
\ 'text': l:message,
\ 'type': ale_linters#dockerfile#dockerfile_lint#GetType(l:type),
\ 'detail': l:detail,
\})
endfor
endfor

View File

@@ -46,7 +46,7 @@ function! ale_linters#elixir#credo#GetMode() abort
endfunction
function! ale_linters#elixir#credo#GetCommand(buffer) abort
let l:project_root = ale#handlers#elixir#FindMixProjectRoot(a:buffer)
let l:project_root = ale#handlers#elixir#FindMixUmbrellaRoot(a:buffer)
let l:mode = ale_linters#elixir#credo#GetMode()
return ale#path#CdString(l:project_root)

View File

@@ -11,7 +11,7 @@ function! ale_linters#eruby#ruumba#GetCommand(buffer) abort
return ale#ruby#EscapeExecutable(l:executable, 'ruumba')
\ . ' --format json --force-exclusion '
\ . ale#Var(a:buffer, 'eruby_ruumba_options')
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
\ . ' --stdin %s'
endfunction
function! ale_linters#eruby#ruumba#Handle(buffer, lines) abort

View File

@@ -6,7 +6,6 @@ function! ale_linters#go#gofmt#GetCommand(buffer) abort
\ . '%e -e %t'
endfunction
call ale#linter#Define('go', {
\ 'name': 'gofmt',
\ 'output_stream': 'stderr',

View File

@@ -4,6 +4,28 @@
call ale#Set('handlebars_embertemplatelint_executable', 'ember-template-lint')
call ale#Set('handlebars_embertemplatelint_use_global', get(g:, 'ale_use_global_executables', 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, version) abort
" Reading from stdin was introduced in ember-template-lint@1.6.0
return ale#semver#GTE(a:version, [1, 6, 0])
\ ? '%e --json --filename %s'
\ : '%e --json %t'
endfunction
function! ale_linters#handlebars#embertemplatelint#GetCommandWithVersionCheck(buffer) abort
return ale#semver#RunWithVersionCheck(
\ a:buffer,
\ ale_linters#handlebars#embertemplatelint#GetExecutable(a:buffer),
\ '%e --version',
\ function('ale_linters#handlebars#embertemplatelint#GetCommand'),
\)
endfunction
function! ale_linters#handlebars#embertemplatelint#Handle(buffer, lines) abort
let l:output = []
let l:json = ale#util#FuzzyJSONDecode(a:lines, {})
@@ -30,10 +52,9 @@ function! ale_linters#handlebars#embertemplatelint#Handle(buffer, lines) abort
endfunction
call ale#linter#Define('handlebars', {
\ 'name': 'ember-template-lint',
\ 'executable': {b -> ale#node#FindExecutable(b, 'handlebars_embertemplatelint', [
\ 'node_modules/.bin/ember-template-lint',
\ ])},
\ 'command': '%e --json %t',
\ 'name': 'embertemplatelint',
\ 'aliases': ['ember-template-lint'],
\ 'executable': function('ale_linters#handlebars#embertemplatelint#GetExecutable'),
\ 'command': function('ale_linters#handlebars#embertemplatelint#GetCommandWithVersionCheck'),
\ 'callback': 'ale_linters#handlebars#embertemplatelint#Handle',
\})

View File

@@ -52,7 +52,7 @@ endfunction
function! ale_linters#java#checkstyle#GetCommand(buffer) abort
let l:options = ale#Var(a:buffer, 'java_checkstyle_options')
let l:config_option = ale#Var(a:buffer, 'java_checkstyle_config')
let l:config = l:options !~# '\v(^| )-c' && !empty(l:config_option)
let l:config = l:options !~# '\v(^| )-c ' && !empty(l:config_option)
\ ? s:GetConfig(a:buffer, l:config_option)
\ : ''

View File

@@ -20,25 +20,39 @@ endfunction
function! ale_linters#java#eclipselsp#JarPath(buffer) abort
let l:path = ale_linters#java#eclipselsp#TargetPath(a:buffer)
" Search jar file within repository path when manually built using mvn
let l:repo_path = l:path . '/org.eclipse.jdt.ls.product/target/repository'
let l:files = globpath(l:repo_path, '**/plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
if has('win32')
let l:platform = 'win32'
elseif has('macunix')
let l:platform = 'macosx'
else
let l:platform = 'linux'
endif
if len(l:files) == 1
" Search jar file within repository path when manually built using mvn
let l:files = globpath(l:path, '**/'.l:platform.'/**/plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
if len(l:files) >= 1
return l:files[0]
endif
" Search jar file within VSCode extensions folder.
let l:files = globpath(l:path, '**/plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
let l:files = globpath(l:path, '**/'.l:platform.'/plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
if len(l:files) == 1
if len(l:files) >= 1
return l:files[0]
endif
" Search jar file within unzipped tar.gz file
let l:files = globpath(l:path, 'plugins/org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
if len(l:files) >= 1
return l:files[0]
endif
" Search jar file within system package path
let l:files = globpath('/usr/share/java/jdtls/plugins', 'org.eclipse.equinox.launcher_\d\.\d\.\d\d\d\.*\.jar', 1, 1)
if len(l:files) == 1
if len(l:files) >= 1
return l:files[0]
endif
@@ -166,7 +180,8 @@ function! ale_linters#java#eclipselsp#RunWithVersionCheck(buffer) abort
return ale#command#Run(
\ a:buffer,
\ l:command,
\ function('ale_linters#java#eclipselsp#CommandWithVersion')
\ function('ale_linters#java#eclipselsp#CommandWithVersion'),
\ { 'output_stream': 'both' }
\)
endfunction

View File

@@ -6,5 +6,5 @@ call ale#linter#Define('kotlin', {
\ 'executable': 'ktlint',
\ 'command': function('ale#handlers#ktlint#GetCommand'),
\ 'callback': 'ale#handlers#ktlint#Handle',
\ 'lint_file': 1
\ 'output_stream': 'stderr'
\})

View File

@@ -1,11 +1,22 @@
" Author: Ty-Lucas Kelley <tylucaskelley@gmail.com>
" Description: Adds support for markdownlint
call ale#Set('markdown_markdownlint_options', '')
function! ale_linters#markdown#markdownlint#GetCommand(buffer) abort
let l:executable = 'markdownlint'
let l:options = ale#Var(a:buffer, 'markdown_markdownlint_options')
return ale#Escape(l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '') . ' %s'
endfunction
call ale#linter#Define('markdown', {
\ 'name': 'markdownlint',
\ 'executable': 'markdownlint',
\ 'lint_file': 1,
\ 'output_stream': 'both',
\ 'command': 'markdownlint %s',
\ 'command': function('ale_linters#markdown#markdownlint#GetCommand'),
\ 'callback': 'ale#handlers#markdownlint#Handle'
\})

View File

@@ -7,10 +7,9 @@ call ale#Set('nasm_nasm_options', '')
function! ale_linters#nasm#nasm#GetCommand(buffer) abort
" Note that NASM requires a trailing slash for the -I option.
let l:separator = has('win32') ? '\' : '/'
let l:path = fnamemodify(bufname(a:buffer), ':p:h') . l:separator
let l:output_null = has('win32') ? 'NUL' : '/dev/null'
return '%e -X gnu -I ' . ale#Escape(l:path)
return '%e -X gnu -I %s:h' . l:separator
\ . ale#Pad(ale#Var(a:buffer, 'nasm_nasm_options'))
\ . ' %s'
\ . ' -o ' . l:output_null

View File

@@ -3,6 +3,7 @@
call ale#Set('objc_ccls_executable', 'ccls')
call ale#Set('objc_ccls_init_options', {})
call ale#Set('c_build_dir', '')
call ale#linter#Define('objc', {
\ 'name': 'ccls',
@@ -10,5 +11,5 @@ call ale#linter#Define('objc', {
\ 'executable': {b -> ale#Var(b, 'objc_ccls_executable')},
\ 'command': '%e',
\ 'project_root': function('ale#handlers#ccls#GetProjectRoot'),
\ 'initialization_options': {b -> ale#Var(b, 'objc_ccls_init_options')},
\ 'initialization_options': {b -> ale#handlers#ccls#GetInitOpts(b, 'objc_ccls_init_options')},
\})

View File

@@ -10,7 +10,7 @@ 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'))
\ . '-iquote %s:h'
\ . ' ' . ale#Var(a:buffer, 'objc_clang_options') . ' -'
endfunction

View File

@@ -10,7 +10,7 @@ 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'))
\ . '-iquote %s:h'
\ . ' ' . ale#Var(a:buffer, 'objcpp_clang_options') . ' -'
endfunction

View File

@@ -9,6 +9,6 @@ call ale#linter#Define('ocaml', {
\ 'lsp': 'stdio',
\ 'executable': function('ale#handlers#ols#GetExecutable'),
\ 'command': function('ale#handlers#ols#GetCommand'),
\ 'language_callback': 'ale#handlers#ols#GetLanguage',
\ 'language': function('ale#handlers#ols#GetLanguage'),
\ 'project_root': function('ale#handlers#ols#GetProjectRoot'),
\})

View File

@@ -1,9 +1,9 @@
" Author: Matt Brown <https://github.com/muglug>
" Description: plugin for Psalm, static analyzer for PHP
call ale#Set('psalm_langserver_executable', 'psalm')
call ale#Set('psalm_langserver_options', '')
call ale#Set('psalm_langserver_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('php_psalm_executable', 'psalm')
call ale#Set('php_psalm_options', '')
call ale#Set('php_psalm_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#php#psalm#GetProjectRoot(buffer) abort
let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git')
@@ -12,13 +12,13 @@ function! ale_linters#php#psalm#GetProjectRoot(buffer) abort
endfunction
function! ale_linters#php#psalm#GetCommand(buffer) abort
return '%e --language-server' . ale#Pad(ale#Var(a:buffer, 'psalm_langserver_options'))
return '%e --language-server' . ale#Pad(ale#Var(a:buffer, 'php_psalm_options'))
endfunction
call ale#linter#Define('php', {
\ 'name': 'psalm',
\ 'lsp': 'stdio',
\ 'executable': {b -> ale#node#FindExecutable(b, 'psalm_langserver', [
\ 'executable': {b -> ale#node#FindExecutable(b, 'php_psalm', [
\ 'vendor/bin/psalm',
\ ])},
\ 'command': function('ale_linters#php#psalm#GetCommand'),

View File

@@ -8,13 +8,15 @@ function! ale_linters#puppet#puppet#Handle(buffer, lines) abort
" 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"
" Error: Could not parse for environment production: Syntax error at 'parameter1' (file: /tmp/modules/mariadb/manifests/slave.pp, line: 4, column: 5)
let l:pattern = '^Error: .*: \(.\+\) \((file:\|at\) .\+\.pp\(, line: \|:\)\(\d\+\)\(, column: \|:\)\=\(\d*\)'
" Error: Illegal attempt to assign to 'a Name'. Not an assignable reference (file: /tmp/modules/waffles/manifests/syrup.pp, line: 5, column: 11)
" Error: Could not parse for environment production: Syntax error at end of input (file: /tmp/modules/bob/manifests/init.pp)
let l:pattern = '^Error:\%(.*:\)\? \(.\+\) \((file:\|at\) .\+\.pp\(\(, line: \|:\)\(\d\+\)\(, column: \|:\)\=\(\d*\)\|)$\)'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'lnum': l:match[4] + 0,
\ 'col': l:match[6] + 0,
\ 'lnum': l:match[5] + 0,
\ 'col': l:match[7] + 0,
\ 'text': l:match[1],
\})
endfor

View File

@@ -6,9 +6,7 @@ call ale#Set('pyrex_cython_executable', 'cython')
call ale#Set('pyrex_cython_options', '--warning-extra')
function! ale_linters#pyrex#cython#GetCommand(buffer) abort
let l:local_dir = ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
return '%e --working ' . l:local_dir . ' --include-dir ' . l:local_dir
return '%e --working %s:h --include-dir %s:h'
\ . ale#Pad(ale#Var(a:buffer, 'pyrex_cython_options'))
\ . ' --output-file ' . g:ale#util#nul_file . ' %t'
endfunction

View File

@@ -4,7 +4,7 @@
call ale#Set('python_flake8_executable', 'flake8')
call ale#Set('python_flake8_options', '')
call ale#Set('python_flake8_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_flake8_change_directory', 1)
call ale#Set('python_flake8_change_directory', 'project')
call ale#Set('python_flake8_auto_pipenv', 0)
function! s:UsingModule(buffer) abort
@@ -38,10 +38,30 @@ function! ale_linters#python#flake8#RunWithVersionCheck(buffer) abort
\)
endfunction
function! ale_linters#python#flake8#GetCdString(buffer) abort
let l:change_directory = ale#Var(a:buffer, 'python_flake8_change_directory')
let l:cd_string = ''
if l:change_directory is# 'project'
let l:project_root = ale#python#FindProjectRootIni(a:buffer)
if !empty(l:project_root)
let l:cd_string = ale#path#CdString(l:project_root)
endif
endif
if (l:change_directory is# 'project' && empty(l:cd_string))
\|| l:change_directory is# 1
\|| l:change_directory is# 'file'
let l:cd_string = ale#path#BufferCdString(a:buffer)
endif
return l:cd_string
endfunction
function! ale_linters#python#flake8#GetCommand(buffer, version) abort
let l:cd_string = ale#Var(a:buffer, 'python_flake8_change_directory')
\ ? ale#path#BufferCdString(a:buffer)
\ : ''
let l:cd_string = ale_linters#python#flake8#GetCdString(a:buffer)
let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv$'

View File

@@ -16,17 +16,15 @@ function! ale_linters#python#pydocstyle#GetExecutable(buffer) abort
endfunction
function! ale_linters#python#pydocstyle#GetCommand(buffer) abort
let l:dir = fnamemodify(bufname(a:buffer), ':p:h')
let l:executable = ale_linters#python#pydocstyle#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run pydocstyle'
\ : ''
return ale#path#CdString(l:dir)
return ale#path#BufferCdString(a:buffer)
\ . ale#Escape(l:executable) . l:exec_args
\ . ' ' . ale#Var(a:buffer, 'python_pydocstyle_options')
\ . ' ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:t'))
\ . ale#Pad(ale#Var(a:buffer, 'python_pydocstyle_options'))
\ . ' %s:t'
endfunction
function! ale_linters#python#pydocstyle#Handle(buffer, lines) abort

View File

@@ -17,7 +17,7 @@ function! ale_linters#python#pylint#GetExecutable(buffer) abort
return ale#python#FindExecutable(a:buffer, 'python_pylint', ['pylint'])
endfunction
function! ale_linters#python#pylint#GetCommand(buffer) abort
function! ale_linters#python#pylint#GetCommand(buffer, version) abort
let l:cd_string = ''
if ale#Var(a:buffer, 'python_pylint_change_directory')
@@ -38,17 +38,23 @@ function! ale_linters#python#pylint#GetCommand(buffer) abort
return l:cd_string
\ . ale#Escape(l:executable) . l:exec_args
\ . ' ' . ale#Var(a:buffer, 'python_pylint_options')
\ . ale#Pad(ale#Var(a:buffer, 'python_pylint_options'))
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n'
\ . (ale#semver#GTE(a:version, [2, 4, 0]) ? ' --from-stdin' : '')
\ . ' %s'
endfunction
function! ale_linters#python#pylint#Handle(buffer, lines) abort
let l:output = ale#python#HandleTraceback(a:lines, 10)
if !empty(l:output)
return l:output
endif
" Matches patterns like the following:
"
" test.py:4:4: W0101 (unreachable) Unreachable code
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:failed = append(0, l:match)
@@ -71,13 +77,19 @@ function! ale_linters#python#pylint#Handle(buffer, lines) abort
let l:code_out = l:match[4]
endif
call add(l:output, {
let l:item = {
\ 'lnum': l:match[1] + 0,
\ 'col': l:match[2] + 1,
\ 'text': l:match[5],
\ 'code': l:code_out,
\ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
\})
\ 'type': 'W',
\}
if l:code[:0] is# 'E'
let l:item.type = 'E'
endif
call add(l:output, l:item)
endfor
return l:output
@@ -86,7 +98,17 @@ endfunction
call ale#linter#Define('python', {
\ 'name': 'pylint',
\ 'executable': function('ale_linters#python#pylint#GetExecutable'),
\ 'command': function('ale_linters#python#pylint#GetCommand'),
\ 'lint_file': {buffer -> ale#semver#RunWithVersionCheck(
\ buffer,
\ ale#Var(buffer, 'python_pylint_executable'),
\ '%e --version',
\ {buffer, version -> !ale#semver#GTE(version, [2, 4, 0])},
\ )},
\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
\ buffer,
\ ale#Var(buffer, 'python_pylint_executable'),
\ '%e --version',
\ function('ale_linters#python#pylint#GetCommand'),
\ )},
\ 'callback': 'ale_linters#python#pylint#Handle',
\ 'lint_file': 1,
\})

View File

@@ -0,0 +1,43 @@
call ale#Set('python_pyright_executable', 'pyright-langserver')
call ale#Set('python_pyright_config', {})
function! ale_linters#python#pyright#GetConfig(buffer) abort
let l:config = deepcopy(ale#Var(a:buffer, 'python_pyright_config'))
if !has_key(l:config, 'python')
let l:config.python = {}
endif
if type(l:config.python) is v:t_dict
" Automatically detect the virtualenv path and use it.
if !has_key(l:config.python, 'venvPath')
let l:venv = ale#python#FindVirtualenv(a:buffer)
if !empty(l:venv)
let l:config.python.venvPath = l:venv
endif
endif
" Automatically use the version of Python in virtualenv.
if type(get(l:config.python, 'venvPath')) is v:t_string
\&& !empty(l:config.python.venvPath)
\&& !has_key(l:config.python, 'pythonPath')
let l:config.python.pythonPath = ale#path#Simplify(
\ l:config.python.venvPath
\ . (has('win32') ? '/Scripts/python' : '/bin/python')
\)
endif
endif
return l:config
endfunction
call ale#linter#Define('python', {
\ 'name': 'pyright',
\ 'lsp': 'stdio',
\ 'executable': {b -> ale#Var(b, 'python_pyright_executable')},
\ 'command': '%e --stdio',
\ 'project_root': function('ale#python#FindProjectRoot'),
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\ 'lsp_config': function('ale_linters#python#pyright#GetConfig'),
\})

View File

@@ -9,6 +9,6 @@ call ale#linter#Define('reason', {
\ 'lsp': 'stdio',
\ 'executable': function('ale#handlers#ols#GetExecutable'),
\ 'command': function('ale#handlers#ols#GetCommand'),
\ 'language_callback': 'ale#handlers#ols#GetLanguage',
\ 'language': function('ale#handlers#ols#GetLanguage'),
\ 'project_root': function('ale#handlers#ols#GetProjectRoot'),
\})

View File

@@ -10,7 +10,7 @@ function! ale_linters#ruby#rubocop#GetCommand(buffer) abort
return ale#ruby#EscapeExecutable(l:executable, 'rubocop')
\ . ' --format json --force-exclusion '
\ . ale#Var(a:buffer, 'ruby_rubocop_options')
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
\ . ' --stdin %s'
endfunction
function! ale_linters#ruby#rubocop#GetType(severity) abort

View File

@@ -11,7 +11,7 @@ function! ale_linters#ruby#standardrb#GetCommand(buffer) abort
return ale#ruby#EscapeExecutable(l:executable, 'standardrb')
\ . ' --format json --force-exclusion '
\ . ale#Var(a:buffer, 'ruby_standardrb_options')
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
\ . ' --stdin %s'
endfunction
" standardrb is based on RuboCop so the callback is the same

View File

@@ -11,6 +11,7 @@ call ale#Set('rust_cargo_default_feature_behavior', 'default')
call ale#Set('rust_cargo_include_features', '')
call ale#Set('rust_cargo_use_clippy', 0)
call ale#Set('rust_cargo_clippy_options', '')
call ale#Set('rust_cargo_target_dir', '')
function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort
if ale#path#FindNearestFile(a:bufnr, 'Cargo.toml') isnot# ''
@@ -31,6 +32,9 @@ function! ale_linters#rust#cargo#GetCommand(buffer, version) abort
\ && ale#semver#GTE(a:version, [0, 22, 0])
let l:use_tests = ale#Var(a:buffer, 'rust_cargo_check_tests')
\ && ale#semver#GTE(a:version, [0, 22, 0])
let l:target_dir = ale#Var(a:buffer, 'rust_cargo_target_dir')
let l:use_target_dir = !empty(l:target_dir)
\ && ale#semver#GTE(a:version, [0, 17, 0])
let l:include_features = ale#Var(a:buffer, 'rust_cargo_include_features')
@@ -82,6 +86,7 @@ function! ale_linters#rust#cargo#GetCommand(buffer, version) abort
\ . (l:use_all_targets ? ' --all-targets' : '')
\ . (l:use_examples ? ' --examples' : '')
\ . (l:use_tests ? ' --tests' : '')
\ . (l:use_target_dir ? (' --target-dir ' . ale#Escape(l:target_dir)) : '')
\ . ' --frozen --message-format=json -q'
\ . l:default_feature
\ . l:include_features

View File

@@ -0,0 +1,43 @@
" Author: hsanson <hsanson@gmail.com>
" Description: Lints sh files using bashate
" URL: https://github.com/openstack/bashate
call ale#Set('sh_bashate_executable', 'bashate')
call ale#Set('sh_bashate_options', '')
function! ale_linters#sh#bashate#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'sh_bashate_executable')
endfunction
function! ale_linters#sh#bashate#GetCommand(buffer) abort
let l:options = ale#Var(a:buffer, 'sh_bashate_options')
let l:executable = ale_linters#sh#bashate#GetExecutable(a:buffer)
return ale#Escape(l:executable) . ' ' . l:options . ' ' . '%t'
endfunction
function! ale_linters#sh#bashate#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" /path/to/script/file:694:1: E003 Indent not multiple of 4
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[1]),
\ 'col': str2nr(l:match[2]),
\ 'text': l:match[3],
\})
endfor
return l:output
endfunction
call ale#linter#Define('sh', {
\ 'name': 'bashate',
\ 'output_stream': 'stdout',
\ 'executable': function('ale_linters#sh#bashate#GetExecutable'),
\ 'command': function('ale_linters#sh#bashate#GetCommand'),
\ 'callback': 'ale_linters#sh#bashate#Handle',
\})

View File

@@ -1,5 +1,5 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Lints sh files using bash -n
" Description: Lints shell files by invoking the shell with -n
" Backwards compatibility
if exists('g:ale_linters_sh_shell_default_shell')

View File

@@ -0,0 +1,33 @@
" ale_linters/sql/sqllint.vim
" Author: Joe Reynolds <joereynolds952@gmail.co>
" Description: sql-lint for SQL files.
" sql-lint can be found at
" https://www.npmjs.com/package/sql-lint
" https://github.com/joereynolds/sql-lint
function! ale_linters#sql#sqllint#Handle(buffer, lines) abort
" Matches patterns like the following:
"
" stdin:1 [ER_NO_DB_ERROR] No database selected
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,
\ 'col': l:match[2] + 0,
\ 'type': l:match[3][0],
\ 'text': l:match[0],
\})
endfor
return l:output
endfunction
call ale#linter#Define('sql', {
\ 'name': 'sqllint',
\ 'aliases': ['sql-lint'],
\ 'executable': 'sql-lint',
\ 'command': 'sql-lint',
\ 'callback': 'ale_linters#sql#sqllint#Handle',
\})

View File

@@ -0,0 +1,62 @@
" Author: Klaas Pieter Annema <https://github.com/klaaspieter>
" Description: Support for swift-format https://github.com/apple/swift-format
let s:default_executable = 'swift-format'
call ale#Set('swift_swiftformat_executable', s:default_executable)
function! ale_linters#swift#swiftformat#UseSwift(buffer) abort
let l:swift_config = ale#path#FindNearestFile(a:buffer, 'Package.swift')
let l:executable = ale#Var(a:buffer, 'swift_swiftformat_executable')
return !empty(l:swift_config) && l:executable is# s:default_executable
endfunction
function! ale_linters#swift#swiftformat#GetExecutable(buffer) abort
if ale_linters#swift#swiftformat#UseSwift(a:buffer)
return 'swift'
endif
return ale#Var(a:buffer, 'swift_swiftformat_executable')
endfunction
function! ale_linters#swift#swiftformat#GetCommand(buffer) abort
let l:executable = ale_linters#swift#swiftformat#GetExecutable(a:buffer)
let l:args = '--mode lint %t'
if ale_linters#swift#swiftformat#UseSwift(a:buffer)
let l:args = 'run swift-format' . ' ' . l:args
endif
return ale#Escape(l:executable) . ' ' . l:args
endfunction
function! ale_linters#swift#swiftformat#Handle(buffer, lines) abort
" Matches lines of the following pattern:
"
" Sources/main.swift:4:21: warning: [DoNotUseSemicolons]: remove ';' and move the next statement to the new line
" Sources/main.swift:3:12: warning: [Spacing]: remove 1 space
let l:pattern = '\v^.*:(\d+):(\d+): (\S+) \[(\S+)\]: (.*)$'
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',
\ 'code': l:match[4],
\ 'text': l:match[5],
\})
endfor
return l:output
endfunction
call ale#linter#Define('swift', {
\ 'name': 'swift-format',
\ 'executable': function('ale_linters#swift#swiftformat#GetExecutable'),
\ 'command': function('ale_linters#swift#swiftformat#GetCommand'),
\ 'output_stream': 'stderr',
\ 'language': 'swift',
\ 'callback': 'ale_linters#swift#swiftformat#Handle'
\})

View File

@@ -0,0 +1,5 @@
" Author: suoto <andre820@gmail.com>
" Description: Adds support for HDL Code Checker, which wraps vcom/vlog, ghdl
" or xvhdl. More info on https://github.com/suoto/hdl_checker
call ale#handlers#hdl_checker#DefineLinter('verilog')

View File

@@ -13,14 +13,15 @@ function! ale_linters#verilog#vlog#Handle(buffer, lines) abort
"Matches patterns like the following:
"** Warning: add.v(7): (vlog-2623) Undefined variable: C.
"** Error: file.v(1): (vlog-13294) Identifier must be declared with a port mode: C.
let l:pattern = '^**\s\(\w*\):[a-zA-Z0-9\-\.\_\/ ]\+(\(\d\+\)):\s\+\(.*\)'
let l:pattern = '^**\s\(\w*\): \([a-zA-Z0-9\-\.\_\/ ]\+\)(\(\d\+\)):\s\+\(.*\)'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'lnum': l:match[2] + 0,
\ 'lnum': l:match[3] + 0,
\ 'type': l:match[1] is? 'Error' ? 'E' : 'W',
\ 'text': l:match[3],
\ 'text': l:match[4],
\ 'filename': l:match[2],
\})
endfor
@@ -28,13 +29,14 @@ function! ale_linters#verilog#vlog#Handle(buffer, lines) abort
"** Warning: (vlog-2623) add.v(7): Undefined variable: C.
"** Error: (vlog-13294) file.v(1): Identifier must be declared with a port mode: C.
" let l:pattern = '^**\s\(\w*\):[a-zA-Z0-9\-\.\_\/ ]\+(\(\d\+\)):\s\+\(.*\)'
let l:pattern = '^**\s\(\w*\):\s\([^)]*)\)[a-zA-Z0-9\-\.\_\/ ]\+(\(\d\+\)):\s\+\(.*\)'
let l:pattern = '^**\s\(\w*\):\s\([^)]*)\) \([a-zA-Z0-9\-\.\_\/ ]\+\)(\(\d\+\)):\s\+\(.*\)'
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'lnum': l:match[3] + 0,
\ 'lnum': l:match[4] + 0,
\ 'type': l:match[1] is? 'Error' ? 'E' : 'W',
\ 'text': l:match[2] . ' ' . l:match[4],
\ 'text': l:match[2] . ' ' . l:match[5],
\ 'filename': l:match[3],
\})
endfor

View File

@@ -0,0 +1,5 @@
" Author: suoto <andre820@gmail.com>
" Description: Adds support for HDL Code Checker, which wraps vcom/vlog, ghdl
" or xvhdl. More info on https://github.com/suoto/hdl_checker
call ale#handlers#hdl_checker#DefineLinter('vhdl')

View File

@@ -5,7 +5,7 @@
call ale#Set('vim_vint_show_style_issues', 1)
call ale#Set('vim_vint_executable', 'vint')
let s:enable_neovim = has('nvim') ? ' --enable-neovim' : ''
let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})"'
let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {policy_name} - {description} (see {reference})"'
function! ale_linters#vim#vint#GetCommand(buffer, version) abort
let l:can_use_no_color_flag = empty(a:version)
@@ -13,12 +13,17 @@ function! ale_linters#vim#vint#GetCommand(buffer, version) abort
let l:warning_flag = ale#Var(a:buffer, 'vim_vint_show_style_issues') ? '-s' : '-w'
" Use the --stdin-display-name argument if supported, temp file otherwise.
let l:stdin_or_temp = ale#semver#GTE(a:version, [0, 4, 0])
\ ? ' --stdin-display-name %s -'
\ : ' %t'
return '%e'
\ . ' ' . l:warning_flag
\ . (l:can_use_no_color_flag ? ' --no-color' : '')
\ . s:enable_neovim
\ . ' ' . s:format
\ . ' %t'
\ . l:stdin_or_temp
endfunction
let s:word_regex_list = [

20
ale_linters/zig/zls.vim Normal file
View File

@@ -0,0 +1,20 @@
" Author: CherryMan <skipper308@hotmail.ca>
" Description: A language server for Zig
call ale#Set('zig_zls_executable', 'zls')
call ale#Set('zig_zls_config', {})
function! ale_linters#zig#zls#GetProjectRoot(buffer) abort
let l:build_rs = ale#path#FindNearestFile(a:buffer, 'build.zig')
return !empty(l:build_rs) ? fnamemodify(l:build_rs, ':h') : ''
endfunction
call ale#linter#Define('zig', {
\ 'name': 'zls',
\ 'lsp': 'stdio',
\ 'lsp_config': {b -> ale#Var(b, 'zig_zls_config')},
\ 'executable': {b -> ale#Var(b, 'zig_zls_executable')},
\ 'command': '%e',
\ 'project_root': function('ale_linters#zig#zls#GetProjectRoot'),
\})

View File

@@ -100,13 +100,7 @@ function! s:Lint(buffer, should_lint_file, timer_id) abort
" Use the filetype from the buffer
let l:filetype = getbufvar(a:buffer, '&filetype')
let l:linters = ale#linter#Get(l:filetype)
" Apply ignore lists for linters only if needed.
let l:ignore_config = ale#Var(a:buffer, 'linters_ignore')
let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp')
let l:linters = !empty(l:ignore_config) || l:disable_lsp
\ ? ale#engine#ignore#Exclude(l:filetype, l:linters, l:ignore_config, l:disable_lsp)
\ : l:linters
let l:linters = ale#linter#RemoveIgnored(a:buffer, l:filetype, l:linters)
" Tell other sources that they can start checking the buffer now.
let g:ale_want_results_buffer = a:buffer
@@ -163,7 +157,7 @@ function! ale#Queue(delay, ...) abort
endif
endfunction
let s:current_ale_version = [2, 7, 0]
let s:current_ale_version = [3, 0, 0]
" A function used to check for ALE features in files outside of the project.
function! ale#Has(feature) abort
@@ -263,6 +257,28 @@ function! ale#GetLocItemMessage(item, format_string) abort
let l:msg = substitute(l:msg, '\V%linter%', '\=l:linter_name', 'g')
" Replace %s with the text.
let l:msg = substitute(l:msg, '\V%s', '\=a:item.text', 'g')
" Windows may insert carriage return line endings (^M), strip these characters.
let l:msg = substitute(l:msg, '\r', '', 'g')
return l:msg
endfunction
" Given a buffer and a linter or fixer name, return an Array of two-item
" Arrays describing how to map filenames to and from the local to foreign file
" systems.
function! ale#GetFilenameMappings(buffer, name) abort
let l:linter_mappings = ale#Var(a:buffer, 'filename_mappings')
if type(l:linter_mappings) is v:t_list
return l:linter_mappings
endif
let l:name = a:name
if !has_key(l:linter_mappings, l:name)
" Use * as a default setting for all tools.
let l:name = '*'
endif
return get(l:linter_mappings, l:name, [])
endfunction

View File

@@ -130,7 +130,7 @@ endfunction
function! ale#assert#LSPLanguage(expected_language) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:language = ale#util#GetFunction(l:linter.language_callback)(l:buffer)
let l:language = ale#linter#GetLanguage(l:buffer, l:linter)
AssertEqual a:expected_language, l:language
endfunction

View File

@@ -2,20 +2,28 @@
" Description: Functions for integrating with C-family linters.
call ale#Set('c_parse_makefile', 0)
call ale#Set('c_parse_compile_commands', 0)
call ale#Set('c_always_make', has('unix') && !has('macunix'))
call ale#Set('c_parse_compile_commands', 1)
let s:sep = has('win32') ? '\' : '/'
" Set just so tests can override it.
let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
function! ale#c#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 g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [
\ 'build',
\ 'bin',
\])
function! s:CanParseMakefile(buffer) abort
" Something somewhere seems to delete this setting in tests, so ensure we
" always have a default value.
call ale#Set('c_parse_makefile', 0)
return ale#Var(a:buffer, 'c_parse_makefile')
endfunction
function! ale#c#GetBuildDirectory(buffer) abort
let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
" c_build_dir has the priority if defined
@@ -68,14 +76,73 @@ function! ale#c#ShellSplit(line) abort
return l:args
endfunction
function! ale#c#ParseCFlags(path_prefix, cflag_line) abort
let l:cflags_list = []
" Takes the path prefix and a list of cflags and expands @file arguments to
" the contents of the file.
"
" @file arguments are command line arguments recognised by gcc and clang. For
" instance, if @./path/to/file was given to gcc, it would load .path/to/file
" and use the contents of that file as arguments.
function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort
let l:out_lines = []
let l:split_lines = ale#c#ShellSplit(a:cflag_line)
for l:option in a:raw_split_lines
if stridx(l:option, '@') == 0
" This is an argument specifying a location of a file containing other arguments
let l:path = join(split(l:option, '\zs')[1:], '')
" Make path absolute
if !ale#path#IsAbsolute(l:path)
let l:rel_path = substitute(l:path, '"', '', 'g')
let l:rel_path = substitute(l:rel_path, '''', '', 'g')
let l:path = ale#path#GetAbsPath(a:path_prefix, l:rel_path)
endif
" Read the file and add all the arguments
try
let l:additional_args = readfile(l:path)
catch
continue " All we can really do is skip this argument
endtry
let l:file_lines = []
for l:line in l:additional_args
let l:file_lines += ale#c#ShellSplit(l:line)
endfor
" @file arguments can include other @file arguments, so we must
" recurse.
let l:out_lines += ale#c#ExpandAtArgs(a:path_prefix, l:file_lines)
else
" This is not an @file argument, so don't touch it.
let l:out_lines += [l:option]
endif
endfor
return l:out_lines
endfunction
" Quote C/C++ a compiler argument, if needed.
"
" Quoting arguments might cause issues with some systems/compilers, so we only
" quote them if we need to.
function! ale#c#QuoteArg(arg) abort
if a:arg !~# '\v[#$&*()\\|[\]{};''"<>/?! ^%]'
return a:arg
endif
return ale#Escape(a:arg)
endfunction
function! ale#c#ParseCFlags(path_prefix, should_quote, raw_arguments) abort
" Expand @file arguments now before parsing
let l:arguments = ale#c#ExpandAtArgs(a:path_prefix, a:raw_arguments)
" A list of [already_quoted, argument]
let l:items = []
let l:option_index = 0
while l:option_index < len(l:split_lines)
let l:option = l:split_lines[l:option_index]
while l:option_index < len(l:arguments)
let l:option = l:arguments[l:option_index]
let l:option_index = l:option_index + 1
" Include options, that may need relative path fix
@@ -83,56 +150,67 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort
\ || stridx(l:option, '-iquote') == 0
\ || stridx(l:option, '-isystem') == 0
\ || stridx(l:option, '-idirafter') == 0
\ || stridx(l:option, '-iframework') == 0
\ || stridx(l:option, '-include') == 0
if stridx(l:option, '-I') == 0 && l:option isnot# '-I'
let l:arg = join(split(l:option, '\zs')[2:], '')
let l:option = '-I'
else
let l:arg = l:split_lines[l:option_index]
let l:arg = l:arguments[l:option_index]
let l:option_index = l:option_index + 1
endif
" Fix relative paths if needed
if stridx(l:arg, s:sep) != 0 && stridx(l:arg, '/') != 0
if !ale#path#IsAbsolute(l:arg)
let l:rel_path = substitute(l:arg, '"', '', 'g')
let l:rel_path = substitute(l:rel_path, '''', '', 'g')
let l:arg = ale#Escape(a:path_prefix . s:sep . l:rel_path)
let l:arg = ale#path#GetAbsPath(a:path_prefix, l:rel_path)
endif
call add(l:cflags_list, l:option)
call add(l:cflags_list, l:arg)
call add(l:items, [1, l:option])
call add(l:items, [1, ale#Escape(l:arg)])
" Options with arg that can be grouped with the option or separate
elseif stridx(l:option, '-D') == 0 || stridx(l:option, '-B') == 0
call add(l:cflags_list, l:option)
if l:option is# '-D' || l:option is# '-B'
call add(l:cflags_list, l:split_lines[l:option_index])
call add(l:items, [1, l:option])
call add(l:items, [0, l:arguments[l:option_index]])
let l:option_index = l:option_index + 1
else
call add(l:items, [0, l:option])
endif
" Options that have an argument (always separate)
elseif l:option is# '-iprefix' || stridx(l:option, '-iwithprefix') == 0
\ || l:option is# '-isysroot' || l:option is# '-imultilib'
call add(l:cflags_list, l:option)
call add(l:cflags_list, l:split_lines[l:option_index])
call add(l:items, [0, l:option])
call add(l:items, [0, l:arguments[l:option_index]])
let l:option_index = l:option_index + 1
" Options without argument
elseif (stridx(l:option, '-W') == 0 && stridx(l:option, '-Wa,') != 0 && stridx(l:option, '-Wl,') != 0 && stridx(l:option, '-Wp,') != 0)
\ || l:option is# '-w' || stridx(l:option, '-pedantic') == 0
\ || l:option is# '-ansi' || stridx(l:option, '-std=') == 0
\ || (stridx(l:option, '-f') == 0 && stridx(l:option, '-fdump') != 0 && stridx(l:option, '-fdiagnostics') != 0 && stridx(l:option, '-fno-show-column') != 0)
\ || stridx(l:option, '-f') == 0 && l:option !~# '\v^-f(dump|diagnostics|no-show-column|stack-usage)'
\ || stridx(l:option, '-O') == 0
\ || l:option is# '-C' || l:option is# '-CC' || l:option is# '-trigraphs'
\ || stridx(l:option, '-nostdinc') == 0 || stridx(l:option, '-iplugindir=') == 0
\ || stridx(l:option, '--sysroot=') == 0 || l:option is# '--no-sysroot-suffix'
\ || stridx(l:option, '-m') == 0
call add(l:cflags_list, l:option)
call add(l:items, [0, l:option])
endif
endwhile
return join(l:cflags_list, ' ')
if a:should_quote
" Quote C arguments that haven't already been quoted above.
" If and only if we've been asked to quote them.
call map(l:items, 'v:val[0] ? v:val[1] : ale#c#QuoteArg(v:val[1])')
else
call map(l:items, 'v:val[1]')
endif
return join(l:items, ' ')
endfunction
function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort
if !g:ale_c_parse_makefile
if !s:CanParseMakefile(a:buffer)
return v:null
endif
@@ -150,7 +228,7 @@ function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h')
return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line)
return ale#c#ParseCFlags(l:makefile_dir, 0, ale#c#ShellSplit(l:cflag_line))
endfunction
" Given a buffer number, find the project directory containing
@@ -218,6 +296,10 @@ if !exists('s:compile_commands_cache')
let s:compile_commands_cache = {}
endif
function! ale#c#ResetCompileCommandsCache() abort
let s:compile_commands_cache = {}
endfunction
function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort
let l:empty = [{}, {}]
@@ -248,9 +330,20 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort
let l:dir_lookup = {}
for l:entry in (type(l:raw_data) is v:t_list ? l:raw_data : [])
let l:filename = ale#path#GetAbsPath(l:entry.directory, l:entry.file)
" Store a key for lookups by the absolute path to the filename.
let l:file_lookup[l:filename] = get(l:file_lookup, l:filename, []) + [l:entry]
" Store a key for fuzzy lookups by the absolute path to the directory.
let l:dirname = fnamemodify(l:filename, ':h')
let l:dir_lookup[l:dirname] = get(l:dir_lookup, l:dirname, []) + [l:entry]
" Store a key for fuzzy lookups by just the basename of the file.
let l:basename = tolower(fnamemodify(l:entry.file, ':t'))
let l:file_lookup[l:basename] = get(l:file_lookup, l:basename, []) + [l:entry]
" Store a key for fuzzy lookups by just the basename of the directory.
let l:dirbasename = tolower(fnamemodify(l:entry.directory, ':p:h:t'))
let l:dir_lookup[l:dirbasename] = get(l:dir_lookup, l:dirbasename, []) + [l:entry]
endfor
@@ -265,28 +358,80 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort
return l:empty
endfunction
function! ale#c#GetCompileCommand(json_item) abort
if has_key(a:json_item, 'command')
return a:json_item.command
elseif has_key(a:json_item, 'arguments')
return join(a:json_item.arguments, ' ')
" Get [should_quote, arguments] from either 'command' or 'arguments'
" 'arguments' should be quoted later, the split 'command' strings should not.
function! s:GetArguments(json_item) abort
if has_key(a:json_item, 'arguments')
return [1, a:json_item.arguments]
elseif has_key(a:json_item, 'command')
return [0, ale#c#ShellSplit(a:json_item.command)]
endif
return ''
return [0, []]
endfunction
function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
let l:buffer_filename = ale#path#Simplify(expand('#' . a:buffer . ':p'))
let l:basename = tolower(fnamemodify(l:buffer_filename, ':t'))
" Look for any file in the same directory if we can't find an exact match.
let l:dir = fnamemodify(l:buffer_filename, ':h')
" Search for an exact file match first.
let l:basename = tolower(expand('#' . a:buffer . ':t'))
let l:file_list = get(a:file_lookup, l:basename, [])
let l:file_list = get(a:file_lookup, l:buffer_filename, [])
" We may have to look for /foo/bar instead of C:\foo\bar
if empty(l:file_list) && has('win32')
let l:file_list = get(
\ a:file_lookup,
\ ale#path#RemoveDriveLetter(l:buffer_filename),
\ []
\)
endif
" Try the absolute path to the directory second.
let l:dir_list = get(a:dir_lookup, l:dir, [])
if empty(l:dir_list) && has('win32')
let l:dir_list = get(
\ a:dir_lookup,
\ ale#path#RemoveDriveLetter(l:dir),
\ []
\)
endif
if empty(l:file_list) && empty(l:dir_list)
" If we can't find matches with the path to the file, try a
" case-insensitive match for any similarly-named file.
let l:file_list = get(a:file_lookup, l:basename, [])
" If we can't find matches with the path to the directory, try a
" case-insensitive match for anything in similarly-named directory.
let l:dir_list = get(a:dir_lookup, tolower(fnamemodify(l:dir, ':t')), [])
endif
" A source file matching the header filename.
let l:source_file = ''
if empty(l:file_list) && l:basename =~? '\.h$\|\.hpp$'
for l:suffix in ['.c', '.cpp']
let l:key = fnamemodify(l:basename, ':r') . l:suffix
" Try to find a source file by an absolute path first.
let l:key = fnamemodify(l:buffer_filename, ':r') . l:suffix
let l:file_list = get(a:file_lookup, l:key, [])
if empty(l:file_list) && has('win32')
let l:file_list = get(
\ a:file_lookup,
\ ale#path#RemoveDriveLetter(l:key),
\ []
\)
endif
if empty(l:file_list)
" Look fuzzy matches on the basename second.
let l:key = fnamemodify(l:basename, ':r') . l:suffix
let l:file_list = get(a:file_lookup, l:key, [])
endif
if !empty(l:file_list)
let l:source_file = l:key
break
@@ -295,28 +440,31 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
endif
for l:item in l:file_list
let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file)
" Load the flags for this file, or for a source file matching the
" header file.
if (
\ bufnr(l:item.file) is a:buffer
\ bufnr(l:filename) is a:buffer
\ || (
\ !empty(l:source_file)
\ && l:item.file[-len(l:source_file):] is? l:source_file
\ && l:filename[-len(l:source_file):] is? l:source_file
\ )
\)
return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item))
let [l:should_quote, l:args] = s:GetArguments(l:item)
return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args)
endif
endfor
" Look for any file in the same directory if we can't find an exact match.
let l:dir = ale#path#Simplify(expand('#' . a:buffer . ':p:h'))
let l:dirbasename = tolower(expand('#' . a:buffer . ':p:h:t'))
let l:dir_list = get(a:dir_lookup, l:dirbasename, [])
for l:item in l:dir_list
if ale#path#Simplify(fnamemodify(l:item.file, ':h')) is? l:dir
return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item))
let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file)
if ale#path#RemoveDriveLetter(fnamemodify(l:filename, ':h'))
\ is? ale#path#RemoveDriveLetter(l:dir)
let [l:should_quote, l:args] = s:GetArguments(l:item)
return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args)
endif
endfor
@@ -334,10 +482,6 @@ endfunction
function! ale#c#GetCFlags(buffer, output) abort
let l:cflags = v:null
if ale#Var(a:buffer, 'c_parse_makefile') && !empty(a:output)
let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output)
endif
if ale#Var(a:buffer, 'c_parse_compile_commands')
let [l:root, l:json_file] = ale#c#FindCompileCommands(a:buffer)
@@ -346,6 +490,10 @@ function! ale#c#GetCFlags(buffer, output) abort
endif
endif
if s:CanParseMakefile(a:buffer) && !empty(a:output) && !empty(l:cflags)
let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output)
endif
if l:cflags is v:null
let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer))
endif
@@ -354,11 +502,14 @@ function! ale#c#GetCFlags(buffer, output) abort
endfunction
function! ale#c#GetMakeCommand(buffer) abort
if ale#Var(a:buffer, 'c_parse_makefile')
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
if s:CanParseMakefile(a:buffer)
let l:path = ale#path#FindNearestFile(a:buffer, 'Makefile')
if !empty(l:makefile_path)
return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n'
if !empty(l:path)
let l:always_make = ale#Var(a:buffer, 'c_always_make')
return ale#path#CdString(fnamemodify(l:path, ':h'))
\ . 'make -n' . (l:always_make ? ' --always-make' : '')
endif
endif
@@ -427,8 +578,3 @@ function! ale#c#IncludeOptions(include_paths) abort
return join(l:option_list)
endfunction
let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [
\ 'build',
\ 'bin',
\])

View File

@@ -24,6 +24,42 @@ function! ale#code_action#HandleCodeAction(code_action, should_save) abort
endfor
endfunction
function! s:ChangeCmp(left, right) abort
if a:left.start.line < a:right.start.line
return -1
endif
if a:left.start.line > a:right.start.line
return 1
endif
if a:left.start.offset < a:right.start.offset
return -1
endif
if a:left.start.offset > a:right.start.offset
return 1
endif
if a:left.end.line < a:right.end.line
return -1
endif
if a:left.end.line > a:right.end.line
return 1
endif
if a:left.end.offset < a:right.end.offset
return -1
endif
if a:left.end.offset > a:right.end.offset
return 1
endif
return 0
endfunction
function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
let l:current_buffer = bufnr('')
" The buffer is used to determine the fileformat, if available.
@@ -48,7 +84,8 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
let l:column_offset = 0
let l:last_end_line = 0
for l:code_edit in a:changes
" Changes have to be sorted so we apply them from top-to-bottom.
for l:code_edit in sort(copy(a:changes), function('s:ChangeCmp'))
if l:code_edit.start.line isnot l:last_end_line
let l:column_offset = 0
endif

View File

@@ -133,11 +133,36 @@ function! ale#command#EscapeCommandPart(command_part) abort
return substitute(a:command_part, '%', '%%', 'g')
endfunction
" Format a filename, converting it with filename mappings, if non-empty,
" and escaping it for putting into a command string.
"
" The filename can be modified.
function! s:FormatFilename(filename, mappings, modifiers) abort
let l:filename = a:filename
if !empty(a:mappings)
let l:filename = ale#filename_mapping#Map(l:filename, a:mappings)
endif
if !empty(a:modifiers)
let l:filename = fnamemodify(l:filename, a:modifiers)
endif
return ale#Escape(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, executable, command, pipe_file_if_needed, input) abort
function! ale#command#FormatCommand(
\ buffer,
\ executable,
\ command,
\ pipe_file_if_needed,
\ input,
\ mappings,
\) abort
let l:temporary_file = ''
let l:command = a:command
@@ -154,14 +179,24 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne
" 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')
let l:command = substitute(
\ l:command,
\ '\v\%s(%(:h|:t|:r|:e)*)',
\ '\=s:FormatFilename(l:filename, a:mappings, submatch(1))',
\ 'g'
\)
endif
if a:input isnot v:false && l:command =~# '%t'
" Create a temporary filename, <temp_dir>/<original_basename>
" The file itself will not be created by this function.
let l:temporary_file = s:TemporaryFilename(a:buffer)
let l:command = substitute(l:command, '%t', '\=ale#Escape(l:temporary_file)', 'g')
let l:command = substitute(
\ l:command,
\ '\v\%t(%(:h|:t|:r|:e)*)',
\ '\=s:FormatFilename(l:temporary_file, a:mappings, submatch(1))',
\ 'g'
\)
endif
" Finish formatting so %% becomes %.
@@ -265,6 +300,7 @@ function! ale#command#Run(buffer, command, Callback, ...) abort
\ a:command,
\ get(l:options, 'read_buffer', 0),
\ get(l:options, 'input', v:null),
\ get(l:options, 'filename_mappings', []),
\)
let l:command = ale#job#PrepareCommand(a:buffer, l:command)
let l:job_options = {

View File

@@ -5,7 +5,7 @@ scriptencoding utf-8
" The omnicompletion menu is shown through a special Plug mapping which is
" only valid in Insert mode. This way, feedkeys() won't send these keys if you
" quit Insert mode quickly enough.
inoremap <silent> <Plug>(ale_show_completion_menu) <C-x><C-o>
inoremap <silent> <Plug>(ale_show_completion_menu) <C-x><C-o><C-p>
" If we hit the key sequence in normal mode, then we won't show the menu, so
" we should restore the old settings right away.
nnoremap <silent> <Plug>(ale_show_completion_menu) :call ale#completion#RestoreCompletionOptions()<CR>
@@ -16,7 +16,8 @@ onoremap <silent> <Plug>(ale_show_completion_menu) <Nop>
let g:ale_completion_delay = get(g:, 'ale_completion_delay', 100)
let g:ale_completion_excluded_words = get(g:, 'ale_completion_excluded_words', [])
let g:ale_completion_max_suggestions = get(g:, 'ale_completion_max_suggestions', 50)
let g:ale_completion_tsserver_autoimport = get(g:, 'ale_completion_tsserver_autoimport', 0)
let g:ale_completion_autoimport = get(g:, 'ale_completion_autoimport', 0)
let g:ale_completion_tsserver_remove_warnings = get(g:, 'ale_completion_tsserver_remove_warnings', 0)
let s:timer_id = -1
let s:last_done_pos = []
@@ -187,7 +188,13 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort
return ''
endfunction
function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
function! ale#completion#Filter(
\ buffer,
\ filetype,
\ suggestions,
\ prefix,
\ exact_prefix_match,
\) abort
let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words')
if empty(a:prefix)
@@ -214,10 +221,17 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
" Dictionaries is accepted here.
let l:word = type(l:item) is v:t_string ? 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)
if a:exact_prefix_match
" Add suggestions if the word is an exact match.
if l:word is# a:prefix
call add(l:filtered_suggestions, l:item)
endif
else
" 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
endif
endfor
endif
@@ -240,21 +254,17 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
return l:filtered_suggestions
endfunction
function! s:ReplaceCompletionOptions() abort
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
" 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
let &l:omnifunc = 'ale#completion#AutomaticOmniFunc'
function! s:ReplaceCompletionOptions(source) 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
if l:source is# 'ale-automatic'
let &l:omnifunc = 'ale#completion#AutomaticOmniFunc'
if a:source is# 'ale-automatic'
if !exists('b:ale_old_completeopt')
let b:ale_old_completeopt = &l:completeopt
endif
@@ -317,41 +327,70 @@ function! ale#completion#AutomaticOmniFunc(findstart, base) abort
else
let l:result = ale#completion#GetCompletionResult()
call s:ReplaceCompletionOptions()
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
call s:ReplaceCompletionOptions(l:source)
endif
return l:result isnot v:null ? l:result : []
endif
endfunction
function! s:OpenCompletionMenu(...) abort
if !&l:paste
call ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")
endif
endfunction
function! ale#completion#Show(result) abort
if ale#util#Mode() isnot# 'i'
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if ale#util#Mode() isnot# 'i' && l:source isnot# 'ale-import'
return
endif
" Set the list in the buffer, temporarily replace omnifunc with our
" function, and then start omni-completion.
" Set the list in the buffer.
let b:ale_completion_result = a:result
" Don't try to open the completion menu if there's nothing to show.
if empty(b:ale_completion_result)
if l:source is# 'ale-import'
" If we ran completion from :ALEImport,
" tell the user that nothing is going to happen.
call s:message('No possible imports found.')
endif
return
endif
" Replace completion options shortly before opening the menu.
call s:ReplaceCompletionOptions()
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if l:source is# 'ale-automatic' || l:source is# 'ale-manual'
call timer_start(
\ 0,
\ {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")}
\)
call s:ReplaceCompletionOptions(l:source)
call timer_start(0, function('s:OpenCompletionMenu'))
endif
if l:source is# 'ale-callback'
call b:CompleteCallback(b:ale_completion_result)
endif
if l:source is# 'ale-import'
call ale#completion#HandleUserData(b:ale_completion_result[0])
let l:text_changed = '' . g:ale_lint_on_text_changed
" Check the buffer again right away, if linting is enabled.
if g:ale_enabled
\&& (
\ l:text_changed is# '1'
\ || l:text_changed is# 'always'
\ || l:text_changed is# 'normal'
\ || l:text_changed is# 'insert'
\)
call ale#Queue(0, '')
endif
endif
endfunction
function! ale#completion#GetAllTriggers() abort
@@ -382,14 +421,18 @@ endfunction
function! s:CompletionStillValid(request_id) abort
let [l:line, l:column] = getpos('.')[1:2]
return ale#util#Mode() is# 'i'
\&& has_key(b:, 'ale_completion_info')
return has_key(b:, 'ale_completion_info')
\&& (
\ ale#util#Mode() is# 'i'
\ || b:ale_completion_info.source is# 'ale-import'
\)
\&& b:ale_completion_info.request_id == a:request_id
\&& b:ale_completion_info.line == l:line
\&& (
\ b:ale_completion_info.column == l:column
\ || b:ale_completion_info.source is# 'ale-omnifunc'
\ || b:ale_completion_info.source is# 'ale-callback'
\ || b:ale_completion_info.source is# 'ale-import'
\)
endfunction
@@ -397,10 +440,14 @@ function! ale#completion#ParseTSServerCompletions(response) abort
let l:names = []
for l:suggestion in a:response.body
call add(l:names, {
\ 'word': l:suggestion.name,
\ 'source': get(l:suggestion, 'source', ''),
\})
let l:kind = get(l:suggestion, 'kind', '')
if g:ale_completion_tsserver_remove_warnings == 0 || l:kind isnot# 'warning'
call add(l:names, {
\ 'word': l:suggestion.name,
\ 'source': get(l:suggestion, 'source', ''),
\})
endif
endfor
return l:names
@@ -410,15 +457,26 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
let l:buffer = bufnr('')
let l:results = []
let l:names_with_details = []
let l:info = get(b:, 'ale_completion_info', {})
for l:suggestion in a:response.body
let l:displayParts = []
let l:local_name = v:null
for l:action in get(l:suggestion, 'codeActions', [])
call add(l:displayParts, l:action.description . ' ')
endfor
for l:part in l:suggestion.displayParts
" Stop on stop on line breaks for the menu.
if get(l:part, 'kind') is# 'lineBreak'
break
endif
if get(l:part, 'kind') is# 'localName'
let l:local_name = l:part.text
endif
call add(l:displayParts, l:part.text)
endfor
@@ -431,21 +489,35 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
" See :help complete-items
let l:result = {
\ 'word': l:suggestion.name,
\ 'word': (
\ l:suggestion.name is# 'default'
\ && l:suggestion.kind is# 'alias'
\ && !empty(l:local_name)
\ ? l:local_name
\ : l:suggestion.name
\ ),
\ 'kind': ale#completion#GetCompletionSymbols(l:suggestion.kind),
\ 'icase': 1,
\ 'menu': join(l:displayParts, ''),
\ 'dup': g:ale_completion_tsserver_autoimport,
\ 'dup': get(l:info, 'additional_edits_only', 0)
\ || g:ale_completion_autoimport,
\ 'info': join(l:documentationParts, ''),
\}
" This flag is used to tell if this completion came from ALE or not.
let l:user_data = {'_ale_completion_item': 1}
if has_key(l:suggestion, 'codeActions')
let l:result.user_data = json_encode({
\ 'codeActions': l:suggestion.codeActions,
\ })
let l:user_data.code_actions = l:suggestion.codeActions
endif
call add(l:results, l:result)
let l:result.user_data = json_encode(l:user_data)
" Include this item if we'll accept any items,
" or if we only want items with additional edits, and this has them.
if !get(l:info, 'additional_edits_only', 0)
\|| has_key(l:user_data, 'code_actions')
call add(l:results, l:result)
endif
endfor
let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', [])
@@ -464,6 +536,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
\ 'icase': 1,
\ 'menu': '',
\ 'info': '',
\ 'user_data': json_encode({'_ale_completion_item': 1}),
\})
endfor
endif
@@ -517,23 +590,80 @@ function! ale#completion#ParseLSPCompletions(response) abort
continue
endif
" Don't use LSP items with additional text edits when autoimport for
" completions is turned off.
if !empty(get(l:item, 'additionalTextEdits'))
\&& !(
\ get(l:info, 'additional_edits_only', 0)
\ || g:ale_completion_autoimport
\)
continue
endif
let l:doc = get(l:item, 'documentation', '')
if type(l:doc) is v:t_dict && has_key(l:doc, 'value')
let l:doc = l:doc.value
endif
call add(l:results, {
let l:result = {
\ 'word': l:word,
\ 'kind': ale#completion#GetCompletionSymbols(get(l:item, 'kind', '')),
\ 'icase': 1,
\ 'menu': get(l:item, 'detail', ''),
\ 'info': (type(l:doc) is v:t_string ? l:doc : ''),
\})
\}
" This flag is used to tell if this completion came from ALE or not.
let l:user_data = {'_ale_completion_item': 1}
if has_key(l:item, 'additionalTextEdits')
let l:text_changes = []
for l:edit in l:item.additionalTextEdits
call add(l:text_changes, {
\ 'start': {
\ 'line': l:edit.range.start.line + 1,
\ 'offset': l:edit.range.start.character + 1,
\ },
\ 'end': {
\ 'line': l:edit.range.end.line + 1,
\ 'offset': l:edit.range.end.character + 1,
\ },
\ 'newText': l:edit.newText,
\})
endfor
if !empty(l:text_changes)
let l:user_data.code_actions = [{
\ 'description': 'completion',
\ 'changes': [
\ {
\ 'fileName': expand('#' . l:buffer . ':p'),
\ 'textChanges': l:text_changes,
\ },
\ ],
\}]
endif
endif
let l:result.user_data = json_encode(l:user_data)
" Include this item if we'll accept any items,
" or if we only want items with additional edits, and this has them.
if !get(l:info, 'additional_edits_only', 0)
\|| has_key(l:user_data, 'code_actions')
call add(l:results, l:result)
endif
endfor
if has_key(l:info, 'prefix')
let l:results = ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix)
let l:results = ale#completion#Filter(
\ l:buffer,
\ &filetype,
\ l:results,
\ l:info.prefix,
\ get(l:info, 'additional_edits_only', 0),
\)
endif
return l:results[: g:ale_completion_max_suggestions - 1]
@@ -557,13 +687,18 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort
\ &filetype,
\ ale#completion#ParseTSServerCompletions(a:response),
\ b:ale_completion_info.prefix,
\ get(b:ale_completion_info, 'additional_edits_only', 0),
\)[: g:ale_completion_max_suggestions - 1]
" We need to remember some names for tsserver, as it doesn't send
" details back for everything we send.
call setbufvar(l:buffer, 'ale_tsserver_completion_names', l:names)
if !empty(l:names)
if empty(l:names)
" Response with no results now and skip making a redundant request
" for nothing.
call ale#completion#Show([])
else
let l:identifiers = []
for l:name in l:names
@@ -628,12 +763,17 @@ function! s:OnReady(linter, lsp_details) abort
call ale#lsp#RegisterCallback(l:id, l:Callback)
if a:linter.lsp is# 'tsserver'
if get(g:, 'ale_completion_tsserver_autoimport') is 1
execute 'echom `g:ale_completion_tsserver_autoimport` is deprecated. Use `g:ale_completion_autoimport` instead.'''
endif
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,
\ g:ale_completion_tsserver_autoimport,
\ get(b:ale_completion_info, 'additional_edits_only', 0)
\ || g:ale_completion_autoimport,
\)
else
" Send a message saying the buffer has changed first, otherwise
@@ -692,9 +832,19 @@ function! ale#completion#GetCompletions(...) abort
let b:CompleteCallback = l:CompleteCallback
endif
let [l:line, l:column] = getpos('.')[1:2]
if has_key(l:options, 'line') && has_key(l:options, 'column')
" Use a provided line and column, if given.
let l:line = l:options.line
let l:column = l:options.column
else
let [l:line, l:column] = getpos('.')[1:2]
endif
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
if has_key(l:options, 'prefix')
let l:prefix = l:options.prefix
else
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
endif
if l:source is# 'ale-automatic' && empty(l:prefix)
return 0
@@ -713,6 +863,11 @@ function! ale#completion#GetCompletions(...) abort
\}
unlet! b:ale_completion_result
if has_key(l:options, 'additional_edits_only')
let b:ale_completion_info.additional_edits_only =
\ l:options.additional_edits_only
endif
let l:buffer = bufnr('')
let l:Callback = function('s:OnReady')
@@ -729,6 +884,37 @@ function! ale#completion#GetCompletions(...) abort
return l:started
endfunction
function! s:message(message) abort
call ale#util#Execute('echom ' . string(a:message))
endfunction
" This function implements the :ALEImport command.
function! ale#completion#Import() abort
let l:word = expand('<cword>')
if empty(l:word)
call s:message('Nothing to complete at cursor!')
return
endif
let [l:line, l:column] = getpos('.')[1:2]
let l:column = searchpos('\V' . escape(l:word, '/\'), 'bn', l:line)[1]
if l:column isnot 0
let l:started = ale#completion#GetCompletions('ale-import', {
\ 'line': l:line,
\ 'column': l:column,
\ 'prefix': l:word,
\ 'additional_edits_only': 1,
\})
if !l:started
call s:message('No completion providers are available.')
endif
endif
endfunction
function! ale#completion#OmniFunc(findstart, base) abort
if a:findstart
let l:started = ale#completion#GetCompletions('ale-omnifunc')
@@ -802,29 +988,29 @@ function! ale#completion#Queue() abort
endfunction
function! ale#completion#HandleUserData(completed_item) abort
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if l:source isnot# 'ale-automatic'
\&& l:source isnot# 'ale-manual'
\&& l:source isnot# 'ale-callback'
return
endif
let l:user_data_json = get(a:completed_item, 'user_data', '')
if empty(l:user_data_json)
return
endif
let l:user_data = json_decode(l:user_data_json)
let l:user_data = !empty(l:user_data_json)
\ ? json_decode(l:user_data_json)
\ : v:null
if type(l:user_data) isnot v:t_dict
\|| get(l:user_data, '_ale_completion_item', 0) isnot 1
return
endif
for l:code_action in get(l:user_data, 'codeActions', [])
call ale#code_action#HandleCodeAction(l:code_action, v:false)
endfor
let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
if l:source is# 'ale-automatic'
\|| l:source is# 'ale-manual'
\|| l:source is# 'ale-callback'
\|| l:source is# 'ale-import'
\|| l:source is# 'ale-omnifunc'
for l:code_action in get(l:user_data, 'code_actions', [])
call ale#code_action#HandleCodeAction(l:code_action, v:false)
endfor
endif
silent doautocmd <nomodeline> User ALECompletePost
endfunction
function! ale#completion#Done() abort
@@ -836,6 +1022,8 @@ function! ale#completion#Done() abort
endfunction
augroup ALECompletionActions
autocmd!
autocmd CompleteDone * call ale#completion#HandleUserData(v:completed_item)
augroup END

View File

@@ -39,6 +39,8 @@ function! ale#cursor#TruncatedEcho(original_message) abort
endif
exec 'echomsg l:message'
catch /E481/
" Do nothing if running from a visual selection.
endtry
" Reset the cursor position if we moved off the end of the line.

View File

@@ -8,6 +8,7 @@ let s:global_variable_list = [
\ 'ale_completion_delay',
\ 'ale_completion_enabled',
\ 'ale_completion_max_suggestions',
\ 'ale_disable_lsp',
\ 'ale_echo_cursor',
\ 'ale_echo_msg_error_str',
\ 'ale_echo_msg_format',
@@ -28,6 +29,7 @@ let s:global_variable_list = [
\ 'ale_linter_aliases',
\ 'ale_linters',
\ 'ale_linters_explicit',
\ 'ale_linters_ignore',
\ 'ale_list_vertical',
\ 'ale_list_window_size',
\ 'ale_loclist_msg_format',
@@ -196,6 +198,7 @@ function! s:EchoLSPErrorMessages(all_linter_names) abort
endfunction
function! ale#debugging#Info() abort
let l:buffer = bufnr('')
let l:filetype = &filetype
" We get the list of enabled linters for free by the above function.
@@ -222,10 +225,20 @@ function! ale#debugging#Info() abort
let l:fixers = uniq(sort(l:fixers[0] + l:fixers[1]))
let l:fixers_string = join(map(copy(l:fixers), '"\n " . v:val'), '')
let l:non_ignored_names = map(
\ copy(ale#linter#RemoveIgnored(l:buffer, l:filetype, l:enabled_linters)),
\ 'v:val[''name'']',
\)
let l:ignored_names = filter(
\ copy(l:enabled_names),
\ 'index(l:non_ignored_names, v:val) < 0'
\)
call s:Echo(' Current Filetype: ' . l:filetype)
call s:Echo('Available Linters: ' . string(l:all_names))
call s:EchoLinterAliases(l:all_linters)
call s:Echo(' Enabled Linters: ' . string(l:enabled_names))
call s:Echo(' Ignored Linters: ' . string(l:ignored_names))
call s:Echo(' Suggested Fixers: ' . l:fixers_string)
call s:Echo(' Linter Variables:')
call s:Echo('')

View File

@@ -135,10 +135,6 @@ function! s:GoToLSPDefinition(linter, options, capability) abort
endfunction
function! ale#definition#GoTo(options) abort
if !get(g:, 'ale_ignore_2_7_warnings') && has_key(a:options, 'deprecated_command')
execute 'echom '':' . a:options.deprecated_command . ' is deprecated. Use `let g:ale_ignore_2_7_warnings = 1` to disable this message.'''
endif
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
call s:GoToLSPDefinition(l:linter, a:options, 'definition')
@@ -147,10 +143,6 @@ function! ale#definition#GoTo(options) abort
endfunction
function! ale#definition#GoToType(options) abort
if !get(g:, 'ale_ignore_2_7_warnings') && has_key(a:options, 'deprecated_command')
execute 'echom '':' . a:options.deprecated_command . ' is deprecated. Use `let g:ale_ignore_2_7_warnings = 1` to disable this message.'''
endif
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
" TODO: handle typeDefinition for tsserver if supported by the

View File

@@ -4,6 +4,7 @@
" Remapping of linter problems.
let g:ale_type_map = get(g:, 'ale_type_map', {})
let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {})
if !has_key(s:, 'executable_cache_map')
let s:executable_cache_map = {}
@@ -104,42 +105,6 @@ function! ale#engine#IsCheckingBuffer(buffer) abort
\ || !empty(get(l:info, 'active_other_sources_list', []))
endfunction
" Register a temporary file to be managed with the ALE engine for
" a current job run.
function! ale#engine#ManageFile(buffer, filename) abort
if !get(g:, 'ale_ignore_2_4_warnings')
execute 'echom ''ale#engine#ManageFile is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
call ale#command#ManageFile(a:buffer, a:filename)
endfunction
" Same as the above, but manage an entire directory.
function! ale#engine#ManageDirectory(buffer, directory) abort
if !get(g:, 'ale_ignore_2_4_warnings')
execute 'echom ''ale#engine#ManageDirectory is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
call ale#command#ManageDirectory(a:buffer, a:directory)
endfunction
function! ale#engine#CreateFile(buffer) abort
if !get(g:, 'ale_ignore_2_4_warnings')
execute 'echom ''ale#engine#CreateFile is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
return ale#command#CreateFile(a:buffer)
endfunction
" Create a new temporary directory and manage it in one go.
function! ale#engine#CreateDirectory(buffer) abort
if !get(g:, 'ale_ignore_2_4_warnings')
execute 'echom ''ale#engine#CreateDirectory is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
return ale#command#CreateDirectory(a:buffer)
endfunction
function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort
let l:info = get(g:ale_buffer_info, a:buffer, {})
@@ -192,7 +157,6 @@ function! s:HandleExit(job_info, buffer, output, data) abort
let l:linter = a:job_info.linter
let l:executable = a:job_info.executable
let l:next_chain_index = a:job_info.next_chain_index
" Remove this job from the list.
call ale#engine#MarkLinterInactive(l:buffer_info, l:linter.name)
@@ -207,20 +171,6 @@ function! s:HandleExit(job_info, buffer, output, data) abort
call remove(a:output, -1)
endif
if l:next_chain_index < len(get(l:linter, 'command_chain', []))
let [l:command, l:options] = ale#engine#ProcessChain(
\ a:buffer,
\ l:executable,
\ l:linter,
\ l:next_chain_index,
\ a:output,
\)
call s:RunJob(l:command, l:options)
return
endif
try
let l:loclist = ale#util#GetFunction(l:linter.callback)(a:buffer, a:output)
" Handle the function being unknown, or being deleted.
@@ -307,6 +257,13 @@ function! s:RemapItemTypes(type_map, loclist) abort
endfunction
function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) abort
let l:mappings = ale#GetFilenameMappings(a:buffer, a:linter_name)
if !empty(l:mappings)
" We need to apply reverse filename mapping here.
let l:mappings = ale#filename_mapping#Invert(l:mappings)
endif
let l:bufnr_map = {}
let l:new_loclist = []
@@ -347,13 +304,19 @@ function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist)
let l:item.code = l:old_item.code
endif
if has_key(l:old_item, 'filename')
\&& !ale#path#IsTempName(l:old_item.filename)
let l:old_name = get(l:old_item, 'filename', '')
" Map parsed from output to local filesystem files.
if !empty(l:old_name) && !empty(l:mappings)
let l:old_name = ale#filename_mapping#Map(l:old_name, l:mappings)
endif
if !empty(l:old_name) && !ale#path#IsTempName(l:old_name)
" Use the filename given.
" Temporary files are assumed to be for this buffer,
" and the filename is not included then, because it looks bad
" in the loclist window.
let l:filename = l:old_item.filename
let l:filename = l:old_name
let l:item.filename = l:filename
if has_key(l:old_item, 'bufnr')
@@ -454,20 +417,19 @@ function! s:RunJob(command, options) abort
let l:buffer = a:options.buffer
let l:linter = a:options.linter
let l:output_stream = a:options.output_stream
let l:next_chain_index = a:options.next_chain_index
let l:read_buffer = a:options.read_buffer
let l:read_buffer = a:options.read_buffer && !a:options.lint_file
let l:info = g:ale_buffer_info[l:buffer]
let l:Callback = function('s:HandleExit', [{
\ 'linter': l:linter,
\ 'executable': l:executable,
\ 'next_chain_index': l:next_chain_index,
\}])
let l:result = ale#command#Run(l:buffer, l:command, l:Callback, {
\ 'output_stream': l:output_stream,
\ 'executable': l:executable,
\ 'read_buffer': l:read_buffer,
\ 'log_output': l:next_chain_index >= len(get(l:linter, 'command_chain', [])),
\ 'log_output': 1,
\ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:linter.name),
\})
" Only proceed if the job is being run.
@@ -482,69 +444,7 @@ function! s:RunJob(command, options) abort
return 1
endfunction
" Determine which commands to run for a link in a command chain, or
" just a regular command.
function! ale#engine#ProcessChain(buffer, executable, linter, chain_index, input) abort
let l:output_stream = get(a:linter, 'output_stream', 'stdout')
let l:read_buffer = a:linter.read_buffer
let l:chain_index = a:chain_index
let l:input = a:input
while l:chain_index < len(a:linter.command_chain)
" Run a chain of commands, one asynchronous command after the other,
" so that many programs can be run in a sequence.
let l:chain_item = a:linter.command_chain[l:chain_index]
if l:chain_index == 0
" The first callback in the chain takes only a buffer number.
let l:command = ale#util#GetFunction(l:chain_item.callback)(
\ a:buffer
\)
else
" The second callback in the chain takes some input too.
let l:command = ale#util#GetFunction(l:chain_item.callback)(
\ a:buffer,
\ l:input
\)
endif
" If we have a command to run, execute that.
if !empty(l:command)
" The chain item can override the output_stream option.
if has_key(l:chain_item, 'output_stream')
let l:output_stream = l:chain_item.output_stream
endif
" The chain item can override the read_buffer option.
if has_key(l:chain_item, 'read_buffer')
let l:read_buffer = l:chain_item.read_buffer
elseif l:chain_index != len(a:linter.command_chain) - 1
" Don't read the buffer for commands besides the last one
" in the chain by default.
let l:read_buffer = 0
endif
break
endif
" Command chain items can return an empty string to indicate that
" a command should be skipped, so we should try the next item
" with no input.
let l:input = []
let l:chain_index += 1
endwhile
return [l:command, {
\ 'executable': a:executable,
\ 'buffer': a:buffer,
\ 'linter': a:linter,
\ 'output_stream': l:output_stream,
\ 'next_chain_index': l:chain_index + 1,
\ 'read_buffer': l:read_buffer,
\}]
endfunction
function! s:StopCurrentJobs(buffer, clear_lint_file_jobs) abort
function! s:StopCurrentJobs(buffer, clear_lint_file_jobs, linter_slots) abort
let l:info = get(g:ale_buffer_info, a:buffer, {})
call ale#command#StopJobs(a:buffer, 'linter')
@@ -553,11 +453,25 @@ function! s:StopCurrentJobs(buffer, clear_lint_file_jobs) abort
call ale#command#StopJobs(a:buffer, 'file_linter')
let l:info.active_linter_list = []
else
let l:lint_file_map = {}
" Use a previously computed map of `lint_file` values to find
" linters that are used for linting files.
for [l:lint_file, l:linter] in a:linter_slots
if l:lint_file is 1
let l:lint_file_map[l:linter.name] = 1
endif
endfor
" Keep jobs for linting files when we're only linting buffers.
call filter(l:info.active_linter_list, 'get(v:val, ''lint_file'')')
call filter(l:info.active_linter_list, 'get(l:lint_file_map, v:val.name)')
endif
endfunction
function! ale#engine#Stop(buffer) abort
call s:StopCurrentJobs(a:buffer, 1, [])
endfunction
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove
" problems for linters which are no longer enabled.
@@ -608,10 +522,15 @@ function! s:AddProblemsFromOtherBuffers(buffer, linters) abort
endif
endfunction
function! s:RunIfExecutable(buffer, linter, executable) abort
function! s:RunIfExecutable(buffer, linter, lint_file, executable) abort
if ale#command#IsDeferred(a:executable)
let a:executable.result_callback = {
\ executable -> s:RunIfExecutable(a:buffer, a:linter, executable)
\ executable -> s:RunIfExecutable(
\ a:buffer,
\ a:linter,
\ a:lint_file,
\ executable
\ )
\}
return 1
@@ -619,29 +538,17 @@ function! s:RunIfExecutable(buffer, linter, executable) abort
if ale#engine#IsExecutable(a:buffer, a:executable)
" Use different job types for file or linter jobs.
let l:job_type = a:linter.lint_file ? 'file_linter' : 'linter'
let l:job_type = a:lint_file ? 'file_linter' : 'linter'
call setbufvar(a:buffer, 'ale_job_type', l:job_type)
if has_key(a:linter, 'command_chain')
let [l:command, l:options] = ale#engine#ProcessChain(
\ a:buffer,
\ a:executable,
\ a:linter,
\ 0,
\ []
\)
return s:RunJob(l:command, l:options)
endif
let l:command = ale#linter#GetCommand(a:buffer, a:linter)
let l:options = {
\ 'executable': a:executable,
\ 'buffer': a:buffer,
\ 'linter': a:linter,
\ 'output_stream': get(a:linter, 'output_stream', 'stdout'),
\ 'next_chain_index': 1,
\ 'read_buffer': a:linter.read_buffer,
\ 'lint_file': a:lint_file,
\}
return s:RunJob(l:command, l:options)
@@ -653,22 +560,73 @@ endfunction
" Run a linter for a buffer.
"
" Returns 1 if the linter was successfully run.
function! s:RunLinter(buffer, linter) abort
function! s:RunLinter(buffer, linter, lint_file) abort
if !empty(a:linter.lsp)
return ale#lsp_linter#CheckWithLSP(a:buffer, a:linter)
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
return s:RunIfExecutable(a:buffer, a:linter, l:executable)
return s:RunIfExecutable(a:buffer, a:linter, a:lint_file, l:executable)
endif
return 0
endfunction
function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
" Initialise the buffer information if needed.
let l:new_buffer = ale#engine#InitBufferInfo(a:buffer)
call s:StopCurrentJobs(a:buffer, a:should_lint_file)
function! s:GetLintFileSlots(buffer, linters) abort
let l:linter_slots = []
for l:linter in a:linters
let l:LintFile = l:linter.lint_file
if type(l:LintFile) is v:t_func
let l:LintFile = l:LintFile(a:buffer)
endif
call add(l:linter_slots, [l:LintFile, l:linter])
endfor
return l:linter_slots
endfunction
function! s:GetLintFileValues(slots, Callback) abort
let l:deferred_list = []
let l:new_slots = []
for [l:lint_file, l:linter] in a:slots
while ale#command#IsDeferred(l:lint_file) && has_key(l:lint_file, 'value')
" If we've already computed the return value, use it.
let l:lint_file = l:lint_file.value
endwhile
if ale#command#IsDeferred(l:lint_file)
" If we are going to return the result later, wait for it.
call add(l:deferred_list, l:lint_file)
else
" If we have the value now, coerce it to 0 or 1.
let l:lint_file = l:lint_file is 1
endif
call add(l:new_slots, [l:lint_file, l:linter])
endfor
if !empty(l:deferred_list)
for l:deferred in l:deferred_list
let l:deferred.result_callback =
\ {-> s:GetLintFileValues(l:new_slots, a:Callback)}
endfor
else
call a:Callback(l:new_slots)
endif
endfunction
function! s:RunLinters(
\ buffer,
\ linters,
\ slots,
\ should_lint_file,
\ new_buffer,
\) abort
call s:StopCurrentJobs(a:buffer, a:should_lint_file, a:slots)
call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters)
" We can only clear the results if we aren't checking the buffer.
@@ -676,10 +634,10 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
silent doautocmd <nomodeline> User ALELintPre
for l:linter in a:linters
for [l:lint_file, l:linter] in a:slots
" Only run lint_file linters if we should.
if !l:linter.lint_file || a:should_lint_file
if s:RunLinter(a:buffer, l:linter)
if !l:lint_file || a:should_lint_file
if s:RunLinter(a:buffer, l:linter, l:lint_file)
" If a single linter ran, we shouldn't clear everything.
let l:can_clear_results = 0
endif
@@ -694,11 +652,32 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
" disabled, or ALE itself is disabled.
if l:can_clear_results
call ale#engine#SetResults(a:buffer, [])
elseif l:new_buffer
call s:AddProblemsFromOtherBuffers(a:buffer, a:linters)
elseif a:new_buffer
call s:AddProblemsFromOtherBuffers(
\ a:buffer,
\ map(copy(a:slots), 'v:val[1]')
\)
endif
endfunction
function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
" Initialise the buffer information if needed.
let l:new_buffer = ale#engine#InitBufferInfo(a:buffer)
call s:GetLintFileValues(
\ s:GetLintFileSlots(a:buffer, a:linters),
\ {
\ slots -> s:RunLinters(
\ a:buffer,
\ a:linters,
\ slots,
\ a:should_lint_file,
\ l:new_buffer,
\ )
\ }
\)
endfunction
" Clean up a buffer.
"
" This function will stop all current jobs for the buffer,

View File

@@ -105,11 +105,11 @@ function! ale#events#Init() abort
if g:ale_enabled
if l:text_changed is? 'always' || l:text_changed is# '1'
autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay)
autocmd TextChanged,TextChangedI * call ale#Queue(ale#Var(str2nr(expand('<abuf>')), 'lint_delay'))
elseif l:text_changed is? 'normal'
autocmd TextChanged * call ale#Queue(g:ale_lint_delay)
autocmd TextChanged * call ale#Queue(ale#Var(str2nr(expand('<abuf>')), 'lint_delay'))
elseif l:text_changed is? 'insert'
autocmd TextChangedI * call ale#Queue(g:ale_lint_delay)
autocmd TextChangedI * call ale#Queue(ale#Var(str2nr(expand('<abuf>')), 'lint_delay'))
endif
if g:ale_lint_on_enter
@@ -147,6 +147,10 @@ function! ale#events#Init() abort
autocmd InsertLeave * if exists('*ale#engine#Cleanup') | call ale#virtualtext#ShowCursorWarning() | endif
endif
if g:ale_hover_cursor
autocmd CursorHold * if exists('*ale#lsp#Send') | call ale#hover#ShowTruncatedMessageAtCursor() | endif
endif
if g:ale_close_preview_on_insert
autocmd InsertEnter * if exists('*ale#preview#CloseIfTypeMatches') | call ale#preview#CloseIfTypeMatches('ale-preview') | endif
endif

View File

@@ -0,0 +1,22 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Logic for handling mappings between files
" Invert filesystem mappings so they can be mapped in reverse.
function! ale#filename_mapping#Invert(filename_mappings) abort
return map(copy(a:filename_mappings), '[v:val[1], v:val[0]]')
endfunction
" Given a filename and some filename_mappings, map a filename.
function! ale#filename_mapping#Map(filename, filename_mappings) abort
let l:simplified_filename = ale#path#Simplify(a:filename)
for [l:mapping_from, l:mapping_to] in a:filename_mappings
let l:mapping_from = ale#path#Simplify(l:mapping_from)
if l:simplified_filename[:len(l:mapping_from) - 1] is# l:mapping_from
return l:mapping_to . l:simplified_filename[len(l:mapping_from):]
endif
endfor
return a:filename
endfunction

View File

@@ -1,4 +1,8 @@
call ale#Set('fix_on_save_ignore', {})
" Author: w0rp <devw0rp@gmail.com>
" Description: Functions for fixing code with programs, or other means.
let g:ale_fix_on_save_ignore = get(g:, 'ale_fix_on_save_ignore', {})
let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {})
" Apply fixes queued up for buffers which may be hidden.
" Vim doesn't let you modify hidden buffers.
@@ -11,22 +15,29 @@ function! ale#fix#ApplyQueuedFixes(buffer) abort
call remove(g:ale_fix_buffer_data, a:buffer)
if l:data.changes_made
let l:new_lines = ale#util#SetBufferContents(a:buffer, l:data.output)
try
if l:data.changes_made
let l:new_lines = ale#util#SetBufferContents(a:buffer, l:data.output)
if l:data.should_save
if a:buffer is bufnr('')
if empty(&buftype)
noautocmd :w!
if l:data.should_save
if a:buffer is bufnr('')
if empty(&buftype)
noautocmd :w!
else
set nomodified
endif
else
set nomodified
call writefile(l:new_lines, expand('#' . a:buffer . ':p')) " no-custom-checks
call setbufvar(a:buffer, '&modified', 0)
endif
else
call writefile(l:new_lines, expand('#' . a:buffer . ':p')) " no-custom-checks
call setbufvar(a:buffer, '&modified', 0)
endif
endif
endif
catch /E21/
" If we cannot modify the buffer now, try again later.
let g:ale_fix_buffer_data[a:buffer] = l:data
return
endtry
if l:data.should_save
let l:should_lint = ale#Var(a:buffer, 'fix_on_save')
@@ -64,7 +75,10 @@ function! ale#fix#ApplyFixes(buffer, output) abort
if l:data.lines_before != l:lines
call remove(g:ale_fix_buffer_data, a:buffer)
execute 'echoerr ''The file was changed before fixing finished'''
if !l:data.ignore_file_changed_errors
execute 'echoerr ''The file was changed before fixing finished'''
endif
return
endif
@@ -90,7 +104,6 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort
let l:output = a:job_output
endif
let l:ChainCallback = get(a:job_info, 'chain_with', v:null)
let l:ProcessWith = get(a:job_info, 'process_with', v:null)
" Post-process the output with a function if we have one.
@@ -102,27 +115,17 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort
" otherwise skip this job and use the input from before.
"
" We'll use the input from before for chained commands.
if l:ChainCallback is v:null && !empty(split(join(l:output)))
if !empty(split(join(l:output)))
let l:input = l:output
else
let l:input = a:job_info.input
endif
if l:ChainCallback isnot v:null && !get(g:, 'ale_ignore_2_4_warnings')
execute 'echom ''chain_with is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
let l:next_index = l:ChainCallback is v:null
\ ? a:job_info.callback_index + 1
\ : a:job_info.callback_index
call s:RunFixer({
\ 'buffer': a:buffer,
\ 'input': l:input,
\ 'output': l:output,
\ 'callback_list': a:job_info.callback_list,
\ 'callback_index': l:next_index,
\ 'chain_callback': l:ChainCallback,
\ 'callback_index': a:job_info.callback_index + 1,
\})
endfunction
@@ -135,6 +138,7 @@ function! s:RunJob(result, options) abort
let l:buffer = a:options.buffer
let l:input = a:options.input
let l:fixer_name = a:options.fixer_name
if a:result is 0 || type(a:result) is v:t_list
if type(a:result) is v:t_list
@@ -152,26 +156,21 @@ function! s:RunJob(result, options) abort
endif
let l:command = get(a:result, 'command', '')
let l:ChainWith = get(a:result, 'chain_with', v:null)
if empty(l:command)
" If the command is empty, skip to the next item, or call the
" chain_with function.
" If the command is empty, skip to the next item.
call s:RunFixer({
\ 'buffer': l:buffer,
\ 'input': l:input,
\ 'callback_index': a:options.callback_index + (l:ChainWith is v:null),
\ 'callback_index': a:options.callback_index,
\ 'callback_list': a:options.callback_list,
\ 'chain_callback': l:ChainWith,
\ 'output': [],
\})
return
endif
let l:read_temporary_file = get(a:result, 'read_temporary_file', 0)
" Default to piping the buffer for the last fixer in the chain.
let l:read_buffer = get(a:result, 'read_buffer', l:ChainWith is v:null)
let l:read_buffer = get(a:result, 'read_buffer', 1)
let l:output_stream = get(a:result, 'output_stream', 'stdout')
if l:read_temporary_file
@@ -180,7 +179,6 @@ function! s:RunJob(result, options) abort
let l:Callback = function('s:HandleExit', [{
\ 'input': l:input,
\ 'chain_with': l:ChainWith,
\ 'callback_index': a:options.callback_index,
\ 'callback_list': a:options.callback_list,
\ 'process_with': get(a:result, 'process_with', v:null),
@@ -192,6 +190,7 @@ function! s:RunJob(result, options) abort
\ 'read_buffer': l:read_buffer,
\ 'input': l:input,
\ 'log_output': 0,
\ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:fixer_name),
\})
if empty(l:run_result)
@@ -215,32 +214,22 @@ function! s:RunFixer(options) abort
return
endif
let l:ChainCallback = get(a:options, 'chain_callback', v:null)
let l:Function = l:ChainCallback isnot v:null
\ ? ale#util#GetFunction(l:ChainCallback)
\ : a:options.callback_list[l:index]
let [l:fixer_name, l:Function] = a:options.callback_list[l:index]
" Record new jobs started as fixer jobs.
call setbufvar(l:buffer, 'ale_job_type', 'fixer')
if l:ChainCallback isnot v:null
" Chained commands accept (buffer, output, [input])
let l:result = ale#util#FunctionArgCount(l:Function) == 2
\ ? call(l:Function, [l:buffer, a:options.output])
\ : call(l:Function, [l:buffer, a:options.output, copy(l:input)])
else
" Regular fixer commands accept (buffer, [input])
let l:result = ale#util#FunctionArgCount(l:Function) == 1
\ ? call(l:Function, [l:buffer])
\ : call(l:Function, [l:buffer, copy(l:input)])
endif
" Regular fixer commands accept (buffer, [input])
let l:result = ale#util#FunctionArgCount(l:Function) == 1
\ ? call(l:Function, [l:buffer])
\ : call(l:Function, [l:buffer, copy(l:input)])
call s:RunJob(l:result, {
\ 'buffer': l:buffer,
\ 'input': l:input,
\ 'callback_list': a:options.callback_list,
\ 'callback_index': l:index,
\ 'fixer_name': l:fixer_name,
\})
endfunction
@@ -308,16 +297,24 @@ function! s:GetCallbacks(buffer, fixing_flag, fixers) abort
" Variables with capital characters are needed, or Vim will complain about
" funcref variables.
for l:Item in l:callback_list
" Try to capture the names of registered fixer names, so we can use
" them for filename mapping or other purposes later.
let l:fixer_name = v:null
if type(l:Item) is v:t_string
let l:Func = ale#fix#registry#GetFunc(l:Item)
if !empty(l:Func)
let l:fixer_name = l:Item
let l:Item = l:Func
endif
endif
try
call add(l:corrected_list, ale#util#GetFunction(l:Item))
call add(l:corrected_list, [
\ l:fixer_name,
\ ale#util#GetFunction(l:Item)
\])
catch /E475/
" Rethrow exceptions for failing to get a function so we can print
" a friendly message about it.
@@ -335,6 +332,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort
\ 'lines_before': getbufline(a:buffer, 1, '$'),
\ 'done': 0,
\ 'should_save': a:fixing_flag is# 'save_file',
\ 'ignore_file_changed_errors': a:fixing_flag is# '!',
\ 'temporary_directory_list': [],
\}
endfunction
@@ -343,19 +341,23 @@ endfunction
"
" Returns 0 if no fixes can be applied, and 1 if fixing can be done.
function! ale#fix#Fix(buffer, fixing_flag, ...) abort
if a:fixing_flag isnot# '' && a:fixing_flag isnot# 'save_file'
throw "fixing_flag must be either '' or 'save_file'"
if a:fixing_flag isnot# ''
\&& a:fixing_flag isnot# '!'
\&& a:fixing_flag isnot# 'save_file'
throw "fixing_flag must be '', '!', or 'save_file'"
endif
try
let l:callback_list = s:GetCallbacks(a:buffer, a:fixing_flag, a:000)
catch /E700\|BADNAME/
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
\ 'There is no fixer named `%s`. Check :ALEFixSuggest',
\ l:function_name,
\)
execute 'echom l:echo_message'
if a:fixing_flag isnot# '!'
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
\ 'There is no fixer named `%s`. Check :ALEFixSuggest',
\ l:function_name,
\)
execute 'echom l:echo_message'
endif
return 0
endtry
@@ -389,3 +391,4 @@ endfunction
augroup ALEBufferFixGroup
autocmd!
autocmd BufEnter * call ale#fix#ApplyQueuedFixes(str2nr(expand('<abuf>')))
augroup END

View File

@@ -160,6 +160,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['php'],
\ 'description': 'Fix PHP files with php-cs-fixer.',
\ },
\ 'astyle': {
\ 'function': 'ale#fixers#astyle#Fix',
\ 'suggested_filetypes': ['c', 'cpp'],
\ 'description': 'Fix C/C++ with astyle.',
\ },
\ 'clangtidy': {
\ 'function': 'ale#fixers#clangtidy#Fix',
\ 'suggested_filetypes': ['c', 'cpp', 'objc'],
@@ -360,11 +365,21 @@ let s:default_registry = {
\ 'suggested_filetypes': ['nix'],
\ 'description': 'A formatter for Nix code',
\ },
\ 'remark-lint': {
\ 'function': 'ale#fixers#remark_lint#Fix',
\ 'suggested_filetypes': ['markdown'],
\ 'description': 'Fix markdown files with remark-lint',
\ },
\ 'html-beautify': {
\ 'function': 'ale#fixers#html_beautify#Fix',
\ 'suggested_filetypes': ['html', 'htmldjango'],
\ 'description': 'Fix HTML files with html-beautify.',
\ },
\ 'dhall': {
\ 'function': 'ale#fixers#dhall#Fix',
\ 'suggested_filetypes': ['dhall'],
\ 'description': 'Fix Dhall files with dhall-format.',
\ },
\}
" Reset the function registry to the default entries.

View File

@@ -0,0 +1,59 @@
" Author: James Kim <jhlink@users.noreply.github.com>
" Description: Fix C/C++ files with astyle.
function! s:set_variables() abort
for l:ft in ['c', 'cpp']
call ale#Set(l:ft . '_astyle_executable', 'astyle')
call ale#Set(l:ft . '_astyle_project_options', '')
endfor
endfunction
call s:set_variables()
function! ale#fixers#astyle#Var(buffer, name) abort
let l:ft = getbufvar(str2nr(a:buffer), '&filetype')
let l:ft = l:ft =~# 'cpp' ? 'cpp' : 'c'
return ale#Var(a:buffer, l:ft . '_astyle_' . a:name)
endfunction
" Try to find a project options file.
function! ale#fixers#astyle#FindProjectOptions(buffer) abort
let l:proj_options = ale#fixers#astyle#Var(a:buffer, 'project_options')
" If user has set project options variable then use it and skip any searching.
" This would allow users to use project files named differently than .astylerc.
if !empty(l:proj_options)
return l:proj_options
endif
" Try to find nearest .astylerc file.
let l:proj_options = fnamemodify(ale#path#FindNearestFile(a:buffer, '.astylerc'), ':t')
if !empty(l:proj_options)
return l:proj_options
endif
" Try to find nearest _astylerc file.
let l:proj_options = fnamemodify(ale#path#FindNearestFile(a:buffer, '_astylerc'), ':t')
if !empty(l:proj_options)
return l:proj_options
endif
" If no project options file is found return an empty string.
return ''
endfunction
function! ale#fixers#astyle#Fix(buffer) abort
let l:executable = ale#fixers#astyle#Var(a:buffer, 'executable')
let l:proj_options = ale#fixers#astyle#FindProjectOptions(a:buffer)
let l:command = ' --stdin=' . ale#Escape(expand('#' . a:buffer))
return {
\ 'command': ale#Escape(l:executable)
\ . (empty(l:proj_options) ? '' : ' --project=' . l:proj_options)
\ . l:command
\}
endfunction

View File

@@ -0,0 +1,23 @@
" Author: Pat Brisbin <pbrisbin@gmail.com>
" Description: Integration of dhall-format with ALE.
call ale#Set('dhall_format_executable', 'dhall')
function! ale#fixers#dhall#GetExecutable(buffer) abort
let l:executable = ale#Var(a:buffer, 'dhall_format_executable')
" Dhall is written in Haskell and commonly installed with Stack
return ale#handlers#haskell_stack#EscapeExecutable(l:executable, 'dhall')
endfunction
function! ale#fixers#dhall#Fix(buffer) abort
let l:executable = ale#fixers#dhall#GetExecutable(a:buffer)
return {
\ 'command': l:executable
\ . ' format'
\ . ' --inplace'
\ . ' %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@@ -3,7 +3,6 @@
function! ale#fixers#ktlint#Fix(buffer) abort
return {
\ 'command': ale#handlers#ktlint#GetCommand(a:buffer) . ' --format',
\ 'read_temporary_file': 1,
\ 'command': ale#handlers#ktlint#GetCommand(a:buffer) . ' --format'
\}
endfunction

View File

@@ -10,9 +10,7 @@ function! ale#fixers#latexindent#Fix(buffer) abort
return {
\ 'command': ale#Escape(l:executable)
\ . ' -l -w'
\ . ' -l'
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@@ -5,14 +5,13 @@ call ale#Set('ocaml_ocamlformat_executable', 'ocamlformat')
call ale#Set('ocaml_ocamlformat_options', '')
function! ale#fixers#ocamlformat#Fix(buffer) abort
let l:filename = expand('#' . a:buffer . ':p')
let l:executable = ale#Var(a:buffer, 'ocaml_ocamlformat_executable')
let l:options = ale#Var(a:buffer, 'ocaml_ocamlformat_options')
return {
\ 'command': ale#Escape(l:executable)
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' --name=' . ale#Escape(l:filename)
\ . ' --name=%s'
\ . ' -'
\}
endfunction

View File

@@ -34,6 +34,21 @@ function! ale#fixers#prettier#ProcessPrettierDOutput(buffer, output) abort
return a:output
endfunction
function! ale#fixers#prettier#GetProjectRoot(buffer) abort
let l:config = ale#path#FindNearestFile(a:buffer, '.prettierignore')
if !empty(l:config)
return fnamemodify(l:config, ':h')
endif
" Fall back to the directory of the buffer
return fnamemodify(bufname(a:buffer), ':p:h')
endfunction
function! ale#fixers#prettier#CdProjectRoot(buffer) abort
return ale#path#CdString(ale#fixers#prettier#GetProjectRoot(a:buffer))
endfunction
function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort
let l:executable = ale#fixers#prettier#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'javascript_prettier_options')
@@ -97,7 +112,7 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort
" 1.4.0 is the first version with --stdin-filepath
if ale#semver#GTE(a:version, [1, 4, 0])
return {
\ 'command': ale#path#BufferCdString(a:buffer)
\ 'command': ale#fixers#prettier#CdProjectRoot(a:buffer)
\ . ale#Escape(l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --stdin-filepath %s --stdin',

View File

@@ -17,8 +17,8 @@ function! ale#fixers#prettier_standard#Fix(buffer) abort
return {
\ 'command': ale#Escape(ale#fixers#prettier_standard#GetExecutable(a:buffer))
\ . ' %t'
\ . ' --stdin'
\ . ' --stdin-filepath=%s'
\ . ' ' . l:options,
\ 'read_temporary_file': 1,
\}
endfunction

View File

@@ -0,0 +1,24 @@
" Author: blyoa <blyoa110@gmail.com>
" Description: Fixing files with remark-lint.
call ale#Set('markdown_remark_lint_executable', 'remark')
call ale#Set('markdown_remark_lint_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('markdown_remark_lint_options', '')
function! ale#fixers#remark_lint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'markdown_remark_lint', [
\ 'node_modules/remark-cli/cli.js',
\ 'node_modules/.bin/remark',
\])
endfunction
function! ale#fixers#remark_lint#Fix(buffer) abort
let l:executable = ale#fixers#remark_lint#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'markdown_remark_lint_options')
return {
\ 'command': ale#Escape(l:executable)
\ . (!empty(l:options) ? ' ' . l:options : ''),
\}
endfunction

View File

@@ -1,20 +1,40 @@
call ale#Set('ruby_rubocop_options', '')
call ale#Set('ruby_rubocop_auto_correct_all', 0)
call ale#Set('ruby_rubocop_executable', 'rubocop')
" Rubocop fixer outputs diagnostics first and then the fixed
" output. These are delimited by a "=======" string that we
" look for to remove everything before it.
function! ale#fixers#rubocop#PostProcess(buffer, output) abort
let l:line = 0
for l:output in a:output
let l:line = l:line + 1
if l:output =~# "^=\\+$"
break
endif
endfor
return a:output[l:line :]
endfunction
function! ale#fixers#rubocop#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable')
let l:config = ale#path#FindNearestFile(a:buffer, '.rubocop.yml')
let l:options = ale#Var(a:buffer, 'ruby_rubocop_options')
let l:auto_correct_all = ale#Var(a:buffer, 'ruby_rubocop_auto_correct_all')
return ale#ruby#EscapeExecutable(l:executable, 'rubocop')
\ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --auto-correct --force-exclusion %t'
\ . (l:auto_correct_all ? ' --auto-correct-all' : ' --auto-correct')
\ . ' --force-exclusion --stdin %s'
endfunction
function! ale#fixers#rubocop#Fix(buffer) abort
return {
\ 'command': ale#fixers#rubocop#GetCommand(a:buffer),
\ 'read_temporary_file': 1,
\ 'process_with': 'ale#fixers#rubocop#PostProcess'
\}
endfunction

View File

@@ -27,7 +27,7 @@ function! ale#fixers#standard#Fix(buffer) abort
return {
\ 'command': ale#node#Executable(a:buffer, l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --fix %t',
\ . ' --fix --stdin < %s > %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@@ -16,7 +16,7 @@ function! ale#fixers#tslint#Fix(buffer) abort
return {
\ 'command': ale#node#Executable(a:buffer, l:executable)
\ . l:tslint_config_option
\ . ' --fix %t',
\ . ' --outputAbsolutePaths --fix %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@@ -17,3 +17,10 @@ function! ale#handlers#ccls#GetProjectRoot(buffer) abort
" Fall back on default project root detection.
return ale#c#FindProjectRoot(a:buffer)
endfunction
function! ale#handlers#ccls#GetInitOpts(buffer, init_options_var) abort
let l:build_dir = ale#c#GetBuildDirectory(a:buffer)
let l:init_options = empty(l:build_dir) ? {} : {'compilationDatabaseDirectory': l:build_dir}
return extend(l:init_options, ale#Var(a:buffer, a:init_options_var))
endfunction

View File

@@ -44,16 +44,21 @@ endfunction
function! ale#handlers#cppcheck#HandleCppCheckFormat(buffer, lines) abort
" Look for lines like the following.
"
" [test.cpp:5]: (error) Array 'a[10]' accessed at index 10, which is out of bounds
let l:pattern = '\v^\[(.+):(\d+)\]: \(([a-z]+)\) (.+)$'
"test.cpp:974:6: error: Array 'n[3]' accessed at index 3, which is out of bounds. [arrayIndexOutOfBounds]\
" n[3]=3;
" ^
let l:pattern = '\v^(\f+):(\d+):(\d+): (\w+): (.*) \[(\w+)\]\'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
if ale#path#IsBufferPath(a:buffer, l:match[1])
call add(l:output, {
\ 'lnum': str2nr(l:match[2]),
\ 'type': l:match[3] is# 'error' ? 'E' : 'W',
\ 'text': l:match[4],
\ 'lnum': str2nr(l:match[2]),
\ 'col': str2nr(l:match[3]),
\ 'type': l:match[4] is# 'error' ? 'E' : 'W',
\ 'sub_type': l:match[4] is# 'style' ? 'style' : '',
\ 'text': l:match[5],
\ 'code': l:match[6]
\})
endif
endfor

View File

@@ -10,7 +10,7 @@ let s:pragma_error = '#pragma once in main file'
" <stdin>:8:5: warning: conversion lacks type at end of format [-Wformat=]
" <stdin>:10:27: error: invalid operands to binary - (have int and char *)
" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004]
let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$'
let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+)?:?(\d+)?:? ([^:]+): (.+)$'
let s:inline_pattern = '\v inlined from .* at \<stdin\>:(\d+):(\d+):$'
function! s:IsHeaderFile(filename) abort
@@ -117,6 +117,23 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
if !empty(l:output)
if !has_key(l:output[-1], 'detail')
let l:output[-1].detail = l:output[-1].text
" handle macro expansion errors/notes
if l:match[5] =~? '^in expansion of macro \w*\w$'
" if the macro expansion is in the file we're in, add
" the lnum and col keys to the previous error
if l:match[1] is# '<stdin>'
\ && !has_key(l:output[-1], 'col')
let l:output[-1].lnum = str2nr(l:match[2])
let l:output[-1].col = str2nr(l:match[3])
else
" the error is not in the current file, and since
" macro expansion errors don't show the full path to
" the error from the current file, we have to just
" give out a generic error message
let l:output[-1].text = 'Error found in macro expansion. See :ALEDetail'
endif
endif
endif
let l:output[-1].detail = l:output[-1].detail . "\n"

View File

@@ -6,9 +6,12 @@
"
" Author: Ben Paxton <ben@gn32.uk>
" Description: moved to generic Golang file from govet
"
" Author: mostfunkyduck <mostfunkyduck@protonmail.com>
" Description: updated to work with go 1.14
function! ale#handlers#go#Handler(buffer, lines) abort
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$'
let l:pattern = '\v^%(vet: )?([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$'
let l:output = []
let l:dir = expand('#' . a:buffer . ':p:h')

View File

@@ -0,0 +1,71 @@
" Author: suoto <andre820@gmail.com>
" Description: Adds support for HDL Code Checker, which wraps vcom/vlog, ghdl
" or xvhdl. More info on https://github.com/suoto/hdl_checker
call ale#Set('hdl_checker_executable', 'hdl_checker')
call ale#Set('hdl_checker_config_file', has('unix') ? '.hdl_checker.config' : '_hdl_checker.config')
call ale#Set('hdl_checker_options', '')
" Use this as a function so we can mock it on testing. Need to do this because
" test files are inside /testplugin (which refers to the ale repo), which will
" always have a .git folder
function! ale#handlers#hdl_checker#IsDotGit(path) abort
return ! empty(a:path) && isdirectory(a:path)
endfunction
" Sould return (in order of preference)
" 1. Nearest config file
" 2. Nearest .git directory
" 3. The current path
function! ale#handlers#hdl_checker#GetProjectRoot(buffer) abort
let l:project_root = ale#path#FindNearestFile(
\ a:buffer,
\ ale#Var(a:buffer, 'hdl_checker_config_file'))
if !empty(l:project_root)
return fnamemodify(l:project_root, ':h')
endif
" Search for .git to use as root
let l:project_root = ale#path#FindNearestDirectory(a:buffer, '.git')
if ale#handlers#hdl_checker#IsDotGit(l:project_root)
return fnamemodify(l:project_root, ':h:h')
endif
endfunction
function! ale#handlers#hdl_checker#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'hdl_checker_executable')
endfunction
function! ale#handlers#hdl_checker#GetCommand(buffer) abort
let l:command = ale#Escape(ale#handlers#hdl_checker#GetExecutable(a:buffer)) . ' --lsp'
" Add extra parameters only if config has been set
let l:options = ale#Var(a:buffer, 'hdl_checker_options')
if ! empty(l:options)
let l:command = l:command . ' ' . l:options
endif
return l:command
endfunction
" To allow testing
function! ale#handlers#hdl_checker#GetInitOptions(buffer) abort
return {'project_file': ale#Var(a:buffer, 'hdl_checker_config_file')}
endfunction
" Define the hdl_checker linter for a given filetype.
function! ale#handlers#hdl_checker#DefineLinter(filetype) abort
call ale#linter#Define(a:filetype, {
\ 'name': 'hdl-checker',
\ 'lsp': 'stdio',
\ 'language': a:filetype,
\ 'executable': function('ale#handlers#hdl_checker#GetExecutable'),
\ 'command': function('ale#handlers#hdl_checker#GetCommand'),
\ 'project_root': function('ale#handlers#hdl_checker#GetProjectRoot'),
\ 'initialization_options': function('ale#handlers#hdl_checker#GetInitOptions'),
\ })
endfunction

View File

@@ -13,7 +13,7 @@ function! ale#handlers#ktlint#GetCommand(buffer) abort
return ale#Escape(l:executable)
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . (empty(l:rulesets) ? '' : ' ' . l:rulesets)
\ . ' %t'
\ . ' --stdin'
endfunction
function! ale#handlers#ktlint#GetRulesets(buffer) abort

View File

@@ -2,15 +2,22 @@
" Description: Adds support for markdownlint
function! ale#handlers#markdownlint#Handle(buffer, lines) abort
let l:pattern=': \(\d*\): \(MD\d\{3}\)\(\/\)\([A-Za-z0-9-]\+\)\(.*\)$'
let l:pattern=': \?\(\d\+\)\(:\(\d\+\)\?\)\? \(MD\d\{3}/[A-Za-z0-9-/]\+\) \(.*\)$'
let l:output=[]
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
let l:result = ({
\ 'lnum': l:match[1] + 0,
\ 'text': '(' . l:match[2] . l:match[3] . l:match[4] . ')' . l:match[5],
\ 'code': l:match[4],
\ 'text': l:match[5],
\ 'type': 'W',
\})
if len(l:match[3]) > 0
let l:result.col = (l:match[3] + 0)
endif
call add(l:output, l:result)
endfor
return l:output

View File

@@ -4,17 +4,24 @@
function! ale#handlers#sh#GetShellType(buffer) abort
let l:bang_line = get(getbufline(a:buffer, 1), 0, '')
let l:command = ''
" Take the shell executable from the hashbang, if we can.
if l:bang_line[:1] is# '#!'
" Remove options like -e, etc.
let l:command = substitute(l:bang_line, ' --\?[a-zA-Z0-9]\+', '', 'g')
for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh']
if l:command =~# l:possible_shell . '\s*$'
return l:possible_shell
endif
endfor
endif
" If we couldn't find a hashbang, try the filetype
if l:command is# ''
let l:command = &filetype
endif
for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh']
if l:command =~# l:possible_shell . '\s*$'
return l:possible_shell
endif
endfor
return ''
endfunction

View File

@@ -42,6 +42,8 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
\&& exists('*balloon_show')
\&& ale#Var(l:options.buffer, 'set_balloons')
call balloon_show(a:response.body.displayString)
elseif get(l:options, 'truncated_echo', 0)
call ale#cursor#TruncatedEcho(split(a:response.body.displayString, "\n")[0])
elseif g:ale_hover_to_preview
call ale#preview#Show(split(a:response.body.displayString, "\n"), {
\ 'filetype': 'ale-preview.message',
@@ -54,6 +56,137 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
endif
endfunction
" Convert a language name to another one.
" The language name could be an empty string or v:null
function! s:ConvertLanguageName(language) abort
return a:language
endfunction
function! ale#hover#ParseLSPResult(contents) abort
let l:includes = {}
let l:highlights = []
let l:lines = []
let l:list = type(a:contents) is v:t_list ? a:contents : [a:contents]
let l:region_index = 0
for l:item in l:list
if !empty(l:lines)
call add(l:lines, '')
endif
if type(l:item) is v:t_dict && has_key(l:item, 'kind')
if l:item.kind is# 'markdown'
" Handle markdown values as we handle strings below.
let l:item = get(l:item, 'value', '')
elseif l:item.kind is# 'plaintext'
" We shouldn't try to parse plaintext as markdown.
" Pass the lines on and skip parsing them.
call extend(l:lines, split(get(l:item, 'value', ''), "\n"))
continue
endif
endif
let l:marked_list = []
" If the item is a string, then we should parse it as Markdown text.
if type(l:item) is v:t_string
let l:fence_language = v:null
let l:fence_lines = []
for l:line in split(l:item, "\n")
if l:fence_language is v:null
" Look for the start of a code fence. (```python, etc.)
let l:match = matchlist(l:line, '^```\(.*\)$')
if !empty(l:match)
let l:fence_language = l:match[1]
if !empty(l:marked_list)
call add(l:fence_lines, '')
endif
else
if !empty(l:marked_list)
\&& l:marked_list[-1][0] isnot v:null
call add(l:marked_list, [v:null, ['']])
endif
call add(l:marked_list, [v:null, [l:line]])
endif
elseif l:line =~# '^```$'
" When we hit the end of a code fence, pass the fenced
" lines on to the next steps below.
call add(l:marked_list, [l:fence_language, l:fence_lines])
let l:fence_language = v:null
let l:fence_lines = []
else
" Gather lines inside of a code fence.
call add(l:fence_lines, l:line)
endif
endfor
" If the result from the LSP server is a {language: ..., value: ...}
" Dictionary, then that should be interpreted as if it was:
"
" ```${language}
" ${value}
" ```
elseif type(l:item) is v:t_dict
\&& has_key(l:item, 'language')
\&& type(l:item.language) is v:t_string
\&& has_key(l:item, 'value')
\&& type(l:item.value) is v:t_string
call add(
\ l:marked_list,
\ [l:item.language, split(l:item.value, "\n")],
\)
endif
for [l:language, l:marked_lines] in l:marked_list
if l:language is v:null
" NOTE: We could handle other Markdown formatting here.
call map(
\ l:marked_lines,
\ 'substitute(v:val, ''\\_'', ''_'', ''g'')',
\)
else
let l:language = s:ConvertLanguageName(l:language)
if !empty(l:language)
let l:includes[l:language] = printf(
\ 'syntax/%s.vim',
\ l:language,
\)
let l:start = len(l:lines) + 1
let l:end = l:start + len(l:marked_lines)
let l:region_index += 1
call add(l:highlights, 'syntax region'
\ . ' ALE_hover_' . l:region_index
\ . ' start=/\%' . l:start . 'l/'
\ . ' end=/\%' . l:end . 'l/'
\ . ' contains=@ALE_hover_' . l:language
\)
endif
endif
call extend(l:lines, l:marked_lines)
endfor
endfor
let l:include_commands = []
for [l:language, l:lang_path] in sort(items(l:includes))
call add(l:include_commands, 'unlet! b:current_syntax')
call add(
\ l:include_commands,
\ printf('syntax include @ALE_hover_%s %s', l:language, l:lang_path),
\)
endfor
return [l:include_commands + l:highlights, l:lines]
endfunction
function! ale#hover#HandleLSPResponse(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:hover_map, a:response.id)
@@ -80,37 +213,25 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
return
endif
let l:result = l:result.contents
let [l:commands, l:lines] = ale#hover#ParseLSPResult(l:result.contents)
if type(l:result) is v:t_string
" The result can be just a string.
let l:result = [l:result]
endif
if type(l:result) is v:t_dict
" If the result is an object, then it's markup content.
let l:result = [l:result.value]
endif
if type(l:result) is v:t_list
" Replace objects with text values.
call map(l:result, 'type(v:val) is v:t_string ? v:val : v:val.value')
let l:str = join(l:result, "\n")
let l:str = substitute(l:str, '^\s*\(.\{-}\)\s*$', '\1', '')
if !empty(l:str)
if get(l:options, 'hover_from_balloonexpr', 0)
\&& exists('*balloon_show')
\&& ale#Var(l:options.buffer, 'set_balloons')
call balloon_show(l:str)
elseif g:ale_hover_to_preview
call ale#preview#Show(split(l:str, "\n"), {
\ 'filetype': 'ale-preview.message',
\ 'stay_here': 1,
\})
else
call ale#util#ShowMessage(l:str)
endif
if !empty(l:lines)
if get(l:options, 'hover_from_balloonexpr', 0)
\&& exists('*balloon_show')
\&& ale#Var(l:options.buffer, 'set_balloons')
call balloon_show(join(l:lines, "\n"))
elseif get(l:options, 'truncated_echo', 0)
call ale#cursor#TruncatedEcho(l:lines[0])
elseif g:ale_hover_to_preview
call ale#preview#Show(l:lines, {
\ 'filetype': 'ale-preview.message',
\ 'stay_here': 1,
\ 'commands': l:commands,
\})
else
call ale#util#ShowMessage(join(l:lines, "\n"), {
\ 'commands': l:commands,
\})
endif
endif
endif
@@ -143,7 +264,10 @@ function! s:OnReady(line, column, opt, linter, lsp_details) abort
" hover position probably won't make sense.
call ale#lsp#NotifyForChanges(l:id, l:buffer)
let l:column = min([a:column, len(getbufline(l:buffer, a:line)[0])])
let l:column = max([
\ min([a:column, len(getbufline(l:buffer, a:line)[0])]),
\ 1,
\])
let l:message = ale#lsp#message#Hover(l:buffer, a:line, l:column)
endif
@@ -156,6 +280,7 @@ function! s:OnReady(line, column, opt, linter, lsp_details) abort
\ 'column': l:column,
\ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0),
\ 'show_documentation': get(a:opt, 'show_documentation', 0),
\ 'truncated_echo': get(a:opt, 'truncated_echo', 0),
\}
endfunction
@@ -181,6 +306,8 @@ function! ale#hover#Show(buffer, line, col, opt) abort
endfor
endfunction
let s:last_pos = [0, 0, 0]
" This function implements the :ALEHover command.
function! ale#hover#ShowAtCursor() abort
let l:buffer = bufnr('')
@@ -189,6 +316,25 @@ function! ale#hover#ShowAtCursor() abort
call ale#hover#Show(l:buffer, l:pos[1], l:pos[2], {})
endfunction
function! ale#hover#ShowTruncatedMessageAtCursor() abort
let l:buffer = bufnr('')
let l:pos = getpos('.')[0:2]
if l:pos != s:last_pos
let s:last_pos = l:pos
let [l:info, l:loc] = ale#util#FindItemAtCursor(l:buffer)
if empty(l:loc)
call ale#hover#Show(
\ l:buffer,
\ l:pos[1],
\ l:pos[2],
\ {'truncated_echo': 1},
\)
endif
endif
endfunction
" This function implements the :ALEDocumentation command.
function! ale#hover#ShowDocumentationAtCursor() abort
let l:buffer = bufnr('')

View File

@@ -14,6 +14,7 @@ let s:default_ale_linter_aliases = {
\ 'csh': 'sh',
\ 'javascriptreact': ['javascript', 'jsx'],
\ 'plaintex': 'tex',
\ 'ps1': 'powershell',
\ 'rmarkdown': 'r',
\ 'rmd': 'r',
\ 'systemverilog': 'verilog',
@@ -31,7 +32,7 @@ let s:default_ale_linter_aliases = {
"
" No linters are used for plaintext files by default.
"
" Only cargo is enabled for Rust by default.
" Only cargo and rls are enabled for Rust by default.
" rpmlint is disabled by default because it can result in code execution.
" hhast is disabled by default because it executes code in the project root.
"
@@ -44,8 +45,8 @@ let s:default_ale_linters = {
\ 'help': [],
\ 'perl': ['perlcritic'],
\ 'perl6': [],
\ 'python': ['flake8', 'mypy', 'pylint'],
\ 'rust': ['cargo'],
\ 'python': ['flake8', 'mypy', 'pylint', 'pyright'],
\ 'rust': ['cargo', 'rls'],
\ 'spec': [],
\ 'text': [],
\ 'vue': ['eslint', 'vls'],
@@ -76,10 +77,6 @@ function! s:IsBoolean(value) abort
return type(a:value) is v:t_number && (a:value == 0 || a:value == 1)
endfunction
function! s:LanguageGetter(buffer) dict abort
return l:self.language
endfunction
function! ale#linter#PreProcess(filetype, linter) abort
if type(a:linter) isnot v:t_dict
throw 'The linter object must be a Dictionary'
@@ -113,14 +110,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
if !l:needs_executable
if has_key(a:linter, 'executable')
\|| has_key(a:linter, 'executable_callback')
throw '`executable` and `executable_callback` cannot be used when lsp == ''socket'''
endif
elseif has_key(a:linter, 'executable_callback')
let l:obj.executable_callback = a:linter.executable_callback
if !s:IsCallback(l:obj.executable_callback)
throw '`executable_callback` must be a callback if defined'
throw '`executable` cannot be used when lsp == ''socket'''
endif
elseif has_key(a:linter, 'executable')
let l:obj.executable = a:linter.executable
@@ -130,54 +120,12 @@ function! ale#linter#PreProcess(filetype, linter) abort
throw '`executable` must be a String or Function if defined'
endif
else
throw 'Either `executable` or `executable_callback` must be defined'
throw '`executable` must be defined'
endif
if !l:needs_command
if has_key(a:linter, 'command')
\|| has_key(a:linter, 'command_callback')
\|| has_key(a:linter, 'command_chain')
throw '`command` and `command_callback` and `command_chain` cannot be used when lsp == ''socket'''
endif
elseif has_key(a:linter, 'command_chain')
let l:obj.command_chain = a:linter.command_chain
if type(l:obj.command_chain) isnot v:t_list
throw '`command_chain` must be a List'
endif
if empty(l:obj.command_chain)
throw '`command_chain` must contain at least one item'
endif
let l:link_index = 0
for l:link in l:obj.command_chain
let l:err_prefix = 'The `command_chain` item ' . l:link_index . ' '
if !s:IsCallback(get(l:link, 'callback'))
throw l:err_prefix . 'must define a `callback` function'
endif
if has_key(l:link, 'output_stream')
if type(l:link.output_stream) isnot v:t_string
\|| index(['stdout', 'stderr', 'both'], l:link.output_stream) < 0
throw l:err_prefix . '`output_stream` flag must be '
\ . "'stdout', 'stderr', or 'both'"
endif
endif
if has_key(l:link, 'read_buffer') && !s:IsBoolean(l:link.read_buffer)
throw l:err_prefix . 'value for `read_buffer` must be `0` or `1`'
endif
let l:link_index += 1
endfor
elseif has_key(a:linter, 'command_callback')
let l:obj.command_callback = a:linter.command_callback
if !s:IsCallback(l:obj.command_callback)
throw '`command_callback` must be a callback if defined'
throw '`command` cannot be used when lsp == ''socket'''
endif
elseif has_key(a:linter, 'command')
let l:obj.command = a:linter.command
@@ -187,22 +135,12 @@ function! ale#linter#PreProcess(filetype, linter) abort
throw '`command` must be a String or Function if defined'
endif
else
throw 'Either `command`, `executable_callback`, `command_chain` '
\ . 'must be defined'
endif
if (
\ has_key(a:linter, 'command')
\ + has_key(a:linter, 'command_chain')
\ + has_key(a:linter, 'command_callback')
\) > 1
throw 'Only one of `command`, `command_callback`, or `command_chain` '
\ . 'should be set'
throw '`command` must be defined'
endif
if !l:needs_address
if has_key(a:linter, 'address') || has_key(a:linter, 'address_callback')
throw '`address` or `address_callback` cannot be used when lsp != ''socket'''
if has_key(a:linter, 'address')
throw '`address` cannot be used when lsp != ''socket'''
endif
elseif has_key(a:linter, 'address')
if type(a:linter.address) isnot v:t_string
@@ -211,41 +149,17 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
let l:obj.address = a:linter.address
elseif has_key(a:linter, 'address_callback')
let l:obj.address_callback = a:linter.address_callback
if !s:IsCallback(l:obj.address_callback)
throw '`address_callback` must be a callback if defined'
endif
else
throw '`address` or `address_callback` must be defined for getting the LSP address'
throw '`address` must be defined for getting the LSP address'
endif
if l:needs_lsp_details
if has_key(a:linter, 'language_callback')
if has_key(a:linter, 'language')
throw 'Only one of `language` or `language_callback` '
\ . 'should be set'
endif
" Default to using the filetype as the language.
let l:obj.language = get(a:linter, 'language', a:filetype)
let l:obj.language_callback = get(a:linter, 'language_callback')
if !s:IsCallback(l:obj.language_callback)
throw '`language_callback` must be a callback for LSP linters'
endif
else
" Default to using the filetype as the language.
let l:Language = get(a:linter, 'language', a:filetype)
if type(l:Language) is v:t_string
" Make 'language_callback' return the 'language' value.
let l:obj.language = l:Language
let l:obj.language_callback = function('s:LanguageGetter')
elseif type(l:Language) is v:t_func
let l:obj.language_callback = l:Language
else
throw '`language` must be a String or Funcref'
endif
if type(l:obj.language) isnot v:t_string
\&& type(l:obj.language) isnot v:t_func
throw '`language` must be a String or Funcref if defined'
endif
if has_key(a:linter, 'project_root')
@@ -253,16 +167,10 @@ function! ale#linter#PreProcess(filetype, linter) abort
if type(l:obj.project_root) isnot v:t_string
\&& type(l:obj.project_root) isnot v:t_func
throw '`project_root` must be a String or Function if defined'
endif
elseif has_key(a:linter, 'project_root_callback')
let l:obj.project_root_callback = a:linter.project_root_callback
if !s:IsCallback(l:obj.project_root_callback)
throw '`project_root_callback` must be a callback if defined'
throw '`project_root` must be a String or Function'
endif
else
throw '`project_root` or `project_root_callback` must be defined for LSP linters'
throw '`project_root` must be defined for LSP linters'
endif
if has_key(a:linter, 'completion_filter')
@@ -273,37 +181,16 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
endif
if has_key(a:linter, 'initialization_options_callback')
if has_key(a:linter, 'initialization_options')
throw 'Only one of `initialization_options` or '
\ . '`initialization_options_callback` should be set'
endif
let l:obj.initialization_options_callback = a:linter.initialization_options_callback
if !s:IsCallback(l:obj.initialization_options_callback)
throw '`initialization_options_callback` must be a callback if defined'
endif
elseif has_key(a:linter, 'initialization_options')
if has_key(a:linter, 'initialization_options')
let l:obj.initialization_options = a:linter.initialization_options
if type(l:obj.initialization_options) isnot v:t_dict
\&& type(l:obj.initialization_options) isnot v:t_func
throw '`initialization_options` must be a String or Function if defined'
throw '`initialization_options` must be a Dictionary or Function if defined'
endif
endif
if has_key(a:linter, 'lsp_config_callback')
if has_key(a:linter, 'lsp_config')
throw 'Only one of `lsp_config` or `lsp_config_callback` should be set'
endif
let l:obj.lsp_config_callback = a:linter.lsp_config_callback
if !s:IsCallback(l:obj.lsp_config_callback)
throw '`lsp_config_callback` must be a callback if defined'
endif
elseif has_key(a:linter, 'lsp_config')
if has_key(a:linter, 'lsp_config')
if type(a:linter.lsp_config) isnot v:t_dict
\&& type(a:linter.lsp_config) isnot v:t_func
throw '`lsp_config` must be a Dictionary or Function if defined'
@@ -324,21 +211,17 @@ function! ale#linter#PreProcess(filetype, linter) abort
" file on disk.
let l:obj.lint_file = get(a:linter, 'lint_file', 0)
if !s:IsBoolean(l:obj.lint_file)
throw '`lint_file` must be `0` or `1`'
if !s:IsBoolean(l:obj.lint_file) && type(l:obj.lint_file) isnot v:t_func
throw '`lint_file` must be `0`, `1`, or a Function'
endif
" An option indicating that the buffer should be read.
let l:obj.read_buffer = get(a:linter, 'read_buffer', !l:obj.lint_file)
let l:obj.read_buffer = get(a:linter, 'read_buffer', 1)
if !s:IsBoolean(l:obj.read_buffer)
throw '`read_buffer` must be `0` or `1`'
endif
if l:obj.lint_file && l:obj.read_buffer
throw 'Only one of `lint_file` or `read_buffer` can be `1`'
endif
let l:obj.aliases = get(a:linter, 'aliases', [])
if type(l:obj.aliases) isnot v:t_list
@@ -346,14 +229,6 @@ function! ale#linter#PreProcess(filetype, linter) abort
throw '`aliases` must be a List of String values'
endif
for l:key in filter(keys(a:linter), 'v:val[-9:] is# ''_callback'' || v:val is# ''command_chain''')
if !get(g:, 'ale_ignore_2_4_warnings')
execute 'echom l:key . '' is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.'''
endif
break
endfor
return l:obj
endfunction
@@ -519,11 +394,19 @@ function! ale#linter#Get(original_filetypes) abort
return reverse(l:combined_linters)
endfunction
function! ale#linter#RemoveIgnored(buffer, filetype, linters) abort
" Apply ignore lists for linters only if needed.
let l:ignore_config = ale#Var(a:buffer, 'linters_ignore')
let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp')
return !empty(l:ignore_config) || l:disable_lsp
\ ? ale#engine#ignore#Exclude(a:filetype, a:linters, l:ignore_config, l:disable_lsp)
\ : a:linters
endfunction
" Given a buffer and linter, get the executable String for the linter.
function! ale#linter#GetExecutable(buffer, linter) abort
let l:Executable = has_key(a:linter, 'executable_callback')
\ ? function(a:linter.executable_callback)
\ : a:linter.executable
let l:Executable = a:linter.executable
return type(l:Executable) is v:t_func
\ ? l:Executable(a:buffer)
@@ -531,24 +414,21 @@ function! ale#linter#GetExecutable(buffer, linter) abort
endfunction
" Given a buffer and linter, get the command String for the linter.
" The command_chain key is not supported.
function! ale#linter#GetCommand(buffer, linter) abort
let l:Command = has_key(a:linter, 'command_callback')
\ ? function(a:linter.command_callback)
\ : a:linter.command
let l:Command = a:linter.command
return type(l:Command) is v:t_func
\ ? l:Command(a:buffer)
\ : l:Command
return type(l:Command) is v:t_func ? l:Command(a:buffer) : l:Command
endfunction
" Given a buffer and linter, get the address for connecting to the server.
function! ale#linter#GetAddress(buffer, linter) abort
let l:Address = has_key(a:linter, 'address_callback')
\ ? function(a:linter.address_callback)
\ : a:linter.address
let l:Address = a:linter.address
return type(l:Address) is v:t_func
\ ? l:Address(a:buffer)
\ : l:Address
return type(l:Address) is v:t_func ? l:Address(a:buffer) : l:Address
endfunction
function! ale#linter#GetLanguage(buffer, linter) abort
let l:Language = a:linter.language
return type(l:Language) is v:t_func ? l:Language(a:buffer) : l:Language
endfunction

View File

@@ -64,6 +64,9 @@ endfunction
" Used only in tests.
function! ale#lsp#GetConnections() abort
" This command will throw from the sandbox.
let &l:equalprg=&l:equalprg
return s:connections
endfunction
@@ -196,14 +199,26 @@ function! s:UpdateCapabilities(conn, capabilities) abort
let a:conn.capabilities.hover = 1
endif
if type(get(a:capabilities, 'hoverProvider')) is v:t_dict
let a:conn.capabilities.hover = 1
endif
if get(a:capabilities, 'referencesProvider') is v:true
let a:conn.capabilities.references = 1
endif
if type(get(a:capabilities, 'referencesProvider')) is v:t_dict
let a:conn.capabilities.references = 1
endif
if get(a:capabilities, 'renameProvider') is v:true
let a:conn.capabilities.rename = 1
endif
if type(get(a:capabilities, 'renameProvider')) is v:t_dict
let a:conn.capabilities.rename = 1
endif
if !empty(get(a:capabilities, 'completionProvider'))
let a:conn.capabilities.completion = 1
endif
@@ -220,13 +235,25 @@ function! s:UpdateCapabilities(conn, capabilities) abort
let a:conn.capabilities.definition = 1
endif
if type(get(a:capabilities, 'definitionProvider')) is v:t_dict
let a:conn.capabilities.definition = 1
endif
if get(a:capabilities, 'typeDefinitionProvider') is v:true
let a:conn.capabilities.typeDefinition = 1
endif
if type(get(a:capabilities, 'typeDefinitionProvider')) is v:t_dict
let a:conn.capabilities.typeDefinition = 1
endif
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
let a:conn.capabilities.symbol_search = 1
endif
if type(get(a:capabilities, 'workspaceSymbolProvider')) is v:t_dict
let a:conn.capabilities.symbol_search = 1
endif
endfunction
" Update a connection's configuration dictionary and notify LSP servers
@@ -425,6 +452,7 @@ function! ale#lsp#StartProgram(conn_id, executable, command) abort
endif
if l:started && !l:conn.is_tsserver
let l:conn.initialized = 0
call s:SendInitMessage(l:conn)
endif

View File

@@ -34,7 +34,11 @@ endfunction
function! s:HandleLSPDiagnostics(conn_id, response) abort
let l:linter_name = s:lsp_linter_map[a:conn_id]
let l:filename = ale#path#FromURI(a:response.params.uri)
let l:buffer = bufnr('^' . l:filename . '$')
let l:escaped_name = escape(
\ fnameescape(l:filename),
\ has('win32') ? '^' : '^,}]'
\)
let l:buffer = bufnr('^' . l:escaped_name . '$')
let l:info = get(g:ale_buffer_info, l:buffer, {})
if empty(l:info)
@@ -52,7 +56,11 @@ endfunction
function! s:HandleTSServerDiagnostics(response, error_type) abort
let l:linter_name = 'tsserver'
let l:buffer = bufnr('^' . a:response.body.file . '$')
let l:escaped_name = escape(
\ fnameescape(a:response.body.file),
\ has('win32') ? '^' : '^,}]'
\)
let l:buffer = bufnr('^' . l:escaped_name . '$')
let l:info = get(g:ale_buffer_info, l:buffer, {})
if empty(l:info)
@@ -227,7 +235,7 @@ function! ale#lsp_linter#OnInit(linter, details, Callback) abort
let l:command = a:details.command
let l:config = ale#lsp_linter#GetConfig(l:buffer, a:linter)
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(l:buffer)
let l:language_id = ale#linter#GetLanguage(l:buffer, a:linter)
call ale#lsp#UpdateConfig(l:conn_id, l:buffer, l:config)
@@ -265,7 +273,14 @@ function! s:StartLSP(options, address, executable, command) abort
call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
endif
let l:command = ale#command#FormatCommand(l:buffer, a:executable, a:command, 0, v:false)[1]
let l:command = ale#command#FormatCommand(
\ l:buffer,
\ a:executable,
\ a:command,
\ 0,
\ v:false,
\ [],
\)[1]
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command)
endif

View File

@@ -24,6 +24,14 @@ function! ale#path#Simplify(path) abort
return substitute(simplify(l:win_path), '^\\\+', '\', 'g') " no-custom-checks
endfunction
" Simplify a path without a Windows drive letter.
" This function can be used for checking if paths are equal.
function! ale#path#RemoveDriveLetter(path) abort
return has('win32') && a:path[1:2] is# ':\'
\ ? ale#path#Simplify(a:path[2:])
\ : ale#path#Simplify(a:path)
endfunction
" Given a buffer and a filename, find the nearest file by searching upwards
" through the paths relative to the given buffer.
function! ale#path#FindNearestFile(buffer, filename) abort
@@ -74,15 +82,19 @@ endfunction
function! ale#path#CdString(directory) abort
if has('win32')
return 'cd /d ' . ale#Escape(a:directory) . ' && '
else
return 'cd ' . ale#Escape(a:directory) . ' && '
endif
return 'cd ' . ale#Escape(a:directory) . ' && '
endfunction
" Output 'cd <buffer_filename_directory> && '
" This function can be used changing the directory for a linter command.
function! ale#path#BufferCdString(buffer) abort
return ale#path#CdString(fnamemodify(bufname(a:buffer), ':p:h'))
if has('win32')
return 'cd /d %s:h && '
endif
return 'cd %s:h && '
endfunction
" Return 1 if a path is an absolute path.
@@ -95,7 +107,7 @@ function! ale#path#IsAbsolute(filename) abort
return a:filename[:0] is# '/' || a:filename[1:2] is# ':\'
endfunction
let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h'))
let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h'))
" Given a filename, return 1 if the file represents some temporary file
" created by Vim.

View File

@@ -1,14 +1,22 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Preview windows for showing whatever information in.
if !has_key(s:, 'last_selection_list')
let s:last_selection_list = []
if !has_key(s:, 'last__list')
let s:last_list = []
endif
if !has_key(s:, 'last_selection_open_in')
let s:last_selection_open_in = 'current-buffer'
if !has_key(s:, 'last_options')
let s:last_options = {}
endif
function! ale#preview#SetLastSelection(item_list, options) abort
let s:last_list = a:item_list
let s:last_options = {
\ 'open_in': get(a:options, 'open_in', 'current-buffer'),
\ 'use_relative_paths': get(a:options, 'use_relative_paths', 0),
\}
endfunction
" Open a preview window and show some lines in it.
" A second argument can be passed as a Dictionary with options. They are...
"
@@ -31,6 +39,10 @@ function! ale#preview#Show(lines, ...) abort
setlocal readonly
let &l:filetype = get(l:options, 'filetype', 'ale-preview')
for l:command in get(l:options, 'commands', [])
call execute(l:command)
endfor
if get(l:options, 'stay_here')
wincmd p
endif
@@ -77,19 +89,14 @@ function! ale#preview#ShowSelection(item_list, ...) abort
let b:ale_preview_item_list = a:item_list
let b:ale_preview_item_open_in = get(l:options, 'open_in', 'current-buffer')
" Remove the last preview
let s:last_selection_list = b:ale_preview_item_list
let s:last_selection_open_in = b:ale_preview_item_open_in
" Remember preview state, so we can repeat it later.
call ale#preview#SetLastSelection(a:item_list, l:options)
endfunction
function! ale#preview#RepeatSelection() abort
if empty(s:last_selection_list)
return
if !empty(s:last_list)
call ale#preview#ShowSelection(s:last_list, s:last_options)
endif
call ale#preview#ShowSelection(s:last_selection_list, {
\ 'open_in': s:last_selection_open_in,
\})
endfunction
function! s:Open(open_in) abort

View File

@@ -83,6 +83,31 @@ function! ale#rename#HandleTSServerResponse(conn_id, response) abort
\}, v:true)
endfunction
function! s:getChanges(workspace_edit) abort
let l:changes = {}
if has_key(a:workspace_edit, 'changes') && !empty(a:workspace_edit.changes)
return a:workspace_edit.changes
elseif has_key(a:workspace_edit, 'documentChanges')
let l:document_changes = []
if type(a:workspace_edit.documentChanges) is v:t_dict
\ && has_key(a:workspace_edit.documentChanges, 'edits')
call add(l:document_changes, a:workspace_edit.documentChanges)
elseif type(a:workspace_edit.documentChanges) is v:t_list
let l:document_changes = a:workspace_edit.documentChanges
endif
for l:text_document_edit in l:document_changes
let l:filename = l:text_document_edit.textDocument.uri
let l:edits = l:text_document_edit.edits
let l:changes[l:filename] = l:edits
endfor
endif
return l:changes
endfunction
function! ale#rename#HandleLSPResponse(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:rename_map, a:response.id)
@@ -94,9 +119,9 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
return
endif
let l:workspace_edit = a:response.result
let l:changes_map = s:getChanges(a:response.result)
if !has_key(l:workspace_edit, 'changes') || empty(l:workspace_edit.changes)
if empty(l:changes_map)
call s:message('No changes received from server')
return
@@ -104,8 +129,8 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
let l:changes = []
for l:file_name in keys(l:workspace_edit.changes)
let l:text_edits = l:workspace_edit.changes[l:file_name]
for l:file_name in keys(l:changes_map)
let l:text_edits = l:changes_map[l:file_name]
let l:text_changes = []
for l:edit in l:text_edits

View File

@@ -145,8 +145,8 @@ function! ale#test#WaitForJobs(deadline) abort
" end, but before handlers are run.
sleep 10ms
" We must check the buffer data again to see if new jobs started
" for command_chain linters.
" We must check the buffer data again to see if new jobs started for
" linters with chained commands.
let l:has_new_jobs = 0
" Check again to see if any jobs are running.

View File

@@ -1,9 +1,18 @@
" This probably doesn't handle Unicode characters well.
function! s:EncodeChar(char) abort
let l:result = ''
for l:index in range(strlen(a:char))
let l:result .= printf('%%%02x', char2nr(a:char[l:index]))
endfor
return l:result
endfunction
function! ale#uri#Encode(value) abort
return substitute(
\ a:value,
\ '\([^a-zA-Z0-9\\/$\-_.!*''(),]\)',
\ '\=printf(''%%%02x'', char2nr(submatch(1)))',
\ '\=s:EncodeChar(submatch(1))',
\ 'g'
\)
endfunction
@@ -12,7 +21,7 @@ function! ale#uri#Decode(value) abort
return substitute(
\ a:value,
\ '%\(\x\x\)',
\ '\=nr2char(''0x'' . submatch(1))',
\ '\=printf("%c", str2nr(submatch(1), 16))',
\ 'g'
\)
endfunction

View File

@@ -16,7 +16,9 @@ endfunction
" Vim 8 does not support echoing long messages from asynchronous callbacks,
" but NeoVim does. Small messages can be echoed in Vim 8, and larger messages
" have to be shown in preview windows.
function! ale#util#ShowMessage(string) abort
function! ale#util#ShowMessage(string, ...) abort
let l:options = get(a:000, 0, {})
if !has('nvim')
call ale#preview#CloseIfTypeMatches('ale-preview.message')
endif
@@ -25,10 +27,13 @@ function! ale#util#ShowMessage(string) abort
if has('nvim') || (a:string !~? "\n" && len(a:string) < &columns)
execute 'echo a:string'
else
call ale#preview#Show(split(a:string, "\n"), {
\ 'filetype': 'ale-preview.message',
\ 'stay_here': 1,
\})
call ale#preview#Show(split(a:string, "\n"), extend(
\ {
\ 'filetype': 'ale-preview.message',
\ 'stay_here': 1,
\ },
\ l:options,
\))
endif
endfunction
@@ -418,7 +423,10 @@ function! ale#util#Writefile(buffer, lines, filename) abort
\ ? map(copy(a:lines), 'substitute(v:val, ''\r*$'', ''\r'', '''')')
\ : a:lines
call writefile(l:corrected_lines, a:filename, 'S') " no-custom-checks
" Set binary flag if buffer doesn't have eol and nofixeol to avoid appending newline
let l:flags = !getbufvar(a:buffer, '&eol') && exists('+fixeol') && !&fixeol ? 'bS' : 'S'
call writefile(l:corrected_lines, a:filename, l:flags) " no-custom-checks
endfunction
if !exists('s:patial_timers')

View File

@@ -1,22 +1,36 @@
===============================================================================
ALE C Integration *ale-c-options*
For basic checking of problems with C files, ALE offers the `cc` linter, which
runs either `clang`, or `gcc`. See |ale-c-cc|.
===============================================================================
Global Options
g:ale_c_always_make *g:ale_c_always_make*
*b:ale_c_always_make*
Type: |Number|
Default: `has('unix') && !has('macunix')`
If set to `1`, use `--always-make` for `make`, which means that output will
always be parsed from `make` dry runs with GNU make. BSD `make` does not
support this option, so you probably want to turn this option off when using
a BSD variant.
g:ale_c_build_dir_names *g:ale_c_build_dir_names*
*b:ale_c_build_dir_names*
Type: |List|
Default: `['build', 'bin']`
A list of directory names to be used when searching upwards from cpp
files to discover compilation databases with. For directory named `'foo'`,
ALE will search for `'foo/compile_commands.json'` in all directories on and above
the directory containing the cpp file to find path to compilation database.
This feature is useful for the clang tools wrapped around LibTooling (namely
here, clang-tidy)
A list of directory names to be used when searching upwards from cpp files
to discover compilation databases with. For directory named `'foo'`, ALE
will search for `'foo/compile_commands.json'` in all directories on and
above the directory containing the cpp file to find path to compilation
database. This feature is useful for the clang tools wrapped around
LibTooling (namely here, clang-tidy)
g:ale_c_build_dir *g:ale_c_build_dir*
@@ -37,7 +51,7 @@ g:ale_c_build_dir *g:ale_c_build_dir*
g:ale_c_parse_compile_commands *g:ale_c_parse_compile_commands*
*b:ale_c_parse_compile_commands*
Type: |Number|
Default: `0`
Default: `1`
If set to `1`, ALE will parse `compile_commands.json` files to automatically
determine flags for C or C++ compilers. ALE will first search for the
@@ -45,9 +59,6 @@ g:ale_c_parse_compile_commands *g:ale_c_parse_compile_commands*
`compile_commands.json` files in the directories for
|g:ale_c_build_dir_names|.
If |g:ale_c_parse_makefile| or |b:ale_c_parse_makefile| is set to `1`, the
output of `make -n` will be preferred over `compile_commands.json` files.
g:ale_c_parse_makefile *g:ale_c_parse_makefile*
*b:ale_c_parse_makefile*
@@ -58,24 +69,102 @@ g:ale_c_parse_makefile *g:ale_c_parse_makefile*
set for C or C++ compilers. This can make it easier to determine the correct
build flags to use for different files.
NOTE: When using this option on BSD, you may need to set
|g:ale_c_always_make| to `0`, and `make -n` will not provide consistent
results if binaries have already been built, so use `make clean` when
editing your files.
WARNING: Running `make -n` automatically can execute arbitrary code, even
though it's supposed to be a dry run, so enable this option with care. You
might prefer to use the buffer-local version of the option instead with
|g:ale_pattern_options|, or you own code for checking which project you're
in.
You might want to disable this option if `make -n` takes too long to run for
projects you work on.
If |g:ale_c_parse_compile_commands| or |b:ale_c_parse_compile_commands| is
set to `1`, flags taken from `compile_commands.json` will be preferred over
`make -n` output.
===============================================================================
clang *ale-c-clang*
astyle *ale-c-astyle*
g:ale_c_clang_executable *g:ale_c_clang_executable*
*b:ale_c_clang_executable*
g:ale_c_astyle_executable *g:ale_c_astyle_executable*
*b:ale_c_astyle_executable*
Type: |String|
Default: `'clang'`
Default: `'astyle'`
This variable can be changed to use a different executable for clang.
This variable can be changed to use a different executable for astyle.
g:ale_c_clang_options *g:ale_c_clang_options*
*b:ale_c_clang_options*
g:ale_c_astyle_project_options *g:ale_c_astyle_project_options*
*b:ale_c_astyle_project_options*
Type: |String|
Default: `''`
This variable can be changed to use an option file for project level
configurations. Provide only the filename of the option file that should be
present at the project's root directory.
For example, if .astylrc is specified, the file is searched in the parent
directories of the source file's directory.
===============================================================================
cc *ale-c-cc*
*ale-c-gcc*
*ale-c-clang*
g:ale_c_cc_executable *g:ale_c_cc_executable*
*b:ale_c_cc_executable*
Type: |String|
Default: `'<auto>'`
This variable can be changed to use a different executable for a C compiler.
ALE will try to use `clang` if Clang is available, otherwise ALE will
default to checking C code with `gcc`.
g:ale_c_cc_options *g:ale_c_cc_options*
*b:ale_c_cc_options*
Type: |String|
Default: `'-std=c11 -Wall'`
This variable can be changed to modify flags given to clang.
This variable can be change to modify flags given to the C compiler.
===============================================================================
ccls *ale-c-ccls*
g:ale_c_ccls_executable *g:ale_c_ccls_executable*
*b:ale_c_ccls_executable*
Type: |String|
Default: `'ccls'`
This variable can be changed to use a different executable for ccls.
g:ale_c_ccls_init_options *g:ale_c_ccls_init_options*
*b:ale_c_ccls_init_options*
Type: |Dictionary|
Default: `{}`
This variable can be changed to customize ccls initialization options.
Example: >
{
\ 'cacheDirectory': '/tmp/ccls',
\ 'cacheFormat': 'binary',
\ 'diagnostics': {
\ 'onOpen': 0,
\ 'opChange': 1000,
\ },
\ }
<
Visit https://github.com/MaskRay/ccls/wiki/Initialization-options for all
available options and explanations.
===============================================================================
@@ -260,25 +349,6 @@ g:ale_c_flawfinder_error_severity *g:ale_c_flawfinder_error_severity*
error. This setting also applies to flawfinder for c++.
===============================================================================
gcc *ale-c-gcc*
g:ale_c_gcc_executable *g:ale_c_gcc_executable*
*b:ale_c_gcc_executable*
Type: |String|
Default: `'gcc'`
This variable can be changed to use a different executable for gcc.
g:ale_c_gcc_options *g:ale_c_gcc_options*
*b:ale_c_gcc_options*
Type: |String|
Default: `'-std=c11 -Wall'`
This variable can be change to modify flags given to gcc.
===============================================================================
uncrustify *ale-c-uncrustify*
@@ -298,36 +368,5 @@ g:ale_c_uncrustify_options *g:ale_c_uncrustify_options*
This variable can be change to modify flags given to uncrustify.
===============================================================================
ccls *ale-c-ccls*
g:ale_c_ccls_executable *g:ale_c_ccls_executable*
*b:ale_c_ccls_executable*
Type: |String|
Default: `'ccls'`
This variable can be changed to use a different executable for ccls.
g:ale_c_ccls_init_options *g:ale_c_ccls_init_options*
*b:ale_c_ccls_init_options*
Type: |Dictionary|
Default: `{}`
This variable can be changed to customize ccls initialization options.
Example: >
{
\ 'cacheDirectory': '/tmp/ccls',
\ 'cacheFormat': 'binary',
\ 'diagnostics': {
\ 'onOpen': 0,
\ 'opChange': 1000,
\ },
\ }
<
Visit https://github.com/MaskRay/ccls/wiki/Initialization-options for all
available options and explanations.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@@ -7,8 +7,40 @@ cfn-python-lint *ale-cloudformation-cfn-python-lint*
cfn-python-lint is a linter for AWS CloudFormation template file.
https://github.com/awslabs/cfn-python-lint
Website: https://github.com/awslabs/cfn-python-lint
Installation
-------------------------------------------------------------------------------
Install cfn-python-lint using either pip or brew: >
`pip install cfn-lint`. If pip is not available, run
`python setup.py clean --all` then `python setup.py install`.
Homebrew (macOS):
`brew install cfn-lint`
<
Configuration
-------------------------------------------------------------------------------
To get cloudformation linter to work on only CloudFormation files we must set
the buffer |filetype| to yaml.cloudformation.
This causes ALE to lint the file with linters configured for cloudformation and
yaml files.
Just put:
>
au BufRead,BufNewFile *.template.yaml set filetype=yaml.cloudformation
<
on `ftdetect/cloudformation.vim`
This will get both cloudformation and yaml linters to work on any file with `.template.yaml` ext.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@@ -1,12 +1,16 @@
===============================================================================
ALE C++ Integration *ale-cpp-options*
For basic checking of problems with C++ files, ALE offers the `cc` linter,
which runs either `clang++`, or `gcc`. See |ale-cpp-cc|.
===============================================================================
Global Options
The following C options also apply to some C++ linters too.
* |g:ale_c_always_make|
* |g:ale_c_build_dir_names|
* |g:ale_c_build_dir|
* |g:ale_c_parse_makefile|
@@ -14,41 +18,82 @@ The following C options also apply to some C++ linters too.
===============================================================================
clang *ale-cpp-clang*
astyle *ale-cpp-astyle*
g:ale_cpp_clang_executable *g:ale_cpp_clang_executable*
*b:ale_cpp_clang_executable*
g:ale_cpp_astyle_executable *g:ale_cpp_astyle_executable*
*b:ale_cpp_astyle_executable*
Type: |String|
Default: `'clang++'`
Default: `'astyle'`
This variable can be changed to use a different executable for clang.
This variable can be changed to use a different executable for astyle.
g:ale_cpp_clang_options *g:ale_cpp_clang_options*
*b:ale_cpp_clang_options*
Type: |String|
Default: `'-std=c++14 -Wall'`
This variable can be changed to modify flags given to clang.
===============================================================================
clangd *ale-cpp-clangd*
g:ale_cpp_clangd_executable *g:ale_cpp_clangd_executable*
*b:ale_cpp_clangd_executable*
Type: |String|
Default: `'clangd'`
This variable can be changed to use a different executable for clangd.
g:ale_cpp_clangd_options *g:ale_cpp_clangd_options*
*b:ale_cpp_clangd_options*
g:ale_cpp_astyle_project_options *g:ale_cpp_astyle_project_options*
*b:ale_cpp_astyle_project_options*
Type: |String|
Default: `''`
This variable can be changed to modify flags given to clangd.
This variable can be changed to use an option file for project level
configurations. Provide only the filename of the option file that should be
present at the project's root directory.
For example, if .astylrc is specified, the file is searched in the parent
directories of the source file's directory.
===============================================================================
cc *ale-cpp-cc*
*ale-cpp-gcc*
*ale-cpp-clang*
g:ale_cpp_cc_executable *g:ale_cpp_cc_executable*
*b:ale_cpp_cc_executable*
Type: |String|
Default: `'<auto>'`
This variable can be changed to use a different executable for a C++ compiler.
ALE will try to use `clang++` if Clang is available, otherwise ALE will
default to checking C++ code with `gcc`.
g:ale_cpp_cc_options *g:ale_cpp_cc_options*
*b:ale_cpp_cc_options*
Type: |String|
Default: `'-std=c++14 -Wall'`
This variable can be change to modify flags given to the C++ compiler.
===============================================================================
ccls *ale-cpp-ccls*
g:ale_cpp_ccls_executable *g:ale_cpp_ccls_executable*
*b:ale_cpp_ccls_executable*
Type: |String|
Default: `'ccls'`
This variable can be changed to use a different executable for ccls.
g:ale_cpp_ccls_init_options *g:ale_cpp_ccls_init_options*
*b:ale_cpp_ccls_init_options*
Type: |Dictionary|
Default: `{}`
This variable can be changed to customize ccls initialization options.
Example: >
{
\ 'cacheDirectory': '/tmp/ccls',
\ 'cacheFormat': 'binary',
\ 'diagnostics': {
\ 'onOpen': 0,
\ 'opChange': 1000,
\ },
\ }
<
Visit https://github.com/MaskRay/ccls/wiki/Initialization-options for all
available options and explanations.
===============================================================================
@@ -82,6 +127,25 @@ g:ale_cpp_clangcheck_options *g:ale_cpp_clangcheck_options*
option.
===============================================================================
clangd *ale-cpp-clangd*
g:ale_cpp_clangd_executable *g:ale_cpp_clangd_executable*
*b:ale_cpp_clangd_executable*
Type: |String|
Default: `'clangd'`
This variable can be changed to use a different executable for clangd.
g:ale_cpp_clangd_options *g:ale_cpp_clangd_options*
*b:ale_cpp_clangd_options*
Type: |String|
Default: `''`
This variable can be changed to modify flags given to clangd.
===============================================================================
clang-format *ale-cpp-clangformat*
@@ -271,61 +335,11 @@ g:ale_cpp_flawfinder_options *g:ale-cpp-flawfinder*
This variable can be used to pass extra options into the flawfinder command.
===============================================================================
gcc *ale-cpp-gcc*
g:ale_cpp_gcc_executable *g:ale_cpp_gcc_executable*
*b:ale_cpp_gcc_executable*
Type: |String|
Default: `'gcc'`
This variable can be changed to use a different executable for gcc.
g:ale_cpp_gcc_options *g:ale_cpp_gcc_options*
*b:ale_cpp_gcc_options*
Type: |String|
Default: `'-std=c++14 -Wall'`
This variable can be changed to modify flags given to gcc.
===============================================================================
uncrustify *ale-cpp-uncrustify*
See |ale-c-uncrustify| for information about the available options.
===============================================================================
ccls *ale-cpp-ccls*
g:ale_cpp_ccls_executable *g:ale_cpp_ccls_executable*
*b:ale_cpp_ccls_executable*
Type: |String|
Default: `'ccls'`
This variable can be changed to use a different executable for ccls.
g:ale_cpp_ccls_init_options *g:ale_cpp_ccls_init_options*
*b:ale_cpp_ccls_init_options*
Type: |Dictionary|
Default: `{}`
This variable can be changed to customize ccls initialization options.
Example: >
{
\ 'cacheDirectory': '/tmp/ccls',
\ 'cacheFormat': 'binary',
\ 'diagnostics': {
\ 'onOpen': 0,
\ 'opChange': 1000,
\ },
\ }
<
Visit https://github.com/MaskRay/ccls/wiki/Initialization-options for all
available options and explanations.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

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