diff --git a/.appveyor.yml b/.appveyor.yml
index aca83191..e6f2a1ee 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -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: >-
diff --git a/.github/stale.yml b/.github/stale.yml
index e9cda3d9..3a5a354b 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,9 +1,10 @@
---
-# This configuration closes stale PRs after 30 days.
+# 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: 2
+daysUntilClose: 7
exemptLabels: []
staleLabel: stale
markComment: >
diff --git a/.gitignore b/.gitignore
index ae9f65fb..7711fb99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/README.md b/README.md
index ffc6dd19..0f1c613b 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ other content at [w0rp.com](https://w0rp.com).
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)
@@ -877,3 +878,14 @@ To set a default height for the error list, use the `g:ale_list_window_size` var
" Show 5 lines of errors (default: 10)
let g:ale_list_window_size = 5
```
+
+
+
+### 5.xx. How can I run linters or fixers via Docker or a VM?
+
+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.
diff --git a/ale_linters/ada/gcc.vim b/ale_linters/ada/gcc.vim
index 87496b81..5afc9ae3 100644
--- a/ale_linters/ada/gcc.vim
+++ b/ale_linters/ada/gcc.vim
@@ -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
diff --git a/ale_linters/asciidoc/languagetool.vim b/ale_linters/asciidoc/languagetool.vim
new file mode 100644
index 00000000..8e8de7f3
--- /dev/null
+++ b/ale_linters/asciidoc/languagetool.vim
@@ -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')
diff --git a/ale_linters/asm/gcc.vim b/ale_linters/asm/gcc.vim
index eecab6ef..cda38923 100644
--- a/ale_linters/asm/gcc.vim
+++ b/ale_linters/asm/gcc.vim
@@ -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
diff --git a/ale_linters/c/cc.vim b/ale_linters/c/cc.vim
new file mode 100644
index 00000000..5655fbf7
--- /dev/null
+++ b/ale_linters/c/cc.vim
@@ -0,0 +1,53 @@
+" Author: w0rp
+" Description: A C compiler linter for C files with gcc/clang, etc.
+
+call ale#Set('c_cc_executable', '')
+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# ''
+ 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',
+\})
diff --git a/ale_linters/c/clang.vim b/ale_linters/c/clang.vim
deleted file mode 100644
index 681101fc..00000000
--- a/ale_linters/c/clang.vim
+++ /dev/null
@@ -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',
-\})
diff --git a/ale_linters/c/gcc.vim b/ale_linters/c/gcc.vim
deleted file mode 100644
index 1df1018e..00000000
--- a/ale_linters/c/gcc.vim
+++ /dev/null
@@ -1,28 +0,0 @@
-" Author: w0rp
-" 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',
-\})
diff --git a/ale_linters/cpp/cc.vim b/ale_linters/cpp/cc.vim
new file mode 100644
index 00000000..ffb8f068
--- /dev/null
+++ b/ale_linters/cpp/cc.vim
@@ -0,0 +1,53 @@
+" Author: w0rp
+" Description: A C++ compiler linter for C++ files with gcc/clang, etc.
+
+call ale#Set('cpp_cc_executable', '')
+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# ''
+ 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',
+\})
diff --git a/ale_linters/cpp/clang.vim b/ale_linters/cpp/clang.vim
deleted file mode 100644
index e48291eb..00000000
--- a/ale_linters/cpp/clang.vim
+++ /dev/null
@@ -1,24 +0,0 @@
-" Author: Tomota Nakamura
-" 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',
-\})
diff --git a/ale_linters/cpp/gcc.vim b/ale_linters/cpp/gcc.vim
deleted file mode 100644
index 108d6d70..00000000
--- a/ale_linters/cpp/gcc.vim
+++ /dev/null
@@ -1,29 +0,0 @@
-" Author: geam
-" Description: gcc linter for cpp files
-"
-call ale#Set('cpp_gcc_executable', 'gcc')
-call ale#Set('cpp_gcc_options', '-std=c++14 -Wall')
-
-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',
-\})
diff --git a/ale_linters/cuda/nvcc.vim b/ale_linters/cuda/nvcc.vim
index f3af07b6..2734f6ec 100644
--- a/ale_linters/cuda/nvcc.vim
+++ b/ale_linters/cuda/nvcc.vim
@@ -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'))
diff --git a/ale_linters/elixir/credo.vim b/ale_linters/elixir/credo.vim
index 317ecab3..7c298502 100644
--- a/ale_linters/elixir/credo.vim
+++ b/ale_linters/elixir/credo.vim
@@ -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)
diff --git a/ale_linters/eruby/ruumba.vim b/ale_linters/eruby/ruumba.vim
index 2e84acf7..f415f1ab 100644
--- a/ale_linters/eruby/ruumba.vim
+++ b/ale_linters/eruby/ruumba.vim
@@ -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
diff --git a/ale_linters/go/gofmt.vim b/ale_linters/go/gofmt.vim
index a233b422..b313f9ca 100644
--- a/ale_linters/go/gofmt.vim
+++ b/ale_linters/go/gofmt.vim
@@ -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',
diff --git a/ale_linters/handlebars/embertemplatelint.vim b/ale_linters/handlebars/embertemplatelint.vim
index 74bd6a99..bd4d1d31 100644
--- a/ale_linters/handlebars/embertemplatelint.vim
+++ b/ale_linters/handlebars/embertemplatelint.vim
@@ -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',
\})
diff --git a/ale_linters/java/eclipselsp.vim b/ale_linters/java/eclipselsp.vim
index c981b749..8bc09039 100644
--- a/ale_linters/java/eclipselsp.vim
+++ b/ale_linters/java/eclipselsp.vim
@@ -31,21 +31,28 @@ function! ale_linters#java#eclipselsp#JarPath(buffer) abort
" 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
+ if len(l:files) >= 1
return l:files[0]
endif
" Search jar file within VSCode extensions folder.
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
diff --git a/ale_linters/markdown/markdownlint.vim b/ale_linters/markdown/markdownlint.vim
index e935cbfe..7a293938 100644
--- a/ale_linters/markdown/markdownlint.vim
+++ b/ale_linters/markdown/markdownlint.vim
@@ -1,11 +1,22 @@
" Author: Ty-Lucas Kelley
" 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'
\})
diff --git a/ale_linters/nasm/nasm.vim b/ale_linters/nasm/nasm.vim
index 347abc1b..c4f53629 100644
--- a/ale_linters/nasm/nasm.vim
+++ b/ale_linters/nasm/nasm.vim
@@ -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
diff --git a/ale_linters/objc/clang.vim b/ale_linters/objc/clang.vim
index 7873dccd..cafb97db 100644
--- a/ale_linters/objc/clang.vim
+++ b/ale_linters/objc/clang.vim
@@ -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
diff --git a/ale_linters/objcpp/clang.vim b/ale_linters/objcpp/clang.vim
index 4dbe55b3..35a40c6f 100644
--- a/ale_linters/objcpp/clang.vim
+++ b/ale_linters/objcpp/clang.vim
@@ -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
diff --git a/ale_linters/ocaml/ols.vim b/ale_linters/ocaml/ols.vim
index d8208c52..ec71bdb4 100644
--- a/ale_linters/ocaml/ols.vim
+++ b/ale_linters/ocaml/ols.vim
@@ -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'),
\})
diff --git a/ale_linters/php/psalm.vim b/ale_linters/php/psalm.vim
index ab4dbbc9..286c8a96 100644
--- a/ale_linters/php/psalm.vim
+++ b/ale_linters/php/psalm.vim
@@ -1,9 +1,9 @@
" Author: Matt Brown
" 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'),
diff --git a/ale_linters/pyrex/cython.vim b/ale_linters/pyrex/cython.vim
index 84382ba1..247c3060 100644
--- a/ale_linters/pyrex/cython.vim
+++ b/ale_linters/pyrex/cython.vim
@@ -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
diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim
index e2e7b743..fc4ab692 100644
--- a/ale_linters/python/flake8.vim
+++ b/ale_linters/python/flake8.vim
@@ -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$'
diff --git a/ale_linters/python/pydocstyle.vim b/ale_linters/python/pydocstyle.vim
index 3901db4d..69ae3807 100644
--- a/ale_linters/python/pydocstyle.vim
+++ b/ale_linters/python/pydocstyle.vim
@@ -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
diff --git a/ale_linters/reason/ols.vim b/ale_linters/reason/ols.vim
index 66137e1b..9fbd9b4f 100644
--- a/ale_linters/reason/ols.vim
+++ b/ale_linters/reason/ols.vim
@@ -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'),
\})
diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim
index 410ed0ea..483806a6 100644
--- a/ale_linters/ruby/rubocop.vim
+++ b/ale_linters/ruby/rubocop.vim
@@ -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
diff --git a/ale_linters/ruby/standardrb.vim b/ale_linters/ruby/standardrb.vim
index f751e803..6ccfd2d6 100644
--- a/ale_linters/ruby/standardrb.vim
+++ b/ale_linters/ruby/standardrb.vim
@@ -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
diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim
index 171fe64e..73ab3608 100644
--- a/ale_linters/sh/shell.vim
+++ b/ale_linters/sh/shell.vim
@@ -1,5 +1,5 @@
" Author: w0rp
-" 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')
diff --git a/ale_linters/sql/sqllint.vim b/ale_linters/sql/sqllint.vim
new file mode 100644
index 00000000..78396fe9
--- /dev/null
+++ b/ale_linters/sql/sqllint.vim
@@ -0,0 +1,33 @@
+" ale_linters/sql/sqllint.vim
+" Author: Joe Reynolds
+" 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',
+\})
diff --git a/ale_linters/swift/swiftformat.vim b/ale_linters/swift/swiftformat.vim
new file mode 100644
index 00000000..2504511a
--- /dev/null
+++ b/ale_linters/swift/swiftformat.vim
@@ -0,0 +1,62 @@
+" Author: Klaas Pieter Annema
+" 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'
+\})
diff --git a/ale_linters/verilog/vlog.vim b/ale_linters/verilog/vlog.vim
index 951e2037..45e1977c 100644
--- a/ale_linters/verilog/vlog.vim
+++ b/ale_linters/verilog/vlog.vim
@@ -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
diff --git a/ale_linters/vim/vint.vim b/ale_linters/vim/vint.vim
index 65e19126..c7461bc8 100644
--- a/ale_linters/vim/vint.vim
+++ b/ale_linters/vim/vint.vim
@@ -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)
diff --git a/autoload/ale.vim b/autoload/ale.vim
index 6251b47b..5ec22f57 100644
--- a/autoload/ale.vim
+++ b/autoload/ale.vim
@@ -263,6 +263,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
diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim
index 291edcee..934fcaa8 100644
--- a/autoload/ale/assert.vim
+++ b/autoload/ale/assert.vim
@@ -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
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index 39892d42..cff53125 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -2,12 +2,27 @@
" Description: Functions for integrating with C-family linters.
call ale#Set('c_parse_makefile', 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']
+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')
@@ -61,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
@@ -76,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
@@ -143,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
@@ -211,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 = [{}, {}]
@@ -241,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
@@ -258,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
@@ -288,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
@@ -335,9 +490,7 @@ function! ale#c#GetCFlags(buffer, output) abort
endif
endif
- if ale#Var(a:buffer, 'c_parse_makefile')
- \&& !empty(a:output)
- \&& !empty(l:cflags)
+ if s:CanParseMakefile(a:buffer) && !empty(a:output) && !empty(l:cflags)
let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output)
endif
@@ -349,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
@@ -422,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',
-\])
diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim
index 60c3bbef..8c7263f3 100644
--- a/autoload/ale/code_action.vim
+++ b/autoload/ale/code_action.vim
@@ -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
diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim
index 1bbc4f4c..8f497169 100644
--- a/autoload/ale/command.vim
+++ b/autoload/ale/command.vim
@@ -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, /
" 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 = {
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index bffcdc8a..bdade95c 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -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 (ale_show_completion_menu)
+inoremap (ale_show_completion_menu)
" 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 (ale_show_completion_menu) :call ale#completion#RestoreCompletionOptions()
@@ -188,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)
@@ -215,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
@@ -241,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
@@ -318,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("\(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("\(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
@@ -383,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
@@ -415,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
@@ -436,11 +489,18 @@ 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_autoimport,
+ \ 'dup': get(l:info, 'additional_edits_only', 0)
+ \ || g:ale_completion_autoimport,
\ 'info': join(l:documentationParts, ''),
\}
@@ -450,7 +510,12 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
\ })
endif
- call add(l:results, l:result)
+ " 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:result, 'user_data')
+ call add(l:results, l:result)
+ endif
endfor
let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', [])
@@ -524,7 +589,11 @@ function! ale#completion#ParseLSPCompletions(response) abort
" Don't use LSP items with additional text edits when autoimport for
" completions is turned off.
- if has_key(l:item, 'additionalTextEdits') && !g:ale_completion_autoimport
+ if !empty(get(l:item, 'additionalTextEdits'))
+ \&& !(
+ \ get(l:info, 'additional_edits_only', 0)
+ \ || g:ale_completion_autoimport
+ \)
continue
endif
@@ -546,38 +615,50 @@ function! ale#completion#ParseLSPCompletions(response) abort
let l:text_changes = []
for l:edit in l:item.additionalTextEdits
- let l:range = l:edit.range
call add(l:text_changes, {
\ 'start': {
- \ 'line': l:range.start.line + 1,
- \ 'offset': l:range.start.character + 1,
+ \ 'line': l:edit.range.start.line + 1,
+ \ 'offset': l:edit.range.start.character + 1,
\ },
\ 'end': {
- \ 'line': l:range.end.line + 1,
- \ 'offset': l:range.end.character + 1,
+ \ 'line': l:edit.range.end.line + 1,
+ \ 'offset': l:edit.range.end.character + 1,
\ },
\ 'newText': l:edit.newText,
\})
endfor
- let l:changes = [{
- \ 'fileName': expand('#' . l:buffer . ':p'),
- \ 'textChanges': l:text_changes,
- \}]
- \
- let l:result.user_data = json_encode({
- \ 'codeActions': [{
- \ 'description': 'completion',
- \ 'changes': l:changes,
- \ }],
- \ })
+ if !empty(l:text_changes)
+ let l:result.user_data = json_encode({
+ \ 'codeActions': [{
+ \ 'description': 'completion',
+ \ 'changes': [
+ \ {
+ \ 'fileName': expand('#' . l:buffer . ':p'),
+ \ 'textChanges': l:text_changes,
+ \ }
+ \ ],
+ \ }],
+ \})
+ endif
endif
- call add(l:results, l:result)
+ " 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:result, 'user_data')
+ 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]
@@ -601,13 +682,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
@@ -681,7 +767,8 @@ function! s:OnReady(linter, lsp_details) abort
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
\ b:ale_completion_info.prefix,
- \ g:ale_completion_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
@@ -740,9 +827,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
@@ -761,6 +858,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')
@@ -777,6 +879,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('')
+
+ 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')
@@ -855,6 +988,7 @@ function! ale#completion#HandleUserData(completed_item) abort
if l:source isnot# 'ale-automatic'
\&& l:source isnot# 'ale-manual'
\&& l:source isnot# 'ale-callback'
+ \&& l:source isnot# 'ale-import'
return
endif
@@ -884,6 +1018,8 @@ function! ale#completion#Done() abort
endfunction
augroup ALECompletionActions
+ autocmd!
+
autocmd CompleteDone * call ale#completion#HandleUserData(v:completed_item)
augroup END
diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim
index 8c331c5c..9ca6fb15 100644
--- a/autoload/ale/cursor.vim
+++ b/autoload/ale/cursor.vim
@@ -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.
diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim
index ffcd9d10..0c1fb7cf 100644
--- a/autoload/ale/definition.vim
+++ b/autoload/ale/definition.vim
@@ -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
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 491d3c2e..ae0354b8 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -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,68 +444,6 @@ 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
let l:info = get(g:ale_buffer_info, a:buffer, {})
call ale#command#StopJobs(a:buffer, 'linter')
@@ -608,10 +508,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 +524,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,33 +546,62 @@ 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)
- call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters)
+function! s:GetLintFileValues(slots, Callback) abort
+ let l:deferred_list = []
+ let l:new_slots = []
- " We can only clear the results if we aren't checking the buffer.
- let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer)
+ 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
- silent doautocmd User ALELintPre
+ 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
- for l:linter in a:linters
+ 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,
+\ slots,
+\ should_lint_file,
+\ new_buffer,
+\ can_clear_results
+\) abort
+ let l:can_clear_results = a:can_clear_results
+
+ 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 +616,49 @@ 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:StopCurrentJobs(a:buffer, a:should_lint_file)
+ call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters)
+
+ " We can only clear the results if we aren't checking the buffer.
+ let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer)
+
+ silent doautocmd User ALELintPre
+
+ " Handle `lint_file` callbacks first.
+ 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
+
+ call s:GetLintFileValues(l:linter_slots, {
+ \ new_slots -> s:RunLinters(
+ \ a:buffer,
+ \ new_slots,
+ \ a:should_lint_file,
+ \ l:new_buffer,
+ \ l:can_clear_results,
+ \ )
+ \})
+endfunction
+
" Clean up a buffer.
"
" This function will stop all current jobs for the buffer,
diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim
index 731e36f2..3568c117 100644
--- a/autoload/ale/events.vim
+++ b/autoload/ale/events.vim
@@ -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('')), '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('')), '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('')), 'lint_delay'))
endif
if g:ale_lint_on_enter
diff --git a/autoload/ale/filename_mapping.vim b/autoload/ale/filename_mapping.vim
new file mode 100644
index 00000000..76d47acc
--- /dev/null
+++ b/autoload/ale/filename_mapping.vim
@@ -0,0 +1,22 @@
+" Author: w0rp
+" 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
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index 69817b36..8b841b13 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -1,4 +1,8 @@
-call ale#Set('fix_on_save_ignore', {})
+" Author: w0rp
+" 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')
@@ -90,7 +101,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 +112,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 +135,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 +153,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 +176,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 +187,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 +211,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 +294,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.
@@ -389,3 +383,4 @@ endfunction
augroup ALEBufferFixGroup
autocmd!
autocmd BufEnter * call ale#fix#ApplyQueuedFixes(str2nr(expand('')))
+augroup END
diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim
index 3dc5c774..0a729cf9 100644
--- a/autoload/ale/fix/registry.vim
+++ b/autoload/ale/fix/registry.vim
@@ -175,11 +175,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['php'],
\ 'description': 'Fix PHP files with php-cs-fixer.',
\ },
-\ 'astyle': {
+\ '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'],
@@ -380,11 +380,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.
diff --git a/autoload/ale/fixers/dhall.vim b/autoload/ale/fixers/dhall.vim
new file mode 100644
index 00000000..18f6006c
--- /dev/null
+++ b/autoload/ale/fixers/dhall.vim
@@ -0,0 +1,23 @@
+" Author: Pat Brisbin
+" 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
diff --git a/autoload/ale/fixers/latexindent.vim b/autoload/ale/fixers/latexindent.vim
index b0a0884a..54f1231e 100644
--- a/autoload/ale/fixers/latexindent.vim
+++ b/autoload/ale/fixers/latexindent.vim
@@ -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
diff --git a/autoload/ale/fixers/ocamlformat.vim b/autoload/ale/fixers/ocamlformat.vim
index 9b7c3e12..b12d2eb9 100644
--- a/autoload/ale/fixers/ocamlformat.vim
+++ b/autoload/ale/fixers/ocamlformat.vim
@@ -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
diff --git a/autoload/ale/fixers/prettier.vim b/autoload/ale/fixers/prettier.vim
index 23120777..e0f4972e 100644
--- a/autoload/ale/fixers/prettier.vim
+++ b/autoload/ale/fixers/prettier.vim
@@ -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',
diff --git a/autoload/ale/fixers/prettier_standard.vim b/autoload/ale/fixers/prettier_standard.vim
index b6e0a6f9..9d982ff6 100644
--- a/autoload/ale/fixers/prettier_standard.vim
+++ b/autoload/ale/fixers/prettier_standard.vim
@@ -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
diff --git a/autoload/ale/fixers/remark_lint.vim b/autoload/ale/fixers/remark_lint.vim
new file mode 100644
index 00000000..3ce442f3
--- /dev/null
+++ b/autoload/ale/fixers/remark_lint.vim
@@ -0,0 +1,24 @@
+" Author: blyoa
+" 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
+
diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim
index d9615256..cdfb014a 100644
--- a/autoload/ale/fixers/rubocop.vim
+++ b/autoload/ale/fixers/rubocop.vim
@@ -29,8 +29,7 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort
\ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . (l:auto_correct_all ? ' --auto-correct-all' : ' --auto-correct')
- \ . ' --force-exclusion --stdin '
- \ . ale#Escape(expand('#' . a:buffer . ':p'))
+ \ . ' --force-exclusion --stdin %s'
endfunction
function! ale#fixers#rubocop#Fix(buffer) abort
diff --git a/autoload/ale/fixers/standard.vim b/autoload/ale/fixers/standard.vim
index cffa9f9d..46decebf 100644
--- a/autoload/ale/fixers/standard.vim
+++ b/autoload/ale/fixers/standard.vim
@@ -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
diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim
index ec16b977..0b37c98a 100644
--- a/autoload/ale/handlers/gcc.vim
+++ b/autoload/ale/handlers/gcc.vim
@@ -10,7 +10,7 @@ let s:pragma_error = '#pragma once in main file'
" :8:5: warning: conversion lacks type at end of format [-Wformat=]
" :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 \:(\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# ''
+ \ && !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"
diff --git a/autoload/ale/handlers/sh.vim b/autoload/ale/handlers/sh.vim
index 75eaf71f..1e50cb89 100644
--- a/autoload/ale/handlers/sh.vim
+++ b/autoload/ale/handlers/sh.vim
@@ -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
diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim
index 168ff424..38b4b866 100644
--- a/autoload/ale/hover.vim
+++ b/autoload/ale/hover.vim
@@ -264,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
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index 0e935149..b483fc19 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -32,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.
"
@@ -46,7 +46,7 @@ let s:default_ale_linters = {
\ 'perl': ['perlcritic'],
\ 'perl6': [],
\ 'python': ['flake8', 'mypy', 'pylint', 'pyright'],
-\ 'rust': ['cargo'],
+\ 'rust': ['cargo', 'rls'],
\ 'spec': [],
\ 'text': [],
\ 'vue': ['eslint', 'vls'],
@@ -77,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'
@@ -114,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
@@ -131,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
@@ -188,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
@@ -212,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')
@@ -254,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')
@@ -274,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'
@@ -325,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
@@ -347,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
@@ -522,9 +396,7 @@ 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)
@@ -532,24 +404,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
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index ae8fd51d..7d99e9d2 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -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
@@ -449,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
diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim
index e4148ceb..dcd76e8f 100644
--- a/autoload/ale/lsp_linter.vim
+++ b/autoload/ale/lsp_linter.vim
@@ -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
diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim
index 30550503..fed95ccd 100644
--- a/autoload/ale/path.vim
+++ b/autoload/ale/path.vim
@@ -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 && '
" 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.
diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim
index faf45cb0..8b94aa7a 100644
--- a/autoload/ale/preview.vim
+++ b/autoload/ale/preview.vim
@@ -1,14 +1,22 @@
" Author: w0rp
" 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...
"
@@ -81,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
diff --git a/autoload/ale/test.vim b/autoload/ale/test.vim
index 082d91ff..6fcbf35e 100644
--- a/autoload/ale/test.vim
+++ b/autoload/ale/test.vim
@@ -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.
diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim
index bb9c1961..1f396377 100644
--- a/autoload/ale/util.vim
+++ b/autoload/ale/util.vim
@@ -423,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')
diff --git a/doc/ale-c.txt b/doc/ale-c.txt
index fc0d941a..b0d94b8e 100644
--- a/doc/ale-c.txt
+++ b/doc/ale-c.txt
@@ -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*
@@ -55,6 +69,11 @@ 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
@@ -94,22 +113,58 @@ g:ale_c_astyle_project_options *g:ale_c_astyle_project_options*
===============================================================================
-clang *ale-c-clang*
+cc *ale-c-cc*
+ *ale-c-gcc*
+ *ale-c-clang*
-g:ale_c_clang_executable *g:ale_c_clang_executable*
- *b:ale_c_clang_executable*
+g:ale_c_cc_executable *g:ale_c_cc_executable*
+ *b:ale_c_cc_executable*
Type: |String|
- Default: `'clang'`
+ Default: `''`
- This variable can be changed to use a different executable for clang.
+ 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_clang_options *g:ale_c_clang_options*
- *b:ale_c_clang_options*
+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.
===============================================================================
@@ -294,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*
@@ -332,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:
diff --git a/doc/ale-cpp.txt b/doc/ale-cpp.txt
index fbe31370..17894e6e 100644
--- a/doc/ale-cpp.txt
+++ b/doc/ale-cpp.txt
@@ -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|
@@ -38,41 +42,58 @@ g:ale_cpp_astyle_project_options *g:ale_cpp_astyle_project_options*
===============================================================================
-clang *ale-cpp-clang*
+cc *ale-cpp-cc*
+ *ale-cpp-gcc*
+ *ale-cpp-clang*
-g:ale_cpp_clang_executable *g:ale_cpp_clang_executable*
- *b:ale_cpp_clang_executable*
+g:ale_cpp_cc_executable *g:ale_cpp_cc_executable*
+ *b:ale_cpp_cc_executable*
Type: |String|
- Default: `'clang++'`
+ Default: `''`
- This variable can be changed to use a different executable for clang.
+ 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_clang_options *g:ale_cpp_clang_options*
- *b:ale_cpp_clang_options*
+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 changed to modify flags given to clang.
+ This variable can be change to modify flags given to the C++ compiler.
===============================================================================
-clangd *ale-cpp-clangd*
+ccls *ale-cpp-ccls*
-g:ale_cpp_clangd_executable *g:ale_cpp_clangd_executable*
- *b:ale_cpp_clangd_executable*
+g:ale_cpp_ccls_executable *g:ale_cpp_ccls_executable*
+ *b:ale_cpp_ccls_executable*
Type: |String|
- Default: `'clangd'`
+ Default: `'ccls'`
- This variable can be changed to use a different executable for clangd.
+ This variable can be changed to use a different executable for ccls.
-g:ale_cpp_clangd_options *g:ale_cpp_clangd_options*
- *b:ale_cpp_clangd_options*
- Type: |String|
- Default: `''`
+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 modify flags given to clangd.
+ 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.
===============================================================================
@@ -106,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*
@@ -295,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:
diff --git a/doc/ale-development.txt b/doc/ale-development.txt
index faa570c1..afd9798f 100644
--- a/doc/ale-development.txt
+++ b/doc/ale-development.txt
@@ -13,6 +13,7 @@ CONTENTS *ale-development-contents*
4. Testing ALE..........................|ale-development-tests|
4.1. Writing Linter Tests.............|ale-development-linter-tests|
4.2. Writing Fixer Tests..............|ale-development-fixer-tests|
+ 4.3. Running Tests in a Windows VM....|ale-development-windows-tests|
===============================================================================
1. Introduction *ale-development-introduction*
@@ -170,6 +171,11 @@ will run all of the tests in Vader, Vint checks, and several Bash scripts for
finding extra issues. Run `./run-tests --help` to see all of the options the
script supports. Note that the script supports selecting particular test files.
+Once you get used to dealing with Vim and NeoVim compatibility issues, you
+probably want to use `./run-tests --fast -q` for running tests with only the
+fastest available Vim version, and with success messages from tests
+suppressed.
+
Generally write tests for any changes you make. The following types of tests
are recommended for the following types of code.
@@ -353,5 +359,81 @@ given the above setup are as follows.
`AssertFixerNotExecuted` - Check that fixers will not be executed.
+===============================================================================
+4.3 Running Tests in a Windows VM *ale-development-windows-tests*
+
+Tests are run for ALE in a build of Vim 8 for Windows via AppVeyor. These
+tests can frequently break due to minor differences in paths and how escaping
+is done for commands on Windows. If you are a Linux or Mac user, running these
+tests locally can be difficult. Here is a process that will make that easier.
+
+First, you want to install a Windows image with VirtualBox. Install VirtualBox
+and grab a VirtualBox image for Windows such as from here:
+https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/
+
+NOTE: If you need to enter a password for the virtual machine at any point,
+the password is "Passw0rd!" without the double quotes.
+
+NOTE: If your trial period for Windows runs out, run the commands like the
+wallpaper tells you to.
+
+Your virtual machine will need to have PowerShell installed. Before you go any
+further, confirm that PowerShell is installed in your Windows virtual machine.
+
+Consult the VirtualBox documentation on how to install "Guest Additions."
+You probably want to install "Guest Additions" for most things to work
+properly.
+
+After you've loaded your virtual machine image, go into "Settings" for your
+virtual machine, and "Shared Folders." Add a shared folder with the name
+"ale", and set the "Folder Path" to the path to your ALE repository, for
+example: "/home/w0rp/ale"
+
+Find out which drive letter "ale" has been mounted as in Windows. We'll use
+"E:" as the drive letter, for example. Open the command prompt as an
+administrator by typing in `cmd` in the start menu, right clicking on the
+command prompt application, and clicking "Run as administrator." Click "Yes"
+when prompted to ask if you're sure you want to run the command prompt. You
+should type in the following command to mount the "ale" directory for testing,
+where "E:" is replaced with your drive letter. >
+
+ mklink /D C:\testplugin E:
+<
+Close the administrator Command Prompt, and try running the command
+`type C:\testplugin\LICENSE` in a new Command Prompt which you are NOT running
+as administrator. You should see the license for ALE in your terminal. After
+you have confirmed that you have mounted ALE on your machine, search in the
+Start Menu for "power shell," run PowerShell as an administrator, and issue
+the following commands to install the correct Vim and Vader versions for
+running tests. >
+
+ Add-Type -A System.IO.Compression.FileSystem
+
+ Invoke-WebRequest ftp://ftp.vim.org/pub/vim/pc/vim80-586w32.zip -OutFile C:\vim.zip
+ [IO.Compression.ZipFile]::ExtractToDirectory('C:\vim.zip', 'C:\vim')
+ rm C:\vim.zip
+
+ Invoke-WebRequest ftp://ftp.vim.org/pub/vim/pc/vim80-586rt.zip -OutFile C:\rt.zip
+ [IO.Compression.ZipFile]::ExtractToDirectory('C:\rt.zip', 'C:\vim')
+ rm C:\rt.zip
+
+ Invoke-WebRequest https://github.com/junegunn/vader.vim/archive/c6243dd81c98350df4dec608fa972df98fa2a3af.zip -OutFile C:\vader.zip
+ [IO.Compression.ZipFile]::ExtractToDirectory('C:\vader.zip', 'C:\')
+ mv C:\vader.vim-c6243dd81c98350df4dec608fa972df98fa2a3af C:\vader
+ rm C:\vader.zip
+<
+After you have finished installing everything, you can run all of the tests
+in Windows by opening a Command Prompt NOT as an administrator by navigating
+to the directory where you've mounted the ALE code, which must be named
+`C:\testplugin`, and by running the `run-tests.bat` batch file. >
+
+ cd C:\testplugin
+ run-tests
+<
+It will probably take several minutes for all of the tests to run. Be patient.
+You can run a specific test by passing the filename as an argument to the
+batch file, for example: `run-tests test/test_c_flag_parsing.vader` . This will
+give you results much more quickly.
+
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-elixir.txt b/doc/ale-elixir.txt
index 5864f728..de9daacf 100644
--- a/doc/ale-elixir.txt
+++ b/doc/ale-elixir.txt
@@ -6,7 +6,7 @@ ALE Elixir Integration *ale-elixir-options*
mix *ale-elixir-mix*
-The `mix` linter is disabled by default, as it can bee too expensive to run.
+The `mix` linter is disabled by default, as it can be too expensive to run.
See `:help g:ale_linters`
diff --git a/doc/ale-handlebars.txt b/doc/ale-handlebars.txt
index 5daec5b3..4a5a3870 100644
--- a/doc/ale-handlebars.txt
+++ b/doc/ale-handlebars.txt
@@ -14,7 +14,8 @@ ember-template-lint *ale-handlebars-embertemplatelint*
g:ale_handlebars_embertemplatelint_executable
*g:ale_handlebars_embertemplatelint_executable*
- Type: |String| *b:ale_handlebars_embertemplatelint_executable*
+ *b:ale_handlebars_embertemplatelint_executable*
+ Type: |String|
Default: `'ember-template-lint'`
See |ale-integrations-local-executables|
@@ -22,7 +23,8 @@ g:ale_handlebars_embertemplatelint_executable
g:ale_handlebars_embertemplatelint_use_global
*g:ale_handlebars_embertemplatelint_use_global*
- Type: |Number| *b:ale_handlebars_embertemplatelint_use_global*
+ *b:ale_handlebars_embertemplatelint_use_global*
+ Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
diff --git a/doc/ale-markdown.txt b/doc/ale-markdown.txt
index 4e27eb91..99848878 100644
--- a/doc/ale-markdown.txt
+++ b/doc/ale-markdown.txt
@@ -2,6 +2,17 @@
ALE Markdown Integration *ale-markdown-options*
+===============================================================================
+markdownlint *ale-markdown-markdownlint*
+
+g:ale_markdown_markdownlint_options *g:ale_markdown_markdownlint_options*
+ *b:ale_markdown_markdownlint_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional options to markdownlint.
+
+
===============================================================================
mdl *ale-markdown-mdl*
diff --git a/doc/ale-php.txt b/doc/ale-php.txt
index 645decd7..9fe868f8 100644
--- a/doc/ale-php.txt
+++ b/doc/ale-php.txt
@@ -189,42 +189,55 @@ g:ale_php_psalm_executable *g:ale_php_psalm_executable*
This variable sets the executable used for psalm.
-g:ale_psalm_langserver_options *g:ale_psalm_langserver_options*
- *b:ale_psalm_langserver_options*
+
+g:ale_php_psalm_options *g:ale_php_psalm_options*
+ *b:ale_php_psalm_options*
Type: |String|
Default: `''`
This variable can be set to pass additional options to psalm.
-===============================================================================
-php-cs-fixer *ale-php-php-cs-fixer*
-g:ale_php_cs_fixer_executable *g:ale_php_cs_fixer_executable*
- *b:ale_php_cs_fixer_executable*
+g:ale_php_psalm_use_global *g:ale_php_psalm_use_global*
+ *b:ale_php_psalm_use_global*
+ Type: |Boolean|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+
+===============================================================================
+php-cs-fixer *ale-php-php-cs-fixer*
+
+g:ale_php_cs_fixer_executable *g:ale_php_cs_fixer_executable*
+ *b:ale_php_cs_fixer_executable*
Type: |String|
Default: `'php-cs-fixer'`
This variable sets executable used for php-cs-fixer.
-g:ale_php_cs_fixer_use_global *g:ale_php_cs_fixer_use_global*
- *b:ale_php_cs_fixer_use_global*
- Type: |Boolean|
- Default: `get(g:, 'ale_use_global_executables', 0)`
- This variable force globally installed fixer.
-
-g:ale_php_cs_fixer_options *g:ale_php_cs_fixer_options*
- *b:ale_php_cs_fixer_options*
+g:ale_php_cs_fixer_options *g:ale_php_cs_fixer_options*
+ *b:ale_php_cs_fixer_options*
Type: |String|
Default: `''`
This variable can be set to pass additional options to php-cs-fixer.
-===============================================================================
-php *ale-php-php*
-g:ale_php_php_executable *g:ale_php_php_executable*
- *b:ale_php_php_executable*
+g:ale_php_cs_fixer_use_global *g:ale_php_cs_fixer_use_global*
+ *b:ale_php_cs_fixer_use_global*
+ Type: |Boolean|
+ Default: `get(g:, 'ale_use_global_executables', 0)`
+
+ See |ale-integrations-local-executables|
+
+
+===============================================================================
+php *ale-php-php*
+
+g:ale_php_php_executable *g:ale_php_php_executable*
+ *b:ale_php_php_executable*
Type: |String|
Default: `'php'`
diff --git a/doc/ale-python.txt b/doc/ale-python.txt
index 60b0771d..6b1a6d33 100644
--- a/doc/ale-python.txt
+++ b/doc/ale-python.txt
@@ -169,13 +169,14 @@ flake8 *ale-python-flake8*
g:ale_python_flake8_change_directory *g:ale_python_flake8_change_directory*
*b:ale_python_flake8_change_directory*
- Type: |Number|
- Default: `1`
+ Type: |String|
+ Default: `project`
- If set to `1`, ALE will switch to the directory the Python file being
- checked with `flake8` is in before checking it. This helps `flake8` find
- configuration files more easily. This option can be turned off if you want
- to control the directory Python is executed from yourself.
+ If set to `project`, ALE will switch to the project root before checking file.
+ If set to `file`, ALE will switch to directory the Python file being
+ checked with `flake8` is in before checking it.
+ You can turn it off with `off` option if you want to control the directory
+ Python is executed from yourself.
g:ale_python_flake8_executable *g:ale_python_flake8_executable*
diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt
index 2c0222b7..f4b4e7b7 100644
--- a/doc/ale-rust.txt
+++ b/doc/ale-rust.txt
@@ -31,11 +31,11 @@ Integration Information
5. rustfmt -- If you have `rustfmt` installed, you can use it as a fixer to
consistently reformat your Rust code.
- Only cargo is enabled by default. To switch to using rustc instead of cargo,
- configure |g:ale_linters| appropriately: >
+ Only cargo and rls are enabled by default. To switch to using rustc instead
+ of cargo, configure |g:ale_linters| appropriately: >
" See the help text for the option for more information.
- let g:ale_linters = {'rust': ['rustc']}
+ let g:ale_linters = {'rust': ['rustc', 'rls']}
<
Also note that rustc 1.12. or later is needed.
@@ -60,6 +60,7 @@ g:ale_rust_analyzer_config *g:ale_rust_analyzer_config*
Dictionary with configuration settings for rust-analyzer.
+
===============================================================================
cargo *ale-rust-cargo*
@@ -252,23 +253,25 @@ g:ale_rust_ignore_error_codes *g:ale_rust_ignore_error_codes*
>
let g:ale_rust_ignore_error_codes = ['E0432', 'E0433']
+
g:ale_rust_ignore_secondary_spans *g:ale_rust_ignore_secondary_spans*
*b:ale_rust_ignore_secondary_spans*
Type: Number
Default: 0
- When set to 1, instructs the Rust error repporting to ignore secondary
- spans. The problem with secondary spans is that they sometimes appear in
- error messages before the main cause of the error, for example: >
+ When set to 1, instructs the Rust error reporting to ignore secondary spans.
+ The problem with secondary spans is that they sometimes appear in error
+ messages before the main cause of the error, for example: >
1 src/main.rs|98 col 5 error| this function takes 4 parameters but 5
- parameters were supplied: defined here
+ parameters were supplied: defined here
2 src/main.rs|430 col 32 error| this function takes 4 parameters but 5
- parameters were supplied: expected 4 parameters
+ parameters were supplied: expected 4 parameters
<
This is due to the sorting by line numbers. With this option set to 1,
the 'defined here' span will not be presented.
+
===============================================================================
rustfmt *ale-rust-rustfmt*
diff --git a/doc/ale-sql.txt b/doc/ale-sql.txt
index 2807271b..398e24d3 100644
--- a/doc/ale-sql.txt
+++ b/doc/ale-sql.txt
@@ -3,7 +3,7 @@ ALE SQL Integration *ale-sql-options*
===============================================================================
-pgformatter *ale-sql-pgformatter*
+pgformatter *ale-sql-pgformatter*
g:ale_sql_pgformatter_executable *g:ale_sql_pgformatter_executable*
*b:ale_sql_pgformatter_executable*
diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt
index 2dc05287..c6bcf421 100644
--- a/doc/ale-supported-languages-and-tools.txt
+++ b/doc/ale-supported-languages-and-tools.txt
@@ -21,6 +21,7 @@ Notes:
* `drafter`
* AsciiDoc
* `alex`!!
+ * `languagetool`!!
* `proselint`
* `redpen`
* `textlint`
@@ -47,7 +48,7 @@ Notes:
* C
* `astyle`
* `ccls`
- * `clang`
+ * `clang` (`cc`)
* `clangd`
* `clang-format`
* `clangtidy`!!
@@ -55,7 +56,7 @@ Notes:
* `cpplint`!!
* `cquery`
* `flawfinder`
- * `gcc`
+ * `gcc` (`cc`)
* `uncrustify`
* C#
* `csc`!!
@@ -65,7 +66,7 @@ Notes:
* C++ (filetype cpp)
* `astyle`
* `ccls`
- * `clang`
+ * `clang` (`cc`)
* `clangcheck`!!
* `clangd`
* `clang-format`
@@ -75,7 +76,7 @@ Notes:
* `cpplint`!!
* `cquery`
* `flawfinder`
- * `gcc`
+ * `gcc` (`cc`)
* `uncrustify`
* Chef
* `cookstyle`
@@ -118,6 +119,8 @@ Notes:
* `dartanalyzer`!!
* `dartfmt`!!
* `language_server`
+* Dhall
+ * `dhall-format`
* Dockerfile
* `dockerfile_lint`
* `hadolint`
@@ -446,6 +449,7 @@ Notes:
* `sqlfmt`
* `sqlformat`
* `sqlint`
+ * `sql-lint`
* Stylus
* `stylelint`
* SugarSS
@@ -453,6 +457,7 @@ Notes:
* Swift
* `sourcekit-lsp`
* `swiftformat`
+ * `swift-format`
* `swiftlint`
* Tcl
* `nagelfar`!!
diff --git a/doc/ale.txt b/doc/ale.txt
index 4b914b82..2192755b 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -9,8 +9,9 @@ CONTENTS *ale-contents*
1. Introduction.........................|ale-introduction|
2. Supported Languages & Tools..........|ale-support|
3. Linting..............................|ale-lint|
- 3.1 Adding Language Servers...........|ale-lint-language-servers|
- 3.2 Other Sources.....................|ale-lint-other-sources|
+ 3.1 Linting On Other Machines.........|ale-lint-other-machines|
+ 3.2 Adding Language Servers...........|ale-lint-language-servers|
+ 3.3 Other Sources.....................|ale-lint-other-sources|
4. Fixing Problems......................|ale-fix|
5. Language Server Protocol Support.....|ale-lsp|
5.1 Completion........................|ale-completion|
@@ -148,7 +149,61 @@ ALE offers several options for controlling which linters are run.
-------------------------------------------------------------------------------
-3.1 Adding Language Servers *ale-lint-language-servers*
+3.1 Linting On Other Machines *ale-lint-other-machines*
+
+ALE offers support for running linters or fixers on files you are editing
+locally on other machines, so long as the other machine has access the file
+you are editing. This could be a linter or fixer run inside of a Docker image,
+running in a virtual machine, running on a remote server, etc.
+
+In order to run tools on other machines, you will need to configure your tools
+to run via scripts that execute commands on those machines, such as by setting
+the ALE `_executable` options for those tools to a path for a script to run,
+or by using |g:ale_command_wrapper| to specify a script to wrap all commands
+that are run by ALE, before they are executed. For tools that ALE runs where
+ALE looks for locally installed executables first, you may need to set the
+`_use_global` options for those tools to `1`, or you can set
+|g:ale_use_global_executables| to `1` before ALE is loaded to only use global
+executables for all tools.
+
+In order for ALE to properly lint or fix files which are running on another
+file system, you must provide ALE with |List|s of strings for mapping paths to
+and from your local file system and the remote file system, such as the file
+system of your Docker container. See |g:ale_filename_mappings| for all of the
+different ways these filename mappings can be configured.
+
+For example, you might configure `pylint` to run via Docker by creating a
+script like so. >
+
+ #!/usr/bin/env bash
+
+ exec docker run --rm -v "$(pwd):/data" cytopia/pylint "$@"
+<
+With the above script in mind, you might configure ALE to lint your Python
+project with `pylint` by providing the path to the script to execute, and
+mappings which describe how to between the two file systems in your
+`python.vim` |ftplugin| file, like so: >
+
+ if expand('%:p') =~# '^/home/w0rp/git/test-pylint/'
+ let b:ale_linters = ['pylint']
+ let b:ale_python_pylint_use_global = 1
+ " This is the path to the script above.
+ let b:ale_python_pylint_executable = '/home/w0rp/git/test-pylint/pylint.sh'
+ " /data matches the path in Docker.
+ let b:ale_filename_mappings = {
+ \ 'pylint': [
+ \ ['/home/w0rp/git/test-pylint', '/data'],
+ \ ],
+ \}
+ endif
+<
+
+You might consider using a Vim plugin for loading Vim configuration files
+specific to each project, if you have a lot of projects to manage.
+
+
+-------------------------------------------------------------------------------
+3.2 Adding Language Servers *ale-lint-language-servers*
ALE comes with many default configurations for language servers, so they can
be detected and run automatically. ALE can connect to other language servers
@@ -189,7 +244,7 @@ address to connect to instead. >
-------------------------------------------------------------------------------
-3.2 Other Sources *ale-lint-other-sources*
+3.3 Other Sources *ale-lint-other-sources*
Problems for a buffer can be taken from other sources and rendered by ALE.
This allows ALE to be used in combination with other plugins which also want
@@ -287,6 +342,8 @@ are supported for running the commands.
file will be created, containing the lines from the file
after previous adjustment have been done.
+ See |ale-command-format-strings| for formatting options.
+
`read_temporary_file` When set to `1`, ALE will read the contents of the
temporary file created for `%t`. This option can be used
for commands which need to modify some file on disk in
@@ -356,6 +413,10 @@ by default.
Fixers can be disabled on save with |g:ale_fix_on_save_ignore|. They will
still be run when you manually run |ALEFix|.
+Fixers can be run on another machines, just like linters, such as fixers run
+from a Docker container, running in a virtual machine, running a remote
+server, etc. See |ale-lint-other-machines|.
+
===============================================================================
5. Language Server Protocol Support *ale-lsp*
@@ -402,12 +463,56 @@ is loaded. The delay for completion can be configured with
|g:ale_completion_delay|. This setting should not be enabled if you wish to
use ALE as a completion source for other plugins.
+ALE automatic completion will not work when 'paste' is active. Only set
+'paste' when you are copy and pasting text into your buffers.
+
+ALE automatic completion will interfere with default insert completion with
+`CTRL-N` and so on (|compl-vim|). You can write your own keybinds and a
+function in your |vimrc| file to force insert completion instead, like so: >
+
+ function! SmartInsertCompletion() abort
+ " Use the default CTRL-N in completion menus
+ if pumvisible()
+ return "\"
+ endif
+
+ " Exit and re-enter insert mode, and use insert completion
+ return "\a\"
+ endfunction
+
+ inoremap =SmartInsertCompletion()
+<
ALE provides an 'omnifunc' function |ale#completion#OmniFunc| for triggering
completion manually with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O| >
" Use ALE's function for omnicompletion.
set omnifunc=ale#completion#OmniFunc
<
+ *ale-completion-fallback*
+
+You can write your own completion function and fallback on other methods of
+completion by checking if there are no results that ALE can determine. For
+example, for Python code, you could fall back on the `python3complete`
+function. >
+
+ function! TestCompletionFunc(findstart, base) abort
+ let l:result = ale#completion#OmniFunc(a:findstart, a:base)
+
+ " Check if ALE couldn't find anything.
+ if (a:findstart && l:result is -3)
+ \|| (!a:findstart && empty(l:result))
+ " Defer to another omnifunc if ALE couldn't find anything.
+ return python3complete#Complete(a:findstart, a:base)
+ endif
+
+ return l:result
+ endfunction
+
+ set omnifunc=TestCompletionFunc
+<
+See |complete-functions| for documentation on how to write completion
+functions.
+
ALE will only suggest so many possible matches for completion. The maximum
number of items can be controlled with |g:ale_completion_max_suggestions|.
@@ -421,6 +526,12 @@ completion information with Deoplete, consult Deoplete's documentation.
ALE by can support automatic imports from external modules. This behavior can
be enabled by setting the |g:ale_completion_autoimport| variable to `1`.
+You can manually request imports for symbols at the cursor with the
+|ALEImport| command. The word at the cursor must be an exact match for some
+potential completion result which includes additional text to insert into the
+current buffer, which ALE will assume is code for an import line. This command
+can be useful when your code already contains something you need to import.
+
When working with TypeScript files, ALE can remove warnings from your
completions by setting the |g:ale_completion_tsserver_remove_warnings|
variable to 1.
@@ -501,15 +612,9 @@ displayed.
-------------------------------------------------------------------------------
5.4 Find References *ale-find-references*
-ALE supports finding references for symbols though any enabled LSP linters.
-ALE will display a preview window showing the places where a symbol is
-referenced in a codebase when a command is run. The following commands are
-supported:
-
-|ALEFindReferences| - Find references for the word under the cursor.
-
-Options:
- `-relative` Show file paths in the results relative to the working dir
+ALE supports finding references for symbols though any enabled LSP linters
+with the |ALEFindReferences| command. See the documentation for the command
+for a full list of options.
-------------------------------------------------------------------------------
5.5 Hovering *ale-hover*
@@ -552,13 +657,9 @@ Documentation for symbols at the cursor can be retrieved using the
-------------------------------------------------------------------------------
5.6 Symbol Search *ale-symbol-search*
-ALE supports searching for workspace symbols via LSP linters. The following
-commands are supported:
-
-|ALESymbolSearch| - Search for symbols in the workspace.
-
-Options:
- `-relative` Show file paths in the results relative to the working dir
+ALE supports searching for workspace symbols via LSP linters with the
+|ALESymbolSearch| command. See the documentation for the command
+for a full list of options.
===============================================================================
6. Global Options *ale-options*
@@ -678,13 +779,18 @@ g:ale_completion_enabled *g:ale_completion_enabled*
This setting should not be enabled if you wish to use ALE as a completion
source for other completion plugins.
+ ALE automatic completion will not work when 'paste' is active. Only set
+ 'paste' when you are copy and pasting text into your buffers.
+
A buffer-local version of this setting `b:ale_completion_enabled` can be set
to `0` to disable ALE's automatic completion support for a single buffer.
ALE's completion support must be enabled globally to be enabled locally.
See |ale-completion|
-g:ale_completion_tsserver_remove_warnings *g:ale_completion_tsserver_remove_warnings*
+
+ *g:ale_completion_tsserver_remove_warnings*
+g:ale_completion_tsserver_remove_warnings
Type: Number
Default: `0`
@@ -693,6 +799,7 @@ g:ale_completion_tsserver_remove_warnings *g:ale_completion_tsserver_remove_warn
including those that are a warning. Warnings can be excluded from completed
items by setting it to `1`.
+
g:ale_completion_autoimport *g:ale_completion_autoimport*
Type: Number
@@ -807,7 +914,7 @@ g:ale_default_navigation *g:ale_default_navigation*
Default: `'buffer'`
The default method for navigating away from the current buffer to another
- buffer, such as for |ALEFindReferences:|, or |ALEGoToDefinition|.
+ buffer, such as for |ALEFindReferences|, or |ALEGoToDefinition|.
g:ale_disable_lsp *g:ale_disable_lsp*
@@ -1118,7 +1225,7 @@ g:ale_list_window_size *g:ale_list_window_size*
g:ale_lint_delay *g:ale_lint_delay*
-
+ *b:ale_lint_delay*
Type: |Number|
Default: `200`
@@ -1126,6 +1233,9 @@ g:ale_lint_delay *g:ale_lint_delay*
be run after text is changed. This option is only meaningful with the
|g:ale_lint_on_text_changed| variable set to `always`, `insert`, or `normal`.
+ A buffer-local option, `b:ale_lint_delay`, can be set to change the delay
+ for different buffers, such as in |ftplugin| files.
+
g:ale_lint_on_enter *g:ale_lint_on_enter*
@@ -1283,6 +1393,90 @@ g:ale_linter_aliases *g:ale_linter_aliases*
<
No linters will be loaded when the buffer's filetype is empty.
+
+g:ale_filename_mappings *g:ale_filename_mappings*
+ *b:ale_filename_mappings*
+
+ Type: |Dictionary| or |List|
+ Default: `{}`
+
+ Either a |Dictionary| mapping a linter or fixer name, as displayed in
+ |:ALEInfo|, to a |List| of two-item |List|s for filename mappings, or just a
+ |List| of two-item |List|s. When given some paths to files, the value of
+ this setting will be used to convert filenames on a local file system to
+ filenames on some remote file system, such as paths in a Docker image,
+ virtual machine, or network drive.
+
+ For example: >
+
+ let g:ale_filename_mappings = {
+ \ 'pylint': [
+ \ ['/home/john/proj', '/data'],
+ \ ],
+ \}
+<
+ With the above configuration, a filename such as `/home/john/proj/foo.py`
+ will be provided to the linter/fixer as `/data/foo.py`, and paths parsed
+ from linter results such as `/data/foo.py` will be converted back to
+ `/home/john/proj/foo.py`.
+
+ You can use `*` as to apply a |List| of filename mappings to all other
+ linters or fixers not otherwise matched. >
+
+ " Use one List of paths for pylint.
+ " Use another List of paths for everything else.
+ let g:ale_filename_mappings = {
+ \ 'pylint': [
+ \ ['/home/john/proj', '/data'],
+ \ ],
+ \ '*': [
+ \ ['/home/john/proj', '/other-data'],
+ \ ],
+ \}
+<
+ If you just want every single linter or fixer to use the same filename
+ mapping, you can just use a |List|. >
+
+ " Same as above, but for ALL linters and fixers.
+ let g:ale_filename_mappings = [
+ \ ['/home/john/proj', '/data'],
+ \]
+<
+ You can provide many such filename paths for multiple projects. Paths are
+ matched by checking if the start of a file path matches the given strings,
+ in a case-sensitive manner. Earlier entries in the |List| will be tried
+ before later entries when mapping to a given file system.
+
+ Buffer-local options can be set to the same values to override the global
+ options, such as in |ftplugin| files.
+
+ NOTE: Only fixers registered with a short name can support filename mapping
+ by their fixer names. See |ale-fix|. Filename mappings set for all tools by
+ using only a |List| for the setting will also be applied to fixers not in
+ the registry.
+
+ NOTE: In order for this filename mapping to work correctly, linters and
+ fixers must exclusively determine paths to files to lint or fix via ALE
+ command formatting as per |ale-command-format-strings|, and paths parsed
+ from linter files must be provided in `filename` keys if a linter returns
+ results for more than one file at a time, as per |ale-loclist-format|. If
+ you discover a linter or fixer which does not behave properly, please report
+ it as an issue.
+
+ If you are running a linter or fixer through Docker or another remote file
+ system, you may have to mount your temporary directory, which you can
+ discover with the following command: >
+
+ :echo fnamemodify(tempname(), ':h:h')
+<
+ You should provide a mapping from this temporary directory to whatever you
+ mount this directory to in Docker, or whatever remote file system you are
+ working with.
+
+ You can inspect the filename mappings ALE will use with the
+ |ale#GetFilenameMappings()| function.
+
+
g:ale_linters *g:ale_linters*
*b:ale_linters*
Type: |Dictionary|
@@ -1303,7 +1497,7 @@ g:ale_linters *g:ale_linters*
\ 'perl': ['perlcritic'],
\ 'perl6': [],
\ 'python': ['flake8', 'mypy', 'pylint', 'pyright'],
- \ 'rust': ['cargo'],
+ \ 'rust': ['cargo', 'rls'],
\ 'spec': [],
\ 'text': [],
\ 'vue': ['eslint', 'vls'],
@@ -2319,16 +2513,15 @@ documented in additional help files.
bibclean..............................|ale-bib-bibclean|
c.......................................|ale-c-options|
astyle................................|ale-c-astyle|
- clang.................................|ale-c-clang|
+ cc....................................|ale-c-cc|
+ ccls..................................|ale-c-ccls|
clangd................................|ale-c-clangd|
clang-format..........................|ale-c-clangformat|
clangtidy.............................|ale-c-clangtidy|
cppcheck..............................|ale-c-cppcheck|
cquery................................|ale-c-cquery|
flawfinder............................|ale-c-flawfinder|
- gcc...................................|ale-c-gcc|
uncrustify............................|ale-c-uncrustify|
- ccls..................................|ale-c-ccls|
chef....................................|ale-chef-options|
cookstyle.............................|ale-chef-cookstyle|
foodcritic............................|ale-chef-foodcritic|
@@ -2342,9 +2535,10 @@ documented in additional help files.
cmake-format..........................|ale-cmake-cmakeformat|
cpp.....................................|ale-cpp-options|
astyle................................|ale-cpp-astyle|
- clang.................................|ale-cpp-clang|
- clangd................................|ale-cpp-clangd|
+ cc....................................|ale-cpp-cc|
+ ccls..................................|ale-cpp-ccls|
clangcheck............................|ale-cpp-clangcheck|
+ clangd................................|ale-cpp-clangd|
clang-format..........................|ale-cpp-clangformat|
clangtidy.............................|ale-cpp-clangtidy|
clazy.................................|ale-cpp-clazy|
@@ -2352,9 +2546,7 @@ documented in additional help files.
cpplint...............................|ale-cpp-cpplint|
cquery................................|ale-cpp-cquery|
flawfinder............................|ale-cpp-flawfinder|
- gcc...................................|ale-cpp-gcc|
uncrustify............................|ale-cpp-uncrustify|
- ccls..................................|ale-cpp-ccls|
c#......................................|ale-cs-options|
csc...................................|ale-cs-csc|
mcs...................................|ale-cs-mcs|
@@ -2506,6 +2698,7 @@ documented in additional help files.
luac..................................|ale-lua-luac|
luacheck..............................|ale-lua-luacheck|
markdown................................|ale-markdown-options|
+ markdownlint..........................|ale-markdown-markdownlint|
mdl...................................|ale-markdown-mdl|
prettier..............................|ale-markdown-prettier|
remark-lint...........................|ale-markdown-remark-lint|
@@ -2768,13 +2961,20 @@ ALEFindReferences *ALEFindReferences*
The default method used for navigating to a new location can be changed
by modifying |g:ale_default_navigation|.
+ You can add `-relative` to the command to view results with relatives paths,
+ instead of absolute paths.
+
The selection can be opened again with the |ALERepeatSelection| command.
You can jump back to the position you were at before going to a reference of
something with jump motions like CTRL-O. See |jump-motions|.
A plug mapping `(ale_find_references)` is defined for this command.
+ You can define additional plug mapping with any additional options you want
+ like so: >
+ nnoremap (my_mapping) :ALEFindReferences -relative
+<
ALEFix *ALEFix*
@@ -2812,7 +3012,13 @@ ALEGoToDefinition `` *ALEGoToDefinition*
command. Otherwise, Vim will refuse to leave the buffer you're jumping from
unless you have saved your edits.
- A plug mapping `(ale_go_to_definition)` is defined for this command.
+ The following Plug mappings are defined for this command, which correspond
+ to the following commands.
+
+ `(ale_go_to_definition)` - `:ALEGoToDefinition`
+ `(ale_go_to_definition_in_tab)` - `:ALEGoToDefinition -tab`
+ `(ale_go_to_definition_in_split)` - `:ALEGoToDefinition -split`
+ `(ale_go_to_definition_in_vsplit)` - `:ALEGoToDefinition -vsplit`
ALEGoToTypeDefinition *ALEGoToTypeDefinition*
@@ -2834,8 +3040,13 @@ ALEGoToTypeDefinition *ALEGoToTypeDefinition*
You can jump back to the position you were at before going to the definition
of something with jump motions like CTRL-O. See |jump-motions|.
- A plug mapping `(ale_go_to_type_definition)` is defined for this
- command.
+ The following Plug mappings are defined for this command, which correspond
+ to the following commands.
+
+ `(ale_go_to_type_definition)` - `:ALEGoToTypeDefinition`
+ `(ale_go_to_type_definition_in_tab)` - `:ALEGoToTypeDefinition -tab`
+ `(ale_go_to_type_definition_in_split)` - `:ALEGoToTypeDefinition -split`
+ `(ale_go_to_type_definition_in_vsplit)` - `:ALEGoToTypeDefinition -vsplit`
ALEHover *ALEHover*
@@ -2851,6 +3062,23 @@ ALEHover *ALEHover*
A plug mapping `(ale_hover)` is defined for this command.
+ALEImport *ALEImport*
+
+ Try to import a symbol using `tsserver` or a Language Server.
+
+ ALE will look for completions for the word at the cursor which contain
+ additional text edits that possible insert lines to import the symbol. The
+ first match with additional text edits will be used, and may add other code
+ to the current buffer other than import lines.
+
+ If linting is enabled, and |g:ale_lint_on_text_changed| is set to ever check
+ buffers when text is changed, the buffer will be checked again after changes
+ are made.
+
+ A Plug mapping `(ale_import)` is defined for this command. This
+ mapping should only be bound for normal mode.
+
+
ALEOrganizeImports *ALEOrganizeImports*
Organize imports using tsserver. Currently not implemented for LSPs.
@@ -2858,9 +3086,10 @@ ALEOrganizeImports *ALEOrganizeImports*
ALERename *ALERename*
- Rename a symbol using TypeScript server or Language Server.
+ Rename a symbol using `tsserver` or a Language Server.
- The user will be prompted for a new name.
+ The symbol where the cursor is resting will be the symbol renamed, and a
+ prompt will open to request a new name.
ALERepeatSelection *ALERepeatSelection*
@@ -2875,14 +3104,17 @@ ALESymbolSearch `` *ALESymbolSearch*
The arguments provided to this command will be used as a search query for
finding symbols in the workspace, such as functions, types, etc.
+ You can add `-relative` to the command to view results with relatives paths,
+ instead of absolute paths.
+
*:ALELint*
ALELint *ALELint*
Run ALE once for the current buffer. This command can be used to run ALE
manually, instead of automatically, if desired.
- This command will also run linters where `lint_file` is set to `1`, or in
- other words linters which check the file instead of the Vim buffer.
+ This command will also run linters where `lint_file` is evaluates to `1`,
+ meaning linters which check the file instead of the Vim buffer.
A plug mapping `(ale_lint)` is defined for this command.
@@ -3064,6 +3296,15 @@ ale#Env(variable_name, value) *ale#Env()*
'set VAR="some value" && command' # On Windows
+ale#GetFilenameMappings(buffer, name) *ale#GetFilenameMappings()*
+
+ Given a `buffer` and the `name` of either a linter for fixer, return a
+ |List| of two-item |List|s that describe mapping to and from the local and
+ foreign file systems for running a particular linter or fixer.
+
+ See |g:ale_filename_mappings| for details on filename mapping.
+
+
ale#Has(feature) *ale#Has()*
Return `1` if ALE supports a given feature, like |has()| for Vim features.
@@ -3086,9 +3327,9 @@ ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()*
The linters will always be run in the background. Calling this function
again from the same buffer
- An optional `linting_flag` argument can be given. If `linting_flag`
- is `'lint_file'`, then linters where the `lint_file` option is set to `1` will be
- run. Linters with `lint_file` set to `1` are not run by default.
+ An optional `linting_flag` argument can be given. If `linting_flag` is
+ `'lint_file'`, then linters where the `lint_file` option evaluates to `1`
+ will be run. Otherwise, those linters will not be run.
An optional `buffer_number` argument can be given for specifying the buffer
to check. The active buffer (`bufnr('')`) will be checked by default.
@@ -3178,23 +3419,36 @@ ale#command#Run(buffer, command, callback, [options]) *ale#command#Run()*
<
The following `options` can be provided.
- `output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or `'none`' for
- selecting which output streams to read lines from.
+ `output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or
+ `'none`' for selecting which output streams to read
+ lines from.
- The default is `'stdout'`
+ The default is `'stdout'`
- `executable` - An executable for formatting into `%e` in the command.
- If this option is not provided, formatting commands with
- `%e` will not work.
+ `executable` - An executable for formatting into `%e` in the
+ command. If this option is not provided, formatting
+ commands with `%e` will not work.
- `read_buffer` - If set to `1`, the buffer will be piped into the
- command.
+ `read_buffer` - If set to `1`, the buffer will be piped into the
+ command.
- The default is `0`.
+ The default is `0`.
+
+ `input` - When creating temporary files with `%t` or piping
+ text into a command `input` can be set to a |List| of
+ text to use instead of the buffer's text.
+
+ `filename_mappings` - A |List| of two-item |List|s describing filename
+ mappings to apply for formatted filenames in the
+ command string, as per |g:ale_filename_mappings|.
+
+ If the call to this function is being used for a
+ linter or fixer, the mappings should be provided with
+ this option, and can be retrieved easily with
+ |ale#GetFilenameMappings()|.
+
+ The default is `[]`.
- `input` - When creating temporary files with `%t` or piping text
- into a command `input` can be set to a |List| of text to
- use instead of the buffer's text.
ale#command#EscapeCommandPart(command_part) *ale#command#EscapeCommandPart()*
@@ -3409,24 +3663,30 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
if a command manually reads from a temporary file
instead, etc.
+ This option behaves as if it was set to `0` when the
+ `lint_file` option evaluates to `1`.
+
*ale-lint-file*
- `lint_file` A |Number| (`0` or `1`) indicating whether a command
- should read the file instead of the Vim buffer. This
- option can be used for linters which must check the
- file on disk, and which cannot check a Vim buffer
- instead.
+ `lint_file` A |Number| (`0` or `1`), or a |Funcref| for a function
+ accepting a buffer number for computing either `0` or
+ `1`, indicating whether a command should read the file
+ instead of the Vim buffer. This option can be used
+ for linters which must check the file on disk, and
+ which cannot check a Vim buffer instead.
- Linters set with this option will not be run as a
- user types, per |g:ale_lint_on_text_changed|. Linters
- will instead be run only when events occur against
- the file on disk, including |g:ale_lint_on_enter|
- and |g:ale_lint_on_save|. Linters with this option
- set to `1` will also be run when linters are run
- manually, per |ALELintPost-autocmd|.
+ The result can be computed with |ale#command#Run()|.
- When this option is set to `1`, `read_buffer` will
- be set automatically to `0`. The two options cannot
- be used together.
+ Linters where the eventual value of this option
+ evaluates to `1` will not be run as a user types, per
+ |g:ale_lint_on_text_changed|. Linters will instead be
+ run only when events occur against the file on disk,
+ including |g:ale_lint_on_enter| and
+ |g:ale_lint_on_save|. Linters where this option
+ evaluates to `1` will also be run when the |ALELint|
+ command is run.
+
+ When this option is evaluates to `1`, ALE will behave
+ as if `read_buffer` was set to `0`.
*ale-lsp-linters*
`lsp` A |String| for defining LSP (Language Server Protocol)
@@ -3565,6 +3825,16 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
command, so literal character sequences `%s` and `%t` can be escaped by
using `%%s` and `%%t` instead, etc.
+ Some |filename-modifiers| can be applied to `%s` and `%t`. Only `:h`, `:t`,
+ `:r`, and `:e` may be applied, other modifiers will be ignored. Filename
+ modifiers can be applied to the format markers by placing them after them.
+
+ For example: >
+ 'command': '%s:h %s:e %s:h:t',
+<
+ Given a path `/foo/baz/bar.txt`, the above command string will generate
+ something akin to `'/foo/baz' 'txt' 'baz'`
+
If a callback for a command generates part of a command string which might
possibly contain `%%`, `%s`, `%t`, or `%e`, where the special formatting
behavior is not desired, the |ale#command#EscapeCommandPart()| function can
diff --git a/plugin/ale.vim b/plugin/ale.vim
index 65c5a77c..9cebb71f 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -97,6 +97,10 @@ let g:ale_fix_on_save = get(g:, 'ale_fix_on_save', 0)
" should be used instead.
let g:ale_enabled = get(g:, 'ale_enabled', 1)
+" A Dictionary mapping linter or fixer names to Arrays of two-item Arrays
+" mapping filename paths from one system to another.
+let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {})
+
" These flags dictates if ale uses the quickfix or the loclist (loclist is the
" default, quickfix overrides loclist).
let g:ale_set_loclist = get(g:, 'ale_set_loclist', 1)
@@ -207,19 +211,9 @@ command! -bar ALEFixSuggest :call ale#fix#registry#Suggest(&filetype)
" Go to definition for tsserver and LSP
command! -bar -nargs=* ALEGoToDefinition :call ale#definition#GoToCommandHandler('', )
-" Deprecated commands we have to keep for now.
-command! -bar ALEGoToDefinitionInTab :call ale#definition#GoTo({'open_in': 'tab', 'deprecated_command': 'ALEGoToDefinitionInTab'})
-command! -bar ALEGoToDefinitionInSplit :call ale#definition#GoTo({'open_in': 'split', 'deprecated_command': 'ALEGoToDefinitionInSplit'})
-command! -bar ALEGoToDefinitionInVSplit :call ale#definition#GoTo({'open_in': 'vsplit', 'deprecated_command': 'ALEGoToDefinitionInVSplit'})
-
" Go to type definition for tsserver and LSP
command! -bar -nargs=* ALEGoToTypeDefinition :call ale#definition#GoToCommandHandler('type', )
-" Deprecated commands we have to keep for now.
-command! -bar ALEGoToTypeDefinitionInTab :call ale#definition#GoToType({'open_in': 'tab', 'deprecated_command': 'ALEGoToTypeDefinitionInTab'})
-command! -bar ALEGoToTypeDefinitionInSplit :call ale#definition#GoToType({'open_in': 'split', 'deprecated_command': 'ALEGoToTypeDefinitionInSplit'})
-command! -bar ALEGoToTypeDefinitionInVSplit :call ale#definition#GoToType({'open_in': 'vsplit', 'deprecated_command': 'ALEGoToTypeDefinitionInVSplit'})
-
" Repeat a previous selection in the preview window
command! -bar ALERepeatSelection :call ale#preview#RepeatSelection()
@@ -235,8 +229,12 @@ command! -bar ALEDocumentation :call ale#hover#ShowDocumentationAtCursor()
" Search for appearances of a symbol, such as a type name or function name.
command! -nargs=1 ALESymbolSearch :call ale#symbol#Search()
+" Complete text with tsserver and LSP
command! -bar ALEComplete :call ale#completion#GetCompletions('ale-manual')
+" Try to find completions for the current symbol that add additional text.
+command! -bar ALEImport :call ale#completion#Import()
+
" Rename symbols using tsserver and LSP
command! -bar ALERename :call ale#rename#Execute()
@@ -270,22 +268,21 @@ nnoremap (ale_lint) :ALELint
nnoremap (ale_detail) :ALEDetail
nnoremap (ale_fix) :ALEFix
nnoremap (ale_go_to_definition) :ALEGoToDefinition
+nnoremap (ale_go_to_definition_in_tab) :ALEGoToDefinition -tab
+nnoremap (ale_go_to_definition_in_split) :ALEGoToDefinition -split
+nnoremap (ale_go_to_definition_in_vsplit) :ALEGoToDefinition -vsplit
nnoremap (ale_go_to_type_definition) :ALEGoToTypeDefinition
+nnoremap (ale_go_to_type_definition_in_tab) :ALEGoToTypeDefinition -tab
+nnoremap (ale_go_to_type_definition_in_split) :ALEGoToTypeDefinition -split
+nnoremap (ale_go_to_type_definition_in_vsplit) :ALEGoToTypeDefinitionIn -vsplit
nnoremap (ale_find_references) :ALEFindReferences
nnoremap (ale_hover) :ALEHover
nnoremap (ale_documentation) :ALEDocumentation
inoremap (ale_complete) :ALEComplete
+nnoremap (ale_import) :ALEImport
nnoremap (ale_rename) :ALERename
nnoremap (ale_repeat_selection) :ALERepeatSelection
-" Deprecated mappings
-nnoremap (ale_go_to_definition_in_tab) :ALEGoToDefinitionInTab
-nnoremap (ale_go_to_definition_in_split) :ALEGoToDefinitionInSplit
-nnoremap (ale_go_to_definition_in_vsplit) :ALEGoToDefinitionInVSplit
-nnoremap (ale_go_to_type_definition_in_tab) :ALEGoToTypeDefinitionInTab
-nnoremap (ale_go_to_type_definition_in_split) :ALEGoToTypeDefinitionInSplit
-nnoremap (ale_go_to_type_definition_in_vsplit) :ALEGoToTypeDefinitionInVSplit
-
" Set up autocmd groups now.
call ale#events#Init()
diff --git a/run-tests b/run-tests
index c71f90d1..1d452a72 100755
--- a/run-tests
+++ b/run-tests
@@ -83,6 +83,13 @@ while [ $# -ne 0 ]; do
run_neovim_03_tests=0
shift
;;
+ --fast)
+ run_vim_80_tests=0
+ run_vim_81_tests=0
+ run_neovim_02_tests=0
+ run_neovim_03_tests=1
+ shift
+ ;;
--help)
echo 'Usage: ./run-tests [OPTION]... [FILE]...'
echo
@@ -99,6 +106,7 @@ while [ $# -ne 0 ]; do
echo ' --vim-80-only Run tests only for Vim 8.0'
echo ' --vim-81-only Run tests only for Vim 8.1'
echo ' --linters-only Run only Vint and custom checks'
+ echo ' --fast Run only the fastest Vim and custom checks'
echo ' --help Show this help text'
echo ' -- Stop parsing options after this'
exit 0
diff --git a/supported-tools.md b/supported-tools.md
index d708ae80..640456cc 100644
--- a/supported-tools.md
+++ b/supported-tools.md
@@ -16,7 +16,7 @@ formatting.
| Key | Definition |
| ------------- | -------------------------------- |
-| :floppy_disk: | Only checked when saved to disk |
+| :floppy_disk: | May only run on files on disk |
| :warning: | Disabled by default |
---
@@ -30,6 +30,7 @@ formatting.
* [drafter](https://github.com/apiaryio/drafter)
* AsciiDoc
* [alex](https://github.com/wooorm/alex) :floppy_disk:
+ * [languagetool](https://languagetool.org/) :floppy_disk:
* [proselint](http://proselint.com/)
* [redpen](http://redpen.cc/)
* [textlint](https://textlint.github.io/)
@@ -128,9 +129,9 @@ formatting.
* [dartfmt](https://github.com/dart-lang/sdk/tree/master/utils/dartfmt)
* [language_server](https://github.com/natebosch/dart_language_server)
* Dhall
- * [dhall-format](https://github.com/dhall-lang)
- * [dhall-freeze](https://github.com/dhall-lang)
- * [dhall-lint](https://github.com/dhall-lang)
+ * [dhall-format](https://github.com/dhall-lang/dhall-lang)
+ * [dhall-freeze](https://github.com/dhall-lang/dhall-lang)
+ * [dhall-lint](https://github.com/dhall-lang/dhall-lang)
* Dockerfile
* [dockerfile_lint](https://github.com/projectatomic/dockerfile_lint)
* [hadolint](https://github.com/hadolint/hadolint)
@@ -459,6 +460,7 @@ formatting.
* [sqlfmt](https://github.com/jackc/sqlfmt)
* [sqlformat](https://github.com/andialbrecht/sqlparse)
* [sqlint](https://github.com/purcell/sqlint)
+ * [sql-lint](https://github.com/joereynolds/sql-lint)
* Stylus
* [stylelint](https://github.com/stylelint/stylelint)
* SugarSS
@@ -466,6 +468,7 @@ formatting.
* Swift
* [sourcekit-lsp](https://github.com/apple/sourcekit-lsp)
* [swiftformat](https://github.com/nicklockwood/SwiftFormat)
+ * [swift-format](https://github.com/apple/swift-format)
* [swiftlint](https://github.com/realm/SwiftLint)
* Tcl
* [nagelfar](http://nagelfar.sourceforge.net) :floppy_disk:
diff --git a/test/command_callback/test_ada_gcc_command_callbacks.vader b/test/command_callback/test_ada_gcc_command_callbacks.vader
index de6e355e..906b31a4 100644
--- a/test/command_callback/test_ada_gcc_command_callbacks.vader
+++ b/test/command_callback/test_ada_gcc_command_callbacks.vader
@@ -18,11 +18,10 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The executable should be configurable):
-
AssertLinter 'gcc',
\ ale#Escape('gcc') . ' -x ada -c -gnatc'
\ . ' -o ' . b:out_file
- \ . ' -I ' . ale#Escape(getcwd())
+ \ . ' -I %s:h'
\ . ' -gnatwa -gnatq %t'
let b:ale_ada_gcc_executable = 'foo'
@@ -30,15 +29,14 @@ Execute(The executable should be configurable):
AssertLinter 'foo',
\ ale#Escape('foo') . ' -x ada -c -gnatc'
\ . ' -o ' . b:out_file
- \ . ' -I ' . ale#Escape(getcwd())
+ \ . ' -I %s:h'
\ . ' -gnatwa -gnatq %t'
Execute(The options should be configurable):
-
let g:ale_ada_gcc_options = '--foo --bar'
AssertLinter 'gcc',
\ ale#Escape('gcc') . ' -x ada -c -gnatc'
\ . ' -o ' . b:out_file
- \ . ' -I ' . ale#Escape(getcwd())
+ \ . ' -I %s:h'
\ . ' --foo --bar %t'
diff --git a/test/command_callback/test_asm_gcc_command_callbacks.vader b/test/command_callback/test_asm_gcc_command_callbacks.vader
index 42606ec0..5976b5f2 100644
--- a/test/command_callback/test_asm_gcc_command_callbacks.vader
+++ b/test/command_callback/test_asm_gcc_command_callbacks.vader
@@ -3,7 +3,7 @@ Before:
call ale#test#SetFilename('test.cpp')
let b:command_tail = ' -x assembler'
\ . ' -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . '-iquote ' . ale#Escape(g:dir)
+ \ . '-iquote %s:h'
\ . ' -Wall -'
After:
diff --git a/test/command_callback/test_c_cc_command_callbacks.vader b/test/command_callback/test_c_cc_command_callbacks.vader
new file mode 100644
index 00000000..c8c2de7d
--- /dev/null
+++ b/test/command_callback/test_c_cc_command_callbacks.vader
@@ -0,0 +1,55 @@
+Before:
+ Save g:ale_c_parse_makefile
+ Save g:ale_history_enabled
+
+ let g:ale_c_parse_makefile = 0
+ let g:ale_history_enabled = 0
+
+ let g:get_cflags_return_value = ''
+ let g:executable_map = {}
+
+ runtime autoload/ale/c.vim
+ runtime autoload/ale/engine.vim
+
+ function! ale#engine#IsExecutable(buffer, executable) abort
+ return has_key(g:executable_map, a:executable)
+ endfunction
+
+ function! ale#c#GetCFlags(buffer, output) abort
+ return g:get_cflags_return_value
+ endfunction
+
+ call ale#assert#SetUpLinterTest('c', 'cc')
+
+ let b:command_tail = ' -S -x c'
+ \ . ' -o ' . (has('win32') ? 'nul': '/dev/null')
+ \ . ' -iquote %s:h'
+ \ . ' -std=c11 -Wall -'
+
+After:
+ unlet! g:get_cflags_return_value
+ unlet! g:executable_map
+ unlet! b:command_tail
+
+ runtime autoload/ale/c.vim
+ runtime autoload/ale/engine.vim
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(clang should be used instead of gcc, if available):
+ let g:executable_map = {'clang': 1}
+
+ AssertLinter 'clang', [ale#Escape('clang') . b:command_tail]
+
+Execute(The executable should be configurable):
+ AssertLinter 'gcc', [ale#Escape('gcc') . b:command_tail]
+
+ let b:ale_c_cc_executable = 'foobar'
+
+ AssertLinter 'foobar', [ale#Escape('foobar') . b:command_tail]
+
+Execute(The -std flag should be replaced by parsed C flags):
+ let b:command_tail = substitute(b:command_tail, 'c11', 'c99 ', '')
+ let g:get_cflags_return_value = '-std=c99'
+
+ AssertLinter 'gcc', ale#Escape('gcc') . b:command_tail
diff --git a/test/command_callback/test_c_clang_command_callbacks.vader b/test/command_callback/test_c_clang_command_callbacks.vader
deleted file mode 100644
index b8c02e4d..00000000
--- a/test/command_callback/test_c_clang_command_callbacks.vader
+++ /dev/null
@@ -1,20 +0,0 @@
-Before:
- Save g:ale_c_parse_makefile
- let g:ale_c_parse_makefile = 0
-
- call ale#assert#SetUpLinterTest('c', 'clang')
- let b:command_tail = ' -S -x c -fsyntax-only -iquote'
- \ . ' ' . ale#Escape(getcwd())
- \ . ' -std=c11 -Wall -'
-
-After:
- unlet! b:command_tail
-
- call ale#assert#TearDownLinterTest()
-
-Execute(The executable should be configurable):
- AssertLinter 'clang', [ale#Escape('clang') . b:command_tail]
-
- let b:ale_c_clang_executable = 'foobar'
-
- AssertLinter 'foobar', [ale#Escape('foobar') . b:command_tail]
diff --git a/test/command_callback/test_c_gcc_command_callbacks.vader b/test/command_callback/test_c_gcc_command_callbacks.vader
deleted file mode 100644
index 2dbb8b7c..00000000
--- a/test/command_callback/test_c_gcc_command_callbacks.vader
+++ /dev/null
@@ -1,22 +0,0 @@
-Before:
- Save g:ale_c_parse_makefile
- let g:ale_c_parse_makefile = 0
-
- call ale#assert#SetUpLinterTest('c', 'gcc')
-
- let b:command_tail = ' -S -x c'
- \ . ' -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(getcwd())
- \ . ' -std=c11 -Wall -'
-
-After:
- call ale#assert#TearDownLinterTest()
-
- unlet! b:command_tail
-
-Execute(The executable should be configurable):
- AssertLinter 'gcc', [ale#Escape('gcc') . b:command_tail]
-
- let b:ale_c_gcc_executable = 'foobar'
-
- AssertLinter 'foobar', [ale#Escape('foobar') . b:command_tail]
diff --git a/test/command_callback/test_c_import_paths.vader b/test/command_callback/test_c_import_paths.vader
index e6102998..3c2bd79b 100644
--- a/test/command_callback/test_c_import_paths.vader
+++ b/test/command_callback/test_c_import_paths.vader
@@ -7,6 +7,7 @@ Before:
Save g:__ale_c_project_filenames
let g:original_project_filenames = g:__ale_c_project_filenames
+ let g:executable_map = {}
" Remove the .git/HEAD dir for C import paths for these tests.
" The tests run inside of a git repo.
@@ -18,106 +19,67 @@ Before:
let g:ale_c_parse_compile_commands = 0
let g:ale_c_parse_makefile = 0
+ runtime autoload/ale/engine.vim
+
+ function! ale#engine#IsExecutable(buffer, executable) abort
+ return has_key(g:executable_map, a:executable)
+ endfunction
+
After:
Restore
unlet! g:original_project_filenames
+ unlet! g:executable_map
+
+ runtime autoload/ale/engine.vim
call ale#assert#TearDownLinterTest()
-Execute(The C GCC handler should include 'include' directories for projects with a Makefile):
- call ale#assert#SetUpLinterTest('c', 'gcc')
+Execute(The C cc linter should include 'include' directories for projects with a Makefile):
+ call ale#assert#SetUpLinterTest('c', 'cc')
call ale#test#SetFilename('../test_c_projects/makefile_project/subdir/file.c')
- let g:ale_c_gcc_options = ''
+ let g:ale_c_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include'))
\ . ' -'
-Execute(The C GCC handler should include 'include' directories for projects with a configure file):
- call ale#assert#SetUpLinterTest('c', 'gcc')
+Execute(The C cc linter should include 'include' directories for projects with a configure file):
+ call ale#assert#SetUpLinterTest('c', 'cc')
call ale#test#SetFilename('../test_c_projects/configure_project/subdir/file.c')
- let g:ale_c_gcc_options = ''
+ let g:ale_c_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include'))
\ . ' -'
-Execute(The C GCC handler should include root directories for projects with .h files in them):
- call ale#assert#SetUpLinterTest('c', 'gcc')
+Execute(The C cc linter should include root directories for projects with .h files in them):
+ call ale#assert#SetUpLinterTest('c', 'cc')
call ale#test#SetFilename('../test_c_projects/h_file_project/subdir/file.c')
- let g:ale_c_gcc_options = ''
+ let g:ale_c_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project'))
\ . ' -'
-Execute(The C GCC handler should include root directories for projects with .hpp files in them):
- call ale#assert#SetUpLinterTest('c', 'gcc')
+Execute(The C cc linter should include root directories for projects with .hpp files in them):
+ call ale#assert#SetUpLinterTest('c', 'cc')
call ale#test#SetFilename('../test_c_projects/hpp_file_project/subdir/file.c')
- let g:ale_c_gcc_options = ''
+ let g:ale_c_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project'))
- \ . ' -'
-
-Execute(The C Clang handler should include 'include' directories for projects with a Makefile):
- call ale#assert#SetUpLinterTest('c', 'clang')
- call ale#test#SetFilename('../test_c_projects/makefile_project/subdir/file.c')
- let g:ale_c_clang_options = ''
-
- AssertLinter 'clang',
- \ ale#Escape('clang')
- \ . ' -S -x c -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include'))
- \ . ' -'
-
-Execute(The C Clang handler should include 'include' directories for projects with a configure file):
- call ale#assert#SetUpLinterTest('c', 'clang')
- call ale#test#SetFilename('../test_c_projects/h_file_project/subdir/file.c')
- let g:ale_c_clang_options = ''
-
- AssertLinter 'clang',
- \ ale#Escape('clang')
- \ . ' -S -x c -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project'))
- \ . ' -'
-
-Execute(The C Clang handler should include root directories for projects with .h files in them):
- call ale#assert#SetUpLinterTest('c', 'clang')
- call ale#test#SetFilename('../test_c_projects/h_file_project/subdir/file.c')
- let g:ale_c_clang_options = ''
-
- AssertLinter 'clang',
- \ ale#Escape('clang')
- \ . ' -S -x c -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project'))
- \ . ' -'
-
-Execute(The C Clang handler should include root directories for projects with .hpp files in them):
- call ale#assert#SetUpLinterTest('c', 'clang')
- call ale#test#SetFilename('../test_c_projects/hpp_file_project/subdir/file.c')
- let g:ale_c_clang_options = ''
-
- AssertLinter 'clang',
- \ ale#Escape('clang')
- \ . ' -S -x c -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project'))
\ . ' -'
@@ -131,99 +93,51 @@ Execute(The C ClangTidy handler should include 'include' directories for project
\ . ' %s '
\ . '-- -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include'))
-Execute(The C++ GCC handler should include 'include' directories for projects with a Makefile):
- call ale#assert#SetUpLinterTest('cpp', 'gcc')
+Execute(The C++ cc linter should include 'include' directories for projects with a Makefile):
+ call ale#assert#SetUpLinterTest('cpp', 'cc')
call ale#test#SetFilename('../test_c_projects/makefile_project/subdir/file.cpp')
- let g:ale_cpp_gcc_options = ''
+ let g:ale_cpp_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include'))
\ . ' -'
-Execute(The C++ GCC handler should include 'include' directories for projects with a configure file):
- call ale#assert#SetUpLinterTest('cpp', 'gcc')
+Execute(The C++ cc linter should include 'include' directories for projects with a configure file):
+ call ale#assert#SetUpLinterTest('cpp', 'cc')
call ale#test#SetFilename('../test_c_projects/configure_project/subdir/file.cpp')
- let g:ale_cpp_gcc_options = ''
+ let g:ale_cpp_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include'))
\ . ' -'
-Execute(The C++ GCC handler should include root directories for projects with .h files in them):
- call ale#assert#SetUpLinterTest('cpp', 'gcc')
+Execute(The C++ cc linter should include root directories for projects with .h files in them):
+ call ale#assert#SetUpLinterTest('cpp', 'cc')
call ale#test#SetFilename('../test_c_projects/h_file_project/subdir/file.cpp')
- let g:ale_cpp_gcc_options = ''
+ let g:ale_cpp_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project'))
\ . ' -'
-Execute(The C++ GCC handler should include root directories for projects with .hpp files in them):
- call ale#assert#SetUpLinterTest('cpp', 'gcc')
+Execute(The C++ cc linter should include root directories for projects with .hpp files in them):
+ call ale#assert#SetUpLinterTest('cpp', 'cc')
call ale#test#SetFilename('../test_c_projects/hpp_file_project/subdir/file.cpp')
- let g:ale_cpp_gcc_options = ''
+ let g:ale_cpp_cc_options = ''
AssertLinter 'gcc',
\ ale#Escape('gcc')
\ . ' -S -x c++ -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project'))
- \ . ' -'
-
-Execute(The C++ Clang handler should include 'include' directories for projects with a Makefile):
- call ale#assert#SetUpLinterTest('cpp', 'clang')
- call ale#test#SetFilename('../test_c_projects/makefile_project/subdir/file.cpp')
- let g:ale_cpp_clang_options = ''
-
- AssertLinter 'clang++',
- \ ale#Escape('clang++')
- \ . ' -S -x c++ -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/makefile_project/include'))
- \ . ' -'
-
-Execute(The C++ Clang handler should include 'include' directories for projects with a configure file):
- call ale#assert#SetUpLinterTest('cpp', 'clang')
- call ale#test#SetFilename('../test_c_projects/configure_project/subdir/file.cpp')
- let g:ale_cpp_clang_options = ''
-
- AssertLinter 'clang++',
- \ ale#Escape('clang++')
- \ . ' -S -x c++ -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/configure_project/include'))
- \ . ' -'
-
-Execute(The C++ Clang handler should include root directories for projects with .h files in them):
- call ale#assert#SetUpLinterTest('cpp', 'clang')
- call ale#test#SetFilename('../test_c_projects/h_file_project/subdir/file.cpp')
- let g:ale_cpp_clang_options = ''
-
- AssertLinter 'clang++',
- \ ale#Escape('clang++')
- \ . ' -S -x c++ -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project/subdir'))
- \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/h_file_project'))
- \ . ' -'
-
-Execute(The C++ Clang handler should include root directories for projects with .hpp files in them):
- call ale#assert#SetUpLinterTest('cpp', 'clang')
- call ale#test#SetFilename('../test_c_projects/hpp_file_project/subdir/file.cpp')
- let g:ale_cpp_clang_options = ''
-
- AssertLinter 'clang++',
- \ ale#Escape('clang++')
- \ . ' -S -x c++ -fsyntax-only'
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project/subdir'))
+ \ . ' -iquote %s:h'
\ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/../test_c_projects/hpp_file_project'))
\ . ' -'
diff --git a/test/command_callback/test_cpp_cc_command_callbacks.vader b/test/command_callback/test_cpp_cc_command_callbacks.vader
new file mode 100644
index 00000000..dec3a07c
--- /dev/null
+++ b/test/command_callback/test_cpp_cc_command_callbacks.vader
@@ -0,0 +1,55 @@
+Before:
+ Save g:ale_c_parse_makefile
+ Save g:ale_history_enabled
+
+ let g:ale_c_parse_makefile = 0
+ let g:ale_history_enabled = 0
+
+ let g:get_cflags_return_value = ''
+ let g:executable_map = {}
+
+ runtime autoload/ale/c.vim
+ runtime autoload/ale/engine.vim
+
+ function! ale#engine#IsExecutable(buffer, executable) abort
+ return has_key(g:executable_map, a:executable)
+ endfunction
+
+ function! ale#c#GetCFlags(buffer, output) abort
+ return g:get_cflags_return_value
+ endfunction
+
+ call ale#assert#SetUpLinterTest('cpp', 'cc')
+
+ let b:command_tail = ' -S -x c++'
+ \ . ' -o ' . (has('win32') ? 'nul': '/dev/null')
+ \ . ' -iquote %s:h'
+ \ . ' -std=c++14 -Wall -'
+
+After:
+ unlet! g:get_cflags_return_value
+ unlet! g:executable_map
+ unlet! b:command_tail
+
+ runtime autoload/ale/c.vim
+ runtime autoload/ale/engine.vim
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(clang++ should be used instead of gcc, if available):
+ let g:executable_map = {'clang++': 1}
+
+ AssertLinter 'clang++', [ale#Escape('clang++') . b:command_tail]
+
+Execute(The executable should be configurable):
+ AssertLinter 'gcc', [ale#Escape('gcc') . b:command_tail]
+
+ let b:ale_cpp_cc_executable = 'foobar'
+
+ AssertLinter 'foobar', [ale#Escape('foobar') . b:command_tail]
+
+Execute(The -std flag should be replaced by parsed C flags):
+ let b:command_tail = substitute(b:command_tail, 'c++14', 'c++11 ', '')
+ let g:get_cflags_return_value = '-std=c++11'
+
+ AssertLinter 'gcc', ale#Escape('gcc') . b:command_tail
diff --git a/test/command_callback/test_cpp_clang_command_callbacks.vader b/test/command_callback/test_cpp_clang_command_callbacks.vader
deleted file mode 100644
index e96fd8e7..00000000
--- a/test/command_callback/test_cpp_clang_command_callbacks.vader
+++ /dev/null
@@ -1,19 +0,0 @@
-Before:
- Save g:ale_c_parse_makefile
- let g:ale_c_parse_makefile = 0
-
- call ale#assert#SetUpLinterTest('cpp', 'clang')
- let b:command_tail = ' -S -x c++ -fsyntax-only -iquote'
- \ . ' ' . ale#Escape(getcwd())
- \ . ' -std=c++14 -Wall -'
-
-After:
- unlet! b:command_tail
- call ale#assert#TearDownLinterTest()
-
-Execute(The executable should be configurable):
- AssertLinter 'clang++', ale#Escape('clang++') . b:command_tail
-
- let b:ale_cpp_clang_executable = 'foobar'
-
- AssertLinter 'foobar', ale#Escape('foobar') . b:command_tail
diff --git a/test/command_callback/test_cpp_gcc_command_callbacks.vader b/test/command_callback/test_cpp_gcc_command_callbacks.vader
deleted file mode 100644
index cfa4ecc0..00000000
--- a/test/command_callback/test_cpp_gcc_command_callbacks.vader
+++ /dev/null
@@ -1,20 +0,0 @@
-Before:
- Save g:ale_c_parse_makefile
- let g:ale_c_parse_makefile = 0
-
- call ale#assert#SetUpLinterTest('cpp', 'gcc')
- let b:command_tail = ' -S -x c++'
- \ . ' -o ' . (has('win32') ? 'nul': '/dev/null')
- \ . ' -iquote ' . ale#Escape(getcwd())
- \ . ' -std=c++14 -Wall -'
-
-After:
- unlet! b:command_tail
- call ale#assert#TearDownLinterTest()
-
-Execute(The executable should be configurable):
- AssertLinter 'gcc', ale#Escape('gcc') . b:command_tail
-
- let b:ale_cpp_gcc_executable = 'foobar'
-
- AssertLinter 'foobar', ale#Escape('foobar') . b:command_tail
diff --git a/test/command_callback/test_elixir_credo.vader b/test/command_callback/test_elixir_credo.vader
index 1a146db8..3eb88846 100644
--- a/test/command_callback/test_elixir_credo.vader
+++ b/test/command_callback/test_elixir_credo.vader
@@ -8,6 +8,18 @@ After:
call ale#assert#TearDownLinterTest()
+Execute(Builds credo command with normal project):
+ AssertLinter 'mix',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project'))
+ \ . 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s'
+
+Execute(Builds credo command with umbrella project):
+ call ale#test#SetFilename('elixir_paths/umbrella_project/apps/mix_project/lib/app.ex')
+
+ AssertLinter 'mix',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/umbrella_project'))
+ \ . 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s'
+
Execute(Builds credo command with --strict mode when set to 1):
let g:ale_elixir_credo_strict = 1
diff --git a/test/command_callback/test_embertemplatelint_command_callbacks.vader b/test/command_callback/test_embertemplatelint_command_callbacks.vader
new file mode 100644
index 00000000..97687d29
--- /dev/null
+++ b/test/command_callback/test_embertemplatelint_command_callbacks.vader
@@ -0,0 +1,17 @@
+Before:
+ call ale#assert#SetUpLinterTest('handlebars', 'embertemplatelint')
+
+ GivenCommandOutput ['1.6.0']
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(ember-template-lint executables runs the right command):
+ AssertLinter 'ember-template-lint',
+ \ ale#Escape('ember-template-lint') . ' --json --filename %s'
+
+Execute(old ember-template-lint executables runs the right command):
+ GivenCommandOutput []
+
+ AssertLinter 'ember-template-lint',
+ \ ale#Escape('ember-template-lint') . ' --json %t'
diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader
index f082a63a..09f64ee3 100644
--- a/test/command_callback/test_flake8_command_callback.vader
+++ b/test/command_callback/test_flake8_command_callback.vader
@@ -34,13 +34,52 @@ Execute(The flake8 callbacks should return the correct default values):
\]
Execute(The option for disabling changing directories should work):
- let g:ale_python_flake8_change_directory = 0
+ let g:ale_python_flake8_change_directory = 'off'
AssertLinter 'flake8', [
\ ale#Escape('flake8') . ' --version',
\ ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
\]
+ let g:ale_python_flake8_change_directory = 0
+
+ AssertLinter 'flake8', [
+ \ ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
+ \]
+
+ " Invalid options should be considered the same as turning the setting off.
+ let g:ale_python_flake8_change_directory = 'xxx'
+
+ AssertLinter 'flake8', [
+ \ ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
+ \]
+
+Execute(The option for changing directory to project root should work):
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/namespace_package_tox/namespace/foo/bar.py')
+
+ AssertLinter 'flake8', [
+ \ ale#Escape('flake8') . ' --version',
+ \ ale#path#CdString(ale#python#FindProjectRootIni(bufnr('')))
+ \ . ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
+ \]
+
+Execute(The option for changing directory to file dir should work):
+ let g:ale_python_flake8_change_directory = 'file'
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/namespace_package_tox/namespace/foo/bar.py')
+
+ AssertLinter 'flake8', [
+ \ ale#Escape('flake8') . ' --version',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
+ \]
+
+ let g:ale_python_flake8_change_directory = 1
+
+ AssertLinter 'flake8', [
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('flake8') . ' --format=default --stdin-display-name %s -',
+ \]
+
Execute(The flake8 command callback should let you set options):
let g:ale_python_flake8_options = '--some-option'
@@ -163,5 +202,5 @@ Execute(Pipenv is detected when python_flake8_auto_pipenv is set):
call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(ale#python#FindProjectRootIni(bufnr('')))
\ . ale#Escape('pipenv') . ' run flake8 --format=default --stdin-display-name %s -'
diff --git a/test/command_callback/test_gobuild_command_callback.vader b/test/command_callback/test_gobuild_command_callback.vader
index fdf23866..063f3f2f 100644
--- a/test/command_callback/test_gobuild_command_callback.vader
+++ b/test/command_callback/test_gobuild_command_callback.vader
@@ -11,14 +11,14 @@ After:
Execute(The default commands should be correct):
AssertLinter 'go',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'go test -c -o /dev/null ./'
Execute(Go environment variables should be supported):
let b:ale_go_go111module = 'on'
AssertLinter 'go',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Env('GO111MODULE', 'on')
\ . 'go test -c -o /dev/null ./'
@@ -28,7 +28,7 @@ Execute(Extra options should be supported):
let g:ale_go_gobuild_options = '--foo-bar'
AssertLinter 'go',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'go test --foo-bar -c -o /dev/null ./'
let g:ale_go_gobuild_options = ''
@@ -37,5 +37,5 @@ Execute(The executable should be configurable):
let g:ale_go_go_executable = 'foobar'
AssertLinter 'foobar',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'foobar test -c -o /dev/null ./'
diff --git a/test/command_callback/test_gofmt_command_callback.vader b/test/command_callback/test_gofmt_command_callback.vader
index 4da1f6c8..88b2e6b0 100644
--- a/test/command_callback/test_gofmt_command_callback.vader
+++ b/test/command_callback/test_gofmt_command_callback.vader
@@ -1,5 +1,8 @@
Before:
Save g:ale_go_go111module
+ Save b:ale_go_go111module
+
+ let b:ale_go_go111module = ''
call ale#assert#SetUpLinterTest('go', 'gofmt')
call ale#test#SetFilename('../go_files/testfile2.go')
diff --git a/test/command_callback/test_golangci_lint_command_callback.vader b/test/command_callback/test_golangci_lint_command_callback.vader
index 7f1e2ac4..37fb1f7d 100644
--- a/test/command_callback/test_golangci_lint_command_callback.vader
+++ b/test/command_callback/test_golangci_lint_command_callback.vader
@@ -13,7 +13,7 @@ After:
Execute(The golangci-lint defaults should be correct):
AssertLinter 'golangci-lint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('golangci-lint')
\ . ' run ' . ale#Escape(expand('%' . ':t'))
\ . ' --enable-all'
@@ -22,7 +22,7 @@ Execute(The golangci-lint callback should use a configured executable):
let b:ale_go_golangci_lint_executable = 'something else'
AssertLinter 'something else',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('something else')
\ . ' run ' . ale#Escape(expand('%' . ':t'))
\ . ' --enable-all'
@@ -31,7 +31,7 @@ Execute(The golangci-lint callback should use configured options):
let b:ale_go_golangci_lint_options = '--foobar'
AssertLinter 'golangci-lint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('golangci-lint')
\ . ' run ' . ale#Escape(expand('%' . ':t'))
\ . ' --foobar'
@@ -40,7 +40,7 @@ Execute(The golangci-lint callback should support environment variables):
let b:ale_go_go111module = 'on'
AssertLinter 'golangci-lint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Env('GO111MODULE', 'on')
\ . ale#Escape('golangci-lint')
\ . ' run ' . ale#Escape(expand('%' . ':t'))
@@ -50,5 +50,5 @@ Execute(The golangci-lint `lint_package` option should use the correct command):
let b:ale_go_golangci_lint_package = 1
AssertLinter 'golangci-lint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('golangci-lint') . ' run --enable-all'
diff --git a/test/command_callback/test_gometalinter_command_callback.vader b/test/command_callback/test_gometalinter_command_callback.vader
index d922efc6..567997d8 100644
--- a/test/command_callback/test_gometalinter_command_callback.vader
+++ b/test/command_callback/test_gometalinter_command_callback.vader
@@ -13,7 +13,7 @@ After:
Execute(The gometalinter defaults should be correct):
AssertLinter 'gometalinter',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('gometalinter')
\ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t')))
\ . ' .'
@@ -22,7 +22,7 @@ Execute(The gometalinter callback should use a configured executable):
let b:ale_go_gometalinter_executable = 'something else'
AssertLinter 'something else',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('something else')
\ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t')))
\ . ' .'
@@ -31,7 +31,7 @@ Execute(The gometalinter callback should use configured options):
let b:ale_go_gometalinter_options = '--foobar'
AssertLinter 'gometalinter',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('gometalinter')
\ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t')))
\ . ' --foobar' . ' .'
@@ -40,7 +40,7 @@ Execute(The gometalinter should use configured environment variables):
let b:ale_go_go111module = 'off'
AssertLinter 'gometalinter',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Env('GO111MODULE', 'off')
\ . ale#Escape('gometalinter')
\ . ' --include=' . ale#Escape(ale#util#EscapePCRE(expand('%' . ':t')))
@@ -50,5 +50,5 @@ Execute(The gometalinter `lint_package` option should use the correct command):
let b:ale_go_gometalinter_lint_package = 1
AssertLinter 'gometalinter',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('gometalinter') . ' .'
diff --git a/test/command_callback/test_gosimple_command_callback.vader b/test/command_callback/test_gosimple_command_callback.vader
index ee89eed8..b006f783 100644
--- a/test/command_callback/test_gosimple_command_callback.vader
+++ b/test/command_callback/test_gosimple_command_callback.vader
@@ -11,11 +11,12 @@ After:
Execute(The default gosimple command should be correct):
AssertLinter 'gosimple',
- \ ale#path#CdString(expand('%:p:h')) . ' gosimple .'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ' gosimple .'
Execute(The gosimple command should support Go environment variables):
let b:ale_go_go111module = 'on'
AssertLinter 'gosimple',
- \ ale#path#CdString(expand('%:p:h')) . ' '
- \ . ale#Env('GO111MODULE', 'on') . 'gosimple .'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ' ' . ale#Env('GO111MODULE', 'on') . 'gosimple .'
diff --git a/test/command_callback/test_gotype_command_callback.vader b/test/command_callback/test_gotype_command_callback.vader
index 1334fcff..204197d9 100644
--- a/test/command_callback/test_gotype_command_callback.vader
+++ b/test/command_callback/test_gotype_command_callback.vader
@@ -11,7 +11,8 @@ After:
Execute(The default gotype command should be correct):
AssertLinter 'gotype',
- \ ale#path#CdString(expand('%:p:h')) . ' gotype -e .'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ' gotype -e .'
Execute(The gotype callback should ignore test files):
call ale#test#SetFilename('bla_test.go')
@@ -22,6 +23,6 @@ Execute(The gotype callback should support Go environment variables):
let b:ale_go_go111module = 'on'
AssertLinter 'gotype',
- \ ale#path#CdString(expand('%:p:h')) . ' '
- \ . ale#Env('GO111MODULE', 'on')
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ' ' . ale#Env('GO111MODULE', 'on')
\ . 'gotype -e .'
diff --git a/test/command_callback/test_govet_command_callback.vader b/test/command_callback/test_govet_command_callback.vader
index 59022180..0e1ea092 100644
--- a/test/command_callback/test_govet_command_callback.vader
+++ b/test/command_callback/test_govet_command_callback.vader
@@ -13,22 +13,22 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The default command should be correct):
- AssertLinter 'go', ale#path#CdString(expand('%:p:h')) . ' go vet .'
+ AssertLinter 'go', ale#path#BufferCdString(bufnr('')) . ' go vet .'
Execute(Extra options should be supported):
let g:ale_go_govet_options = '--foo-bar'
- AssertLinter 'go', ale#path#CdString(expand('%:p:h')) . ' go vet --foo-bar .'
+ AssertLinter 'go', ale#path#BufferCdString(bufnr('')) . ' go vet --foo-bar .'
Execute(The executable should be configurable):
let g:ale_go_go_executable = 'foobar'
- AssertLinter 'foobar', ale#path#CdString(expand('%:p:h')) . ' foobar vet .'
+ AssertLinter 'foobar', ale#path#BufferCdString(bufnr('')) . ' foobar vet .'
Execute(Go environment variables should be supported):
let b:ale_go_go111module = 'on'
AssertLinter 'go',
- \ ale#path#CdString(expand('%:p:h')) . ' '
+ \ ale#path#BufferCdString(bufnr('')) . ' '
\ . ale#Env('GO111MODULE', 'on')
\ . 'go vet .'
diff --git a/test/command_callback/test_graphql_gqlint_command_callbacks.vader b/test/command_callback/test_graphql_gqlint_command_callbacks.vader
index 0f4e9770..e8ed0e5d 100644
--- a/test/command_callback/test_graphql_gqlint_command_callbacks.vader
+++ b/test/command_callback/test_graphql_gqlint_command_callbacks.vader
@@ -6,6 +6,6 @@ After:
Execute(The linter should run from the directory of the file in the buffer):
AssertLinter 'gqlint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'gqlint --reporter=simple'
\ . ' %t'
diff --git a/test/command_callback/test_javac_command_callback.vader b/test/command_callback/test_javac_command_callback.vader
index d2eebf7a..ac898e5f 100644
--- a/test/command_callback/test_javac_command_callback.vader
+++ b/test/command_callback/test_javac_command_callback.vader
@@ -3,7 +3,7 @@ Before:
call ale#test#SetFilename('dummy.java')
let g:cp_sep = has('unix') ? ':' : ';'
- let g:prefix = ale#path#CdString(expand('%:p:h'))
+ let g:prefix = ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('javac') . ' -Xlint'
function! GetCommand(previous_output) abort
@@ -51,7 +51,7 @@ Execute(The executable should be configurable):
let g:ale_java_javac_executable = 'foobar'
AssertLinter 'foobar',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('foobar') . ' -Xlint'
\ . ' -d ' . ale#Escape('TEMP_DIR') . ' %t'
@@ -197,7 +197,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones)
let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {})
AssertEqual
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'),
\ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/'),
@@ -210,7 +211,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones)
let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {})
AssertEqual
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'),
\ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/'),
@@ -223,7 +225,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones)
let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {})
AssertEqual
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'),
\ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/')
@@ -238,7 +241,8 @@ Execute(The javac callback should combine discovered sourcepath and manual ones)
let b:command = ale_linters#java#javac#GetCommand(bufnr(''), [], {})
AssertEqual
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'),
\ ale#path#Simplify(g:dir . '/java_paths/build/gen/main/'),
@@ -253,7 +257,8 @@ Execute(The javac callback should detect source directories):
call ale#engine#InitBufferInfo(bufnr(''))
AssertLinter 'javac',
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/')
\ )
@@ -272,7 +277,8 @@ Execute(The javac callback should combine detected source directories and classp
\], {})
AssertEqual
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -cp ' . ale#Escape(join(['/foo/bar.jar', '/xyz/abc.jar'], g:cp_sep))
\ . ' -sourcepath ' . ale#Escape(
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/')
@@ -294,7 +300,8 @@ Execute(The javac callback should include src/test/java for test paths):
call ale#engine#InitBufferInfo(bufnr(''))
AssertLinter 'javac',
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths/src/main/java/'),
\ ale#path#Simplify(g:dir . '/java_paths/src/test/java/'),
@@ -307,7 +314,8 @@ Execute(The javac callback should include src/main/jaxb when available):
call ale#engine#InitBufferInfo(bufnr(''))
AssertLinter 'javac',
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/java/'),
\ ale#path#Simplify(g:dir . '/java_paths_with_jaxb/src/main/jaxb/'),
@@ -320,7 +328,8 @@ Execute(The javac callback should add -sourcepath even if src/java/main doesn't
call ale#engine#InitBufferInfo(bufnr(''))
AssertLinter 'javac',
- \ ale#path#CdString(expand('%:p:h')) . ale#Escape('javac') . ' -Xlint'
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape('javac') . ' -Xlint'
\ . ' -sourcepath ' . ale#Escape(join([
\ ale#path#Simplify(g:dir . '/java_paths_no_main/src/test/java/'),
\ ], g:cp_sep))
diff --git a/test/command_callback/test_lintr_command_callback.vader b/test/command_callback/test_lintr_command_callback.vader
index 187d3875..ac4b419b 100644
--- a/test/command_callback/test_lintr_command_callback.vader
+++ b/test/command_callback/test_lintr_command_callback.vader
@@ -6,7 +6,7 @@ After:
Execute(The default lintr command should be correct):
AssertLinter 'Rscript',
- \ ale#path#CdString(getcwd())
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'Rscript --vanilla -e '
\ . ale#Escape('suppressPackageStartupMessages(library(lintr));'
\ . 'lint(cache = FALSE, commandArgs(TRUE), '
@@ -17,7 +17,7 @@ Execute(The lintr options should be configurable):
let b:ale_r_lintr_options = 'with_defaults(object_usage_linter = NULL)'
AssertLinter 'Rscript',
- \ ale#path#CdString(getcwd())
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'Rscript --vanilla -e '
\ . ale#Escape('suppressPackageStartupMessages(library(lintr));'
\ . 'lint(cache = FALSE, commandArgs(TRUE), '
@@ -28,7 +28,7 @@ Execute(If the lint_package flag is set, lintr::lint_package should be called):
let b:ale_r_lintr_lint_package = 1
AssertLinter 'Rscript',
- \ ale#path#CdString(getcwd())
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'Rscript --vanilla -e '
\ . ale#Escape('suppressPackageStartupMessages(library(lintr));'
\ . 'lint_package(cache = FALSE, '
diff --git a/test/command_callback/test_markdown_markdownlint_command_callback.vader b/test/command_callback/test_markdown_markdownlint_command_callback.vader
new file mode 100644
index 00000000..12766cfd
--- /dev/null
+++ b/test/command_callback/test_markdown_markdownlint_command_callback.vader
@@ -0,0 +1,13 @@
+Before:
+ call ale#assert#SetUpLinterTest('markdown', 'markdownlint')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'markdownlint', ale#Escape('markdownlint') . ' %s'
+
+Execute(The options should be configurable):
+ let g:ale_markdown_markdownlint_options = '--config ~/custom/.markdownlintrc'
+
+ AssertLinter 'markdownlint', ale#Escape('markdownlint') . ' --config ~/custom/.markdownlintrc %s'
diff --git a/test/command_callback/test_mypy_command_callback.vader b/test/command_callback/test_mypy_command_callback.vader
index afa9f9af..b9b6ae70 100644
--- a/test/command_callback/test_mypy_command_callback.vader
+++ b/test/command_callback/test_mypy_command_callback.vader
@@ -75,14 +75,14 @@ Execute(Setting executable to 'pipenv' appends 'run mypy'):
let g:ale_python_mypy_executable = 'path/to/pipenv'
AssertLinter 'path/to/pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('path/to/pipenv') . ' run mypy'
\ . ' --show-column-numbers --shadow-file %s %t %s'
Execute(Pipenv is detected when python_mypy_auto_pipenv is set):
let g:ale_python_mypy_auto_pipenv = 1
- call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+ call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pipenv') . ' run mypy --show-column-numbers --shadow-file %s %t %s'
diff --git a/test/command_callback/test_nasm_nasm_command_callbacks.vader b/test/command_callback/test_nasm_nasm_command_callbacks.vader
index 8e077306..2bfe2b0d 100644
--- a/test/command_callback/test_nasm_nasm_command_callbacks.vader
+++ b/test/command_callback/test_nasm_nasm_command_callbacks.vader
@@ -2,9 +2,9 @@ Before:
call ale#assert#SetUpLinterTest('nasm', 'nasm')
let b:command_tail =
- \ ' -X gnu -I ' . ale#Escape(getcwd() . (has('win32') ? '\' : '/')) . ' %s -o ' . (has('win32') ? 'NUL' : '/dev/null')
+ \ ' -X gnu -I %s:h' . (has('win32') ? '\' : '/') . ' %s -o ' . (has('win32') ? 'NUL' : '/dev/null')
let b:command_tail_opt =
- \ ' -X gnu -I ' . ale#Escape(getcwd() . (has('win32') ? '\' : '/')) . ' -w+orphan-labels %s -o ' . (has('win32') ? 'NUL' : '/dev/null')
+ \ ' -X gnu -I %s:h' . (has('win32') ? '\' : '/') . ' -w+orphan-labels %s -o ' . (has('win32') ? 'NUL' : '/dev/null')
After:
unlet! b:command_tail
@@ -23,7 +23,8 @@ Execute(The options should be configurable):
let b:ale_nasm_nasm_options = '-w-macro-params'
AssertLinter 'nasm', ale#Escape('nasm')
- \ . ' -X gnu -I ' . ale#Escape(getcwd() . (has('win32') ? '\' : '/')) . ' -w-macro-params %s -o ' . (has('win32') ? 'NUL' : '/dev/null')
+ \ . ' -X gnu -I %s:h' . (has('win32') ? '\' : '/')
+ \ . ' -w-macro-params %s -o ' . (has('win32') ? 'NUL' : '/dev/null')
Execute(The options should be used in command):
let b:ale_nasm_nasm_options = '-w+orphan-labels'
diff --git a/test/command_callback/test_psalm_command_callbacks.vader b/test/command_callback/test_psalm_command_callbacks.vader
index 70b5af95..d32780e6 100644
--- a/test/command_callback/test_psalm_command_callbacks.vader
+++ b/test/command_callback/test_psalm_command_callbacks.vader
@@ -2,6 +2,9 @@ Before:
call ale#assert#SetUpLinterTest('php', 'psalm')
After:
+ unlet! g:i
+ unlet! g:matched
+
if isdirectory(g:dir . '/.git')
call delete(g:dir . '/.git', 'd')
endif
@@ -22,19 +25,36 @@ Execute(Vendor executables should be detected):
\ . '/psalm-project/vendor/bin/psalm'
\ )) . ' --language-server'
+ let g:ale_php_psalm_use_global = 1
+
+ AssertLinter 'psalm',
+ \ ale#Escape('psalm') . ' --language-server'
+
Execute(User provided options should be used):
- let g:ale_psalm_langserver_options = '--my-user-provided-option my-value'
+ let g:ale_php_psalm_options = '--my-user-provided-option my-value'
AssertLinter 'psalm',
\ ale#Escape('psalm')
\ . ' --language-server --my-user-provided-option my-value'
-
Execute(The project path should be correct for .git directories):
call ale#test#SetFilename('psalm-project/test.php')
+ let g:matched = 0
- if !isdirectory(g:dir . '/.git')
- call mkdir(g:dir . '/.git')
+ for g:i in range(4)
+ if !isdirectory(g:dir . '/.git')
+ call mkdir(g:dir . '/.git')
+ endif
+
+ try
+ AssertLSPProject g:dir
+ catch /.+/
+ endtry
+
+ let g:matched = 1
+ break
+ endfor
+
+ if !g:matched
+ AssertLSPProject g:dir
endif
-
- AssertLSPProject g:dir
diff --git a/test/command_callback/test_pydocstyle_command_callback.vader b/test/command_callback/test_pydocstyle_command_callback.vader
index 7e0df9ca..511443a6 100644
--- a/test/command_callback/test_pydocstyle_command_callback.vader
+++ b/test/command_callback/test_pydocstyle_command_callback.vader
@@ -1,5 +1,6 @@
Before:
call ale#assert#SetUpLinterTest('python', 'pydocstyle')
+ call ale#test#SetFilename('test.py')
After:
call ale#assert#TearDownLinterTest()
@@ -7,33 +8,33 @@ After:
Execute(The pydocstyle command callback should return default string):
AssertLinter 'pydocstyle',
\ ale#path#BufferCdString(bufnr(''))
- \ . ale#Escape('pydocstyle') . ' ' . ale#Escape('dummy.txt')
+ \ . ale#Escape('pydocstyle') . ' %s:t'
Execute(The pydocstyle command callback should allow options):
let g:ale_python_pydocstyle_options = '--verbose'
AssertLinter 'pydocstyle',
\ ale#path#BufferCdString(bufnr(''))
- \ . ale#Escape('pydocstyle') . ' --verbose ' . ale#Escape('dummy.txt')
+ \ . ale#Escape('pydocstyle') . ' --verbose %s:t'
Execute(The pydocstyle executable should be configurable):
let g:ale_python_pydocstyle_executable = '~/.local/bin/pydocstyle'
AssertLinter '~/.local/bin/pydocstyle',
\ ale#path#BufferCdString(bufnr(''))
- \ . ale#Escape('~/.local/bin/pydocstyle') . ' ' . ale#Escape('dummy.txt')
+ \ . ale#Escape('~/.local/bin/pydocstyle') . ' %s:t'
Execute(Setting executable to 'pipenv' appends 'run pydocstyle'):
let g:ale_python_pydocstyle_executable = 'path/to/pipenv'
AssertLinter 'path/to/pipenv',
\ ale#path#BufferCdString(bufnr(''))
- \ . ale#Escape('path/to/pipenv') . ' run pydocstyle ' . ale#Escape('dummy.txt')
+ \ . ale#Escape('path/to/pipenv') . ' run pydocstyle %s:t'
Execute(Pipenv is detected when python_pydocstyle_auto_pipenv is set):
let g:ale_python_pydocstyle_auto_pipenv = 1
- call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+ call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
\ ale#path#BufferCdString(bufnr(''))
- \ . ale#Escape('pipenv') . ' run pydocstyle ' . ale#Escape('whatever.py')
+ \ . ale#Escape('pipenv') . ' run pydocstyle %s:t'
diff --git a/test/command_callback/test_pylama_command_callback.vader b/test/command_callback/test_pylama_command_callback.vader
index 417cb5c9..0aea9a93 100644
--- a/test/command_callback/test_pylama_command_callback.vader
+++ b/test/command_callback/test_pylama_command_callback.vader
@@ -14,7 +14,7 @@ After:
Execute(The pylama command callback should return a default):
AssertLinter 'pylama',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pylama') . b:command_tail
Execute(The option for disabling changing directories should work):
@@ -26,14 +26,14 @@ Execute(The pylama executable should be configurable, and escaped properly):
let g:ale_python_pylama_executable = 'executable with spaces'
AssertLinter 'executable with spaces',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('executable with spaces') . b:command_tail
Execute(The pylama command callback should let you set options):
let g:ale_python_pylama_options = '--some-option'
AssertLinter 'pylama',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pylama') . ' --some-option' . b:command_tail
Execute(The pylama command callback should switch directories to the detected project root):
@@ -73,13 +73,13 @@ Execute(Setting executable to 'pipenv' appends 'run pylama'):
let g:ale_python_pylama_executable = 'path/to/pipenv'
AssertLinter 'path/to/pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('path/to/pipenv') . ' run pylama' . b:command_tail
Execute(Pipenv is detected when python_pylama_auto_pipenv is set):
let g:ale_python_pylama_auto_pipenv = 1
- call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+ call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pipenv') . ' run pylama' . b:command_tail
diff --git a/test/command_callback/test_pylint_command_callback.vader b/test/command_callback/test_pylint_command_callback.vader
index c41c8398..755dd292 100644
--- a/test/command_callback/test_pylint_command_callback.vader
+++ b/test/command_callback/test_pylint_command_callback.vader
@@ -1,4 +1,8 @@
Before:
+ Save g:ale_python_auto_pipenv
+
+ let g:ale_python_auto_pipenv = 0
+
call ale#assert#SetUpLinterTest('python', 'pylint')
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
@@ -13,7 +17,7 @@ After:
Execute(The pylint callbacks should return the correct default values):
AssertLinter 'pylint',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pylint') . ' ' . b:command_tail
Execute(The option for disabling changing directories should work):
@@ -25,14 +29,14 @@ Execute(The pylint executable should be configurable, and escaped properly):
let g:ale_python_pylint_executable = 'executable with spaces'
AssertLinter 'executable with spaces',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('executable with spaces') . ' ' . b:command_tail
Execute(The pylint command callback should let you set options):
let g:ale_python_pylint_options = '--some-option'
AssertLinter 'pylint',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pylint') . ' --some-option' . b:command_tail
Execute(The pylint callbacks shouldn't detect virtualenv directories where they don't exist):
@@ -65,15 +69,15 @@ Execute(Setting executable to 'pipenv' appends 'run pylint'):
let g:ale_python_pylint_executable = 'path/to/pipenv'
AssertLinter 'path/to/pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('path/to/pipenv') . ' run pylint'
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n %s'
Execute(Pipenv is detected when python_pylint_auto_pipenv is set):
let g:ale_python_pylint_auto_pipenv = 1
- call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+ call ale#test#SetFilename('../python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('pipenv') . ' run pylint'
\ . ' --output-format text --msg-template="{path}:{line}:{column}: {msg_id} ({symbol}) {msg}" --reports n %s'
diff --git a/test/command_callback/test_pyrex_cython_command_callback.vader b/test/command_callback/test_pyrex_cython_command_callback.vader
index b9020f11..af86366a 100644
--- a/test/command_callback/test_pyrex_cython_command_callback.vader
+++ b/test/command_callback/test_pyrex_cython_command_callback.vader
@@ -6,8 +6,8 @@ After:
Execute(The default cython command should be correct):
AssertLinter 'cython', ale#Escape('cython')
- \ . ' --working ' . ale#Escape(g:dir)
- \ . ' --include-dir ' . ale#Escape(g:dir)
+ \ . ' --working %s:h'
+ \ . ' --include-dir %s:h'
\ . ' --warning-extra'
\ . ' --output-file ' . g:ale#util#nul_file . ' %t'
@@ -15,8 +15,8 @@ Execute(The cython executable should be configurable):
let b:ale_pyrex_cython_executable = 'cython_foobar'
AssertLinter 'cython_foobar', ale#Escape('cython_foobar')
- \ . ' --working ' . ale#Escape(g:dir)
- \ . ' --include-dir ' . ale#Escape(g:dir)
+ \ . ' --working %s:h'
+ \ . ' --include-dir %s:h'
\ . ' --warning-extra'
\ . ' --output-file ' . g:ale#util#nul_file . ' %t'
@@ -24,7 +24,7 @@ Execute(Additional cython options should be configurable):
let b:ale_pyrex_cython_options = '--foobar'
AssertLinter 'cython', ale#Escape('cython')
- \ . ' --working ' . ale#Escape(g:dir)
- \ . ' --include-dir ' . ale#Escape(g:dir)
+ \ . ' --working %s:h'
+ \ . ' --include-dir %s:h'
\ . ' --foobar'
\ . ' --output-file ' . g:ale#util#nul_file . ' %t'
diff --git a/test/command_callback/test_rubocop_command_callback.vader b/test/command_callback/test_rubocop_command_callback.vader
index 7f42a8c0..e7cc32e8 100644
--- a/test/command_callback/test_rubocop_command_callback.vader
+++ b/test/command_callback/test_rubocop_command_callback.vader
@@ -10,20 +10,17 @@ After:
Execute(Executable should default to rubocop):
AssertLinter 'rubocop', ale#Escape('rubocop')
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+ \ . ' --format json --force-exclusion --stdin %s'
Execute(Should be able to set a custom executable):
let g:ale_ruby_rubocop_executable = 'bin/rubocop'
AssertLinter 'bin/rubocop' , ale#Escape('bin/rubocop')
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+ \ . ' --format json --force-exclusion --stdin %s'
Execute(Setting bundle appends 'exec rubocop'):
let g:ale_ruby_rubocop_executable = 'path to/bundle'
AssertLinter 'path to/bundle', ale#Escape('path to/bundle')
\ . ' exec rubocop'
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+ \ . ' --format json --force-exclusion --stdin %s'
diff --git a/test/command_callback/test_ruumba_command_callback.vader b/test/command_callback/test_ruumba_command_callback.vader
index 244b264a..9fa48903 100644
--- a/test/command_callback/test_ruumba_command_callback.vader
+++ b/test/command_callback/test_ruumba_command_callback.vader
@@ -10,20 +10,17 @@ After:
Execute(Executable should default to ruumba):
AssertLinter 'ruumba', ale#Escape('ruumba')
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.html.erb'))
+ \ . ' --format json --force-exclusion --stdin %s'
Execute(Should be able to set a custom executable):
let g:ale_eruby_ruumba_executable = 'bin/ruumba'
AssertLinter 'bin/ruumba' , ale#Escape('bin/ruumba')
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.html.erb'))
+ \ . ' --format json --force-exclusion --stdin %s'
Execute(Setting bundle appends 'exec ruumba'):
let g:ale_eruby_ruumba_executable = 'path to/bundle'
AssertLinter 'path to/bundle', ale#Escape('path to/bundle')
\ . ' exec ruumba'
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.html.erb'))
+ \ . ' --format json --force-exclusion --stdin %s'
diff --git a/test/command_callback/test_shellcheck_command_callback.vader b/test/command_callback/test_shellcheck_command_callback.vader
index 1d5b056b..9fb5303a 100644
--- a/test/command_callback/test_shellcheck_command_callback.vader
+++ b/test/command_callback/test_shellcheck_command_callback.vader
@@ -2,7 +2,7 @@ Before:
call ale#assert#SetUpLinterTest('sh', 'shellcheck')
call ale#test#SetFilename('test.sh')
- let b:prefix = ale#path#CdString(ale#path#Simplify(g:dir))
+ let b:prefix = ale#path#BufferCdString(bufnr(''))
let b:suffix = ' -f gcc -'
After:
diff --git a/test/command_callback/test_sqllint_command_callback.vader b/test/command_callback/test_sqllint_command_callback.vader
new file mode 100644
index 00000000..eea9b4e0
--- /dev/null
+++ b/test/command_callback/test_sqllint_command_callback.vader
@@ -0,0 +1,12 @@
+Before:
+ " Load the linter and set up a series of commands, reset linter variables,
+ " clear caches, etc.
+ "
+ " Vader's 'Save' command will be called here for linter variables.
+ call ale#assert#SetUpLinterTest('sql', 'sqllint')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct):
+ AssertLinter 'sql-lint', ['sql-lint']
diff --git a/test/command_callback/test_standardrb_command_callback.vader b/test/command_callback/test_standardrb_command_callback.vader
index 7bc1c976..108dd870 100644
--- a/test/command_callback/test_standardrb_command_callback.vader
+++ b/test/command_callback/test_standardrb_command_callback.vader
@@ -10,20 +10,17 @@ After:
Execute(Executable should default to standardrb):
AssertLinter 'standardrb', ale#Escape('standardrb')
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+ \ . ' --format json --force-exclusion --stdin %s'
Execute(Should be able to set a custom executable):
let g:ale_ruby_standardrb_executable = 'bin/standardrb'
AssertLinter 'bin/standardrb' , ale#Escape('bin/standardrb')
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+ \ . ' --format json --force-exclusion --stdin %s'
Execute(Setting bundle appends 'exec standardrb'):
let g:ale_ruby_standardrb_executable = 'path to/bundle'
AssertLinter 'path to/bundle', ale#Escape('path to/bundle')
\ . ' exec standardrb'
- \ . ' --format json --force-exclusion --stdin '
- \ . ale#Escape(ale#path#Simplify(g:dir . '/dummy.rb'))
+ \ . ' --format json --force-exclusion --stdin %s'
diff --git a/test/command_callback/test_staticcheck_command_callback.vader b/test/command_callback/test_staticcheck_command_callback.vader
index ae0d3584..871a5510 100644
--- a/test/command_callback/test_staticcheck_command_callback.vader
+++ b/test/command_callback/test_staticcheck_command_callback.vader
@@ -11,7 +11,7 @@ After:
Execute(The staticcheck callback should return the right defaults):
AssertLinter 'staticcheck',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'staticcheck '
\ . ale#Escape(expand('%' . ':t'))
@@ -19,7 +19,7 @@ Execute(The staticcheck callback should use configured options):
let b:ale_go_staticcheck_options = '-test'
AssertLinter 'staticcheck',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . 'staticcheck '
\ . '-test ' . ale#Escape(expand('%' . ':t'))
@@ -27,13 +27,14 @@ Execute(The staticcheck `lint_package` option should use the correct command):
let b:ale_go_staticcheck_lint_package = 1
AssertLinter 'staticcheck',
- \ ale#path#CdString(expand('%:p:h')) . 'staticcheck .',
+ \ ale#path#BufferCdString(bufnr(''))
+ \ . 'staticcheck .',
Execute(The staticcheck callback should use the `GO111MODULE` option if set):
let b:ale_go_go111module = 'off'
AssertLinter 'staticcheck',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Env('GO111MODULE', 'off')
\ . 'staticcheck '
\ . ale#Escape(expand('%' . ':t'))
@@ -42,6 +43,6 @@ Execute(The staticcheck callback should use the `GO111MODULE` option if set):
let b:ale_go_staticcheck_lint_package = 1
AssertLinter 'staticcheck',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Env('GO111MODULE', 'off')
\ . 'staticcheck .'
diff --git a/test/command_callback/test_swift_swiftformat_command_callbacks.vader b/test/command_callback/test_swift_swiftformat_command_callbacks.vader
new file mode 100644
index 00000000..7be20bf7
--- /dev/null
+++ b/test/command_callback/test_swift_swiftformat_command_callbacks.vader
@@ -0,0 +1,25 @@
+Before:
+ call ale#assert#SetUpLinterTest('swift', 'swiftformat')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(Should use default command when not in a swift package):
+ call ale#test#SetFilename('../swift-test-files/non-swift-package-project/src/folder/dummy.swift')
+
+ AssertLinter 'swift-format',
+ \ ale#Escape('swift-format') . ' --mode lint %t'
+
+Execute(Should use swift run when in a swift package):
+ call ale#test#SetFilename('../swift-test-files/swift-package-project/src/folder/dummy.swift')
+
+ AssertLinter 'swift',
+ \ ale#Escape('swift') . ' run swift-format --mode lint %t'
+
+Execute(Should let users configure a global executable and override local paths):
+ call ale#test#SetFilename('../swift-test-files/swift-package-project/src/folder/dummy.swift')
+
+ let g:ale_swift_swiftformat_executable = '/path/to/custom/swift-format'
+
+ AssertLinter '/path/to/custom/swift-format',
+ \ ale#Escape('/path/to/custom/swift-format') . ' --mode lint %t'
diff --git a/test/command_callback/test_tslint_command_callback.vader b/test/command_callback/test_tslint_command_callback.vader
index 229ccc96..cc5d2666 100644
--- a/test/command_callback/test_tslint_command_callback.vader
+++ b/test/command_callback/test_tslint_command_callback.vader
@@ -7,14 +7,14 @@ After:
Execute(The default tslint command should be correct):
AssertLinter 'tslint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('tslint') . ' --format json %t'
Execute(The rules directory option should be included if set):
let b:ale_typescript_tslint_rules_dir = '/foo/bar'
AssertLinter 'tslint',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('tslint') . ' --format json'
\ . ' -r ' . ale#Escape('/foo/bar')
\ . ' %t'
@@ -23,5 +23,5 @@ Execute(The executable should be configurable and escaped):
let b:ale_typescript_tslint_executable = 'foo bar'
AssertLinter 'foo bar',
- \ ale#path#CdString(expand('%:p:h'))
+ \ ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('foo bar') . ' --format json %t'
diff --git a/test/command_callback/test_vint_command_callback.vader b/test/command_callback/test_vint_command_callback.vader
index e0051f26..4ce277e8 100644
--- a/test/command_callback/test_vint_command_callback.vader
+++ b/test/command_callback/test_vint_command_callback.vader
@@ -1,7 +1,7 @@
Before:
call ale#assert#SetUpLinterTest('vim', 'vint')
let b:command_tail = (has('nvim') ? ' --enable-neovim' : '')
- \ . ' -f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})" %t'
+ \ . ' -f "{file_path}:{line_number}:{column_number}: {severity}: {policy_name} - {description} (see {reference})" %t'
After:
unlet! b:bin_dir
diff --git a/test/command_callback/test_vulture_command_callback.vader b/test/command_callback/test_vulture_command_callback.vader
index d6c866b9..bacf8f12 100644
--- a/test/command_callback/test_vulture_command_callback.vader
+++ b/test/command_callback/test_vulture_command_callback.vader
@@ -12,7 +12,7 @@ After:
Execute(The vulture command callback should lint file directory by default):
AssertLinter 'vulture',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('vulture') . ' .'
Execute(The vulture command callback should lint project root, when present):
@@ -31,14 +31,14 @@ Execute(The vulture executable should be configurable, and escaped properly):
let g:ale_python_vulture_executable = 'executable with spaces'
AssertLinter 'executable with spaces',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('executable with spaces') . ' .'
Execute(The vulture command callback should let you set options):
let g:ale_python_vulture_options = '--some-option'
AssertLinter 'vulture',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('vulture') . ' --some-option .'
Execute(The vulture command callback should detect virtualenv directories and switch to the project root):
@@ -64,5 +64,5 @@ Execute(Setting executable to 'pipenv' appends 'run vulture'):
let g:ale_python_vulture_executable = 'path/to/pipenv'
AssertLinter 'path/to/pipenv',
- \ ale#path#BufferCdString(bufnr(''))
+ \ ale#path#CdString(expand('#' . bufnr('') . ':p:h'))
\ . ale#Escape('path/to/pipenv') . ' run vulture' . ' .'
diff --git a/test/completion/test_ale_import_command.vader b/test/completion/test_ale_import_command.vader
new file mode 100644
index 00000000..2ba9b8d7
--- /dev/null
+++ b/test/completion/test_ale_import_command.vader
@@ -0,0 +1,562 @@
+Before:
+ Save g:ale_enabled
+ Save b:ale_enabled
+ Save g:ale_lint_on_text_changed
+ Save g:ale_completion_enabled
+ Save g:ale_completion_autoimport
+ Save g:ale_completion_max_suggestions
+ Save g:ale_linters
+ Save b:ale_linters
+
+ let g:ale_enabled = 0
+ let b:ale_enabled = 0
+ let g:ale_lint_on_text_changed = 'always'
+ let g:ale_completion_enabled = 0
+ let g:ale_completion_autoimport = 0
+ let g:ale_completion_max_suggestions = 50
+ let g:ale_linters = {'typescript': ['tsserver'], 'python': ['pyre']}
+ unlet! b:ale_linters
+
+ let g:server_started_value = 1
+ let g:request_id = 0
+ let g:LastCallback = v:null
+ let g:LastHandleCallback = v:null
+ let g:sent_message_list = []
+ let g:code_action_list = []
+ let g:execute_list = []
+ let g:ale_queue_call_list = []
+
+ runtime autoload/ale.vim
+ runtime autoload/ale/util.vim
+ runtime autoload/ale/code_action.vim
+ runtime autoload/ale/lsp.vim
+ runtime autoload/ale/lsp_linter.vim
+
+ function! ale#util#Execute(expr) abort
+ call add(g:execute_list, a:expr)
+ endfunction
+
+ function! ale#Queue(...) abort
+ call add(g:ale_queue_call_list, a:000)
+ endfunction
+
+ function! ale#lsp#RegisterCallback(id, Callback) abort
+ let g:LastHandleCallback = a:Callback
+ endfunction
+
+ function! ale#lsp#NotifyForChanges(id, buffer) abort
+ endfunction
+
+ function! ale#lsp#HasCapability(id, capability) abort
+ return 1
+ endfunction
+
+ function! ale#lsp#Send(id, message) abort
+ let g:request_id += 1
+
+ call add(g:sent_message_list, a:message)
+
+ return g:request_id
+ endfunction
+
+ function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
+ let g:LastCallback = a:Callback
+
+ return g:server_started_value
+ endfunction
+
+ function! ale#code_action#HandleCodeAction(code_action, should_save) abort
+ Assert !a:should_save
+
+ call add(g:code_action_list, a:code_action)
+ endfunction
+
+ function GetLastMessage()
+ return get(g:execute_list, -1, '')
+ endfunction
+
+ function CheckLintStates(conn_id, message)
+ " Check that we request more linter results after adding completions.
+ AssertEqual [[0, '']], g:ale_queue_call_list
+
+ let g:ale_enabled = 0
+
+ let g:ale_queue_call_list = []
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [], g:ale_queue_call_list
+
+ let g:ale_enabled = 1
+ let g:ale_lint_on_text_changed = 1
+
+ let g:ale_queue_call_list = []
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [[0, '']], g:ale_queue_call_list
+
+ let g:ale_lint_on_text_changed = 'normal'
+
+ let g:ale_queue_call_list = []
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [[0, '']], g:ale_queue_call_list
+
+ let g:ale_lint_on_text_changed = 'insert'
+
+ let g:ale_queue_call_list = []
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [[0, '']], g:ale_queue_call_list
+
+ let g:ale_queue_call_list = []
+ let g:ale_lint_on_text_changed = 'never'
+
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [], g:ale_queue_call_list
+
+ let g:ale_lint_on_text_changed = '0'
+
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [], g:ale_queue_call_list
+
+ let g:ale_lint_on_text_changed = 0
+
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [], g:ale_queue_call_list
+
+ let g:ale_lint_on_text_changed = 'xxx'
+
+ call g:LastHandleCallback(a:conn_id, a:message)
+ AssertEqual [], g:ale_queue_call_list
+ endfunction
+
+After:
+ call ale#linter#Reset()
+
+ Restore
+
+ delfunction GetLastMessage
+ delfunction CheckLintStates
+
+ unlet! g:LastCallback
+ unlet! g:LastHandleCallback
+ unlet! g:request_id
+ unlet! g:server_started_value
+ unlet! g:sent_message_list
+ unlet! g:code_action_list
+ unlet! g:ale_queue_call_list
+ unlet! g:execute_list
+ unlet! g:received_message
+ unlet! b:ale_old_omnifunc
+ unlet! b:ale_old_completeopt
+ unlet! b:ale_completion_info
+ unlet! b:ale_completion_result
+ unlet! b:ale_complete_done_time
+
+ runtime autoload/ale.vim
+ runtime autoload/ale/util.vim
+ runtime autoload/ale/code_action.vim
+ runtime autoload/ale/lsp.vim
+ runtime autoload/ale/lsp_linter.vim
+
+Given typescript(Some example TypeScript code):
+ let xyz = 123
+ let foo = missingword
+
+ let abc = 456
+
+Execute(ALEImport should complain when there's no word at the cursor):
+ call setpos('.', [bufnr(''), 3, 1, 0])
+ ALEImport
+
+ AssertEqual 'echom ''Nothing to complete at cursor!''', GetLastMessage()
+
+Execute(ALEImport should tell the user if no LSP is available):
+ let g:server_started_value = 0
+
+ call setpos('.', [bufnr(''), 2, 16, 0])
+ ALEImport
+
+ AssertEqual
+ \ 'echom ''No completion providers are available.''',
+ \ GetLastMessage()
+
+Execute(ALEImport should request imports correctly for tsserver):
+ call setpos('.', [bufnr(''), 2, 16, 0])
+
+ ALEImport
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 0,
+ \ 'request_id': 0,
+ \ 'source': 'ale-import',
+ \ 'column': 11,
+ \ 'line': 2,
+ \ 'line_length': 21,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastCallback isnot v:null
+
+ call g:LastCallback(ale#linter#Get(&filetype)[0], {
+ \ 'connection_id': 347,
+ \ 'buffer': bufnr(''),
+ \})
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 347,
+ \ 'request_id': 1,
+ \ 'source': 'ale-import',
+ \ 'column': 11,
+ \ 'line': 2,
+ \ 'line_length': 21,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastHandleCallback isnot v:null
+
+ call g:LastHandleCallback(347, {
+ \ 'request_seq': 1,
+ \ 'command': 'completions',
+ \ 'body': [
+ \ {'name': 'missingwordIgnoreMe'},
+ \ {'name': 'missingword'},
+ \ ],
+ \})
+
+ AssertEqual
+ \ [
+ \ [0, 'ts@completions', {
+ \ 'file': expand('%:p'),
+ \ 'includeExternalModuleExports': 1,
+ \ 'offset': 11,
+ \ 'line': 2,
+ \ 'prefix': 'missingword',
+ \ }],
+ \ [0, 'ts@completionEntryDetails', {
+ \ 'file': expand('%:p'),
+ \ 'entryNames': [{'name': 'missingword'}],
+ \ 'offset': 11,
+ \ 'line': 2,
+ \ }]
+ \ ],
+ \ g:sent_message_list
+ AssertEqual 2, b:ale_completion_info.request_id
+
+ let g:ale_enabled = 1
+ let g:received_message = {
+ \ 'request_seq': 2,
+ \ 'command': 'completionEntryDetails',
+ \ 'body': [
+ \ {
+ \ 'name': 'missingword',
+ \ 'kind': 'className',
+ \ 'displayParts': [],
+ \ 'codeActions': [{
+ \ 'description': 'import { missingword } from "./Something";',
+ \ 'changes': [],
+ \ }],
+ \ },
+ \ ],
+ \}
+ call g:LastHandleCallback(347, g:received_message)
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'description': 'import { missingword } from "./Something";',
+ \ 'changes': [],
+ \ },
+ \ ],
+ \ g:code_action_list
+
+ call CheckLintStates(347, g:received_message)
+
+Execute(ALEImport should tell the user when no completions were found from tsserver):
+ call setpos('.', [bufnr(''), 2, 16, 0])
+
+ ALEImport
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 0,
+ \ 'request_id': 0,
+ \ 'source': 'ale-import',
+ \ 'column': 11,
+ \ 'line': 2,
+ \ 'line_length': 21,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastCallback isnot v:null
+
+ call g:LastCallback(ale#linter#Get(&filetype)[0], {
+ \ 'connection_id': 347,
+ \ 'buffer': bufnr(''),
+ \})
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 347,
+ \ 'request_id': 1,
+ \ 'source': 'ale-import',
+ \ 'column': 11,
+ \ 'line': 2,
+ \ 'line_length': 21,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastHandleCallback isnot v:null
+
+ call g:LastHandleCallback(347, {
+ \ 'request_seq': 1,
+ \ 'command': 'completions',
+ \ 'body': [
+ \ {'name': 'missingwordIgnoreMe'},
+ \ ],
+ \})
+
+ AssertEqual 'echom ''No possible imports found.''', GetLastMessage()
+
+Given python(Some example Python code):
+ xyz = 123
+ foo = missingword
+
+ abc = 456
+
+Execute(ALEImport should request imports correctly for language servers):
+ call setpos('.', [bufnr(''), 2, 12, 0])
+
+ ALEImport
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 0,
+ \ 'request_id': 0,
+ \ 'source': 'ale-import',
+ \ 'column': 7,
+ \ 'line': 2,
+ \ 'line_length': 17,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastCallback isnot v:null
+
+ call g:LastCallback(ale#linter#Get(&filetype)[0], {
+ \ 'connection_id': 347,
+ \ 'buffer': bufnr(''),
+ \})
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 347,
+ \ 'request_id': 1,
+ \ 'source': 'ale-import',
+ \ 'column': 7,
+ \ 'line': 2,
+ \ 'line_length': 17,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastHandleCallback isnot v:null
+
+ AssertEqual
+ \ [
+ \ [0, 'textDocument/completion', {
+ \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
+ \ 'position': {'character': 6, 'line': 1}
+ \ }],
+ \ ],
+ \ g:sent_message_list
+ AssertEqual 1, b:ale_completion_info.request_id
+
+ let g:ale_enabled = 1
+ let g:received_message = {
+ \ 'id': 1,
+ \ 'jsonrpc': '2.0',
+ \ 'result': {
+ \ 'isIncomplete': v:false,
+ \ 'items': [
+ \ {
+ \ 'detail': 'Some other word we should ignore',
+ \ 'filterText': 'missingwordIgnoreMe',
+ \ 'insertText': 'missingwordIgnoreMe',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' missingwordIgnoreMe',
+ \ 'sortText': '3ee19999missingword',
+ \ 'additionalTextEdits': [
+ \ {
+ \ 'range': {
+ \ 'start': {'line': 1, 'character': 1},
+ \ 'end': {'line': 2, 'character': 1},
+ \ },
+ \ 'newText': 'from something import missingwordIgnoreMe',
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ 'detail': 'Some word without text edits',
+ \ 'filterText': 'missingword',
+ \ 'insertText': 'missingword',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' missingword',
+ \ 'sortText': '3ee19999missingword',
+ \ },
+ \ {
+ \ 'detail': 'The word we should use',
+ \ 'filterText': 'missingword',
+ \ 'insertText': 'missingword',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' missingword',
+ \ 'sortText': '3ee19999missingword',
+ \ 'additionalTextEdits': [
+ \ {
+ \ 'range': {
+ \ 'start': {'line': 1, 'character': 1},
+ \ 'end': {'line': 2, 'character': 1},
+ \ },
+ \ 'newText': 'from something import missingword',
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ 'detail': 'The other word we should not use',
+ \ 'filterText': 'missingword',
+ \ 'insertText': 'missingword',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' missingword',
+ \ 'sortText': '3ee19999missingword',
+ \ 'additionalTextEdits': [
+ \ {
+ \ 'range': {
+ \ 'start': {'line': 1, 'character': 1},
+ \ 'end': {'line': 2, 'character': 1},
+ \ },
+ \ 'newText': 'from something_else import missingword',
+ \ },
+ \ ],
+ \ },
+ \ ],
+ \ },
+ \}
+ call g:LastHandleCallback(347, g:received_message)
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'description': 'completion',
+ \ 'changes': [
+ \ {
+ \ 'fileName': expand('%:p'),
+ \ 'textChanges': [
+ \ {
+ \ 'start': {'line': 2, 'offset': 2},
+ \ 'end': {'line': 3, 'offset': 2},
+ \ 'newText': 'from something import missingword',
+ \ },
+ \ ],
+ \ },
+ \ ],
+ \ },
+ \ ],
+ \ g:code_action_list
+
+ call CheckLintStates(347, g:received_message)
+
+Execute(ALEImport should tell the user when no completions were found from a language server):
+ call setpos('.', [bufnr(''), 2, 12, 0])
+
+ ALEImport
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 0,
+ \ 'request_id': 0,
+ \ 'source': 'ale-import',
+ \ 'column': 7,
+ \ 'line': 2,
+ \ 'line_length': 17,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastCallback isnot v:null
+
+ call g:LastCallback(ale#linter#Get(&filetype)[0], {
+ \ 'connection_id': 347,
+ \ 'buffer': bufnr(''),
+ \})
+
+ AssertEqual
+ \ {
+ \ 'conn_id': 347,
+ \ 'request_id': 1,
+ \ 'source': 'ale-import',
+ \ 'column': 7,
+ \ 'line': 2,
+ \ 'line_length': 17,
+ \ 'prefix': 'missingword',
+ \ 'additional_edits_only': 1,
+ \ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
+ \ },
+ \ b:ale_completion_info
+ Assert g:LastHandleCallback isnot v:null
+
+ AssertEqual
+ \ [
+ \ [0, 'textDocument/completion', {
+ \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))},
+ \ 'position': {'character': 6, 'line': 1}
+ \ }],
+ \ ],
+ \ g:sent_message_list
+ AssertEqual 1, b:ale_completion_info.request_id
+
+ let g:received_message = {
+ \ 'id': 1,
+ \ 'jsonrpc': '2.0',
+ \ 'result': {
+ \ 'isIncomplete': v:false,
+ \ 'items': [
+ \ {
+ \ 'detail': 'Some other word we should ignore',
+ \ 'filterText': 'missingwordIgnoreMe',
+ \ 'insertText': 'missingwordIgnoreMe',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' missingwordIgnoreMe',
+ \ 'sortText': '3ee19999missingword',
+ \ 'additionalTextEdits': [
+ \ {
+ \ 'range': {
+ \ 'start': {'line': 1, 'character': 1},
+ \ 'end': {'line': 2, 'character': 1},
+ \ },
+ \ 'newText': 'from something import missingwordIgnoreMe',
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ 'detail': 'Some word without text edits',
+ \ 'filterText': 'missingword',
+ \ 'insertText': 'missingword',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' missingword',
+ \ 'sortText': '3ee19999missingword',
+ \ },
+ \ ],
+ \ },
+ \}
+ call g:LastHandleCallback(347, g:received_message)
+
+ AssertEqual 'echom ''No possible imports found.''', GetLastMessage()
diff --git a/test/completion/test_completion_events.vader b/test/completion/test_completion_events.vader
index 3a7a31d0..87bd10ad 100644
--- a/test/completion/test_completion_events.vader
+++ b/test/completion/test_completion_events.vader
@@ -35,7 +35,7 @@ Before:
let g:ale_completion_delay = 0
" Run this check a few times, as it can fail randomly.
- for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
+ for l:i in range(has('nvim-0.3') || has('win32') ? 5 : 1)
call ale#completion#Queue()
sleep 1m
diff --git a/test/completion/test_completion_filtering.vader b/test/completion/test_completion_filtering.vader
index c5f14266..172203a4 100644
--- a/test/completion/test_completion_filtering.vader
+++ b/test/completion/test_completion_filtering.vader
@@ -12,13 +12,24 @@ After:
Execute(Prefix filtering should work for Lists of strings):
AssertEqual
\ ['FooBar', 'foo'],
- \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo')
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo', 0)
AssertEqual
\ ['FooBar', 'FongBar', 'baz', 'foo'],
- \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.')
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.', 0)
AssertEqual
\ ['FooBar', 'FongBar', 'baz', 'foo'],
- \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '')
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '', 0)
+
+Execute(Exact filtering should work):
+ AssertEqual
+ \ ['foo'],
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], 'foo', 1)
+ AssertEqual
+ \ ['FooBar', 'FongBar', 'baz', 'foo'],
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'baz', 'foo'], '.', 1)
+ AssertEqual
+ \ ['Foo'],
+ \ ale#completion#Filter(bufnr(''), '', ['FooBar', 'FongBar', 'Foo', 'foo'], 'Foo', 1)
Execute(Prefix filtering should work for completion items):
AssertEqual
@@ -32,7 +43,8 @@ Execute(Prefix filtering should work for completion items):
\ {'word': 'baz'},
\ {'word': 'foo'},
\ ],
- \ 'foo'
+ \ 'foo',
+ \ 0,
\ )
AssertEqual
@@ -51,7 +63,8 @@ Execute(Prefix filtering should work for completion items):
\ {'word': 'baz'},
\ {'word': 'foo'},
\ ],
- \ '.'
+ \ '.',
+ \ 0,
\ )
Execute(Excluding words from completion results should work):
@@ -66,7 +79,8 @@ Execute(Excluding words from completion results should work):
\ {'word': 'Italian'},
\ {'word': 'it'},
\ ],
- \ 'it'
+ \ 'it',
+ \ 0,
\ )
AssertEqual
@@ -78,7 +92,8 @@ Execute(Excluding words from completion results should work):
\ {'word': 'describe'},
\ {'word': 'Deutsch'},
\ ],
- \ 'de'
+ \ 'de',
+ \ 0,
\ )
AssertEqual
@@ -90,7 +105,8 @@ Execute(Excluding words from completion results should work):
\ {'word': 'describe'},
\ {'word': 'Deutsch'},
\ ],
- \ '.'
+ \ '.',
+ \ 0,
\ )
Execute(Excluding words from completion results should work with lists of Strings):
@@ -98,29 +114,29 @@ Execute(Excluding words from completion results should work with lists of String
AssertEqual
\ ['Italian'],
- \ ale#completion#Filter(bufnr(''), '', ['Italian', 'it'], 'it')
+ \ ale#completion#Filter(bufnr(''), '', ['Italian', 'it'], 'it', 0)
AssertEqual
\ ['Deutsch'],
- \ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], 'de')
+ \ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], 'de', 0)
AssertEqual
\ ['Deutsch'],
- \ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], '.')
+ \ ale#completion#Filter(bufnr(''), '', ['describe', 'Deutsch'], '.', 0)
AssertEqual
\ ['Deutsch'],
- \ ale#completion#Filter(bufnr(''), '', ['Deutsch'], '')
+ \ ale#completion#Filter(bufnr(''), '', ['Deutsch'], '', 0)
Execute(Filtering shouldn't modify the original list):
let b:ale_completion_excluded_words = ['it', 'describe']
let b:suggestions = [{'word': 'describe'}]
- AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, '.')
+ AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, '.', 0)
AssertEqual b:suggestions, [{'word': 'describe'}]
- AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, 'de')
+ AssertEqual [], ale#completion#Filter(bufnr(''), '', b:suggestions, 'de', 0)
AssertEqual b:suggestions, [{'word': 'describe'}]
Execute(Filtering should respect filetype triggers):
let b:suggestions = [{'word': 'describe'}]
- AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), '', b:suggestions, '.')
- AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '.')
- AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '::')
+ AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), '', b:suggestions, '.', 0)
+ AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '.', 0)
+ AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '::', 0)
diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader
index 8b8b41c7..b8e71320 100644
--- a/test/completion/test_lsp_completion_parsing.vader
+++ b/test/completion/test_lsp_completion_parsing.vader
@@ -609,6 +609,7 @@ Execute(Should handle completion messages with additionalTextEdits when ale_comp
Execute(Should not handle completion messages with additionalTextEdits when ale_completion_autoimport is turned off):
let g:ale_completion_autoimport = 0
+ let b:ale_completion_info = {'line': 30}
AssertEqual
\ [],
@@ -645,3 +646,36 @@ Execute(Should not handle completion messages with additionalTextEdits when ale_
\ ],
\ },
\ })
+
+Execute(Should still handle completion messages with empty additionalTextEdits with ale_completion_autoimport turned off):
+ let g:ale_completion_autoimport = 0
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'word': 'next_callback',
+ \ 'menu': 'PlayTimeCallback',
+ \ 'info': '',
+ \ 'kind': 'v',
+ \ 'icase': 1,
+ \ }
+ \ ],
+ \ ale#completion#ParseLSPCompletions({
+ \ 'id': 226,
+ \ 'jsonrpc': '2.0',
+ \ 'result': {
+ \ 'isIncomplete': v:false,
+ \ 'items': [
+ \ {
+ \ 'detail': 'PlayTimeCallback',
+ \ 'filterText': 'next_callback',
+ \ 'insertText': 'next_callback',
+ \ 'insertTextFormat': 1,
+ \ 'kind': 6,
+ \ 'label': ' next_callback',
+ \ 'sortText': '3ee19999next_callback',
+ \ 'additionalTextEdits': [],
+ \ },
+ \ ],
+ \ },
+ \ })
diff --git a/test/completion/test_tsserver_completion_parsing.vader b/test/completion/test_tsserver_completion_parsing.vader
index 4515afd1..aaaaae95 100644
--- a/test/completion/test_tsserver_completion_parsing.vader
+++ b/test/completion/test_tsserver_completion_parsing.vader
@@ -1,4 +1,11 @@
+Before:
+ Save g:ale_completion_tsserver_remove_warnings
+
+ let g:ale_completion_tsserver_remove_warnings = 0
+
After:
+ Restore
+
unlet! b:ale_tsserver_completion_names
Execute(TypeScript completions responses should be parsed correctly):
@@ -242,3 +249,54 @@ Execute(Entries without details should be included in the responses):
\ },
\ ],
\})
+
+Execute(Default imports should be handled correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'word': 'abcd',
+ \ 'menu': 'Import default ''abcd'' from module "./foo" (alias) const abcd: 3',
+ \ 'info': '',
+ \ 'kind': 't',
+ \ 'icase': 1,
+ \ 'user_data': json_encode({
+ \ 'codeActions': [{
+ \ 'description': 'Import default ''abcd'' from module "./foo"',
+ \ 'changes': [],
+ \ }],
+ \ }),
+ \ 'dup': g:ale_completion_autoimport,
+ \ },
+ \ ],
+ \ ale#completion#ParseTSServerCompletionEntryDetails({
+ \ 'body': [
+ \ {
+ \ 'name': 'default',
+ \ 'kind': 'alias',
+ \ 'displayParts': [
+ \ {'kind': 'punctuation', 'text': '('},
+ \ {'kind': 'text', 'text': 'alias'},
+ \ {'kind': 'punctuation', 'text': ')'},
+ \ {'kind': 'space', 'text': ' '},
+ \ {'kind': 'keyword', 'text': 'const'},
+ \ {'kind': 'space', 'text': ' '},
+ \ {'kind': 'localName', 'text': 'abcd'},
+ \ {'kind': 'punctuation', 'text': ':'},
+ \ {'kind': 'space', 'text': ' '},
+ \ {'kind': 'stringLiteral', 'text': '3'},
+ \ {'kind': 'lineBreak', 'text': '^@'},
+ \ {'kind': 'keyword', 'text': 'export'},
+ \ {'kind': 'space', 'text': ' '},
+ \ {'kind': 'keyword', 'text': 'default'},
+ \ {'kind': 'space', 'text': ' '},
+ \ {'kind': 'aliasName', 'text': 'abcd'}
+ \ ],
+ \ 'codeActions': [
+ \ {
+ \ 'description': 'Import default ''abcd'' from module "./foo"',
+ \ 'changes': [],
+ \ },
+ \ ],
+ \ },
+ \ ],
+ \ })
diff --git a/test/ember-template-lint-test-files/app/template.hbs b/test/ember-template-lint-test-files/app/template.hbs
new file mode 100644
index 00000000..e69de29b
diff --git a/test/ember-template-lint-test-files/package.json b/test/ember-template-lint-test-files/package.json
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fix/test_ale_fix.vader b/test/fix/test_ale_fix.vader
index c3dd20e4..a51a5a53 100644
--- a/test/fix/test_ale_fix.vader
+++ b/test/fix/test_ale_fix.vader
@@ -5,18 +5,22 @@ Before:
Save g:ale_fix_on_save
Save g:ale_lint_on_save
Save g:ale_echo_cursor
+ Save g:ale_command_wrapper
+ Save g:ale_filename_mappings
silent! cd /testplugin/test/fix
unlet! b:ale_lint_on_save
let g:ale_enabled = 0
let g:ale_echo_cursor = 0
+ let g:ale_command_wrapper = ''
let g:ale_run_synchronously = 1
let g:ale_set_lists_synchronously = 1
let g:ale_fix_buffer_data = {}
let g:ale_fixers = {
\ 'testft': [],
\}
+ let g:ale_filename_mappings = {}
let g:pre_success = 0
let g:post_success = 0
@@ -70,6 +74,10 @@ Before:
return {'command': 'cat %t <(echo d)'}
endfunction
+ function EchoFilename(buffer, lines) abort
+ return {'command': 'echo %s'}
+ endfunction
+
function RemoveLastLine(buffer, lines) abort
return ['a', 'b']
endfunction
@@ -82,56 +90,6 @@ Before:
return [{'lnum': 1, 'col': 1, 'text': 'xxx'}]
endfunction
- function! FirstChainCallback(buffer)
- return {'command': 'echo echoline', 'chain_with': 'SecondChainCallback'}
- endfunction
-
- function! FirstChainCallbackSkipped(buffer)
- let l:ChainWith = 'SecondChainCallback'
-
- " Test with lambdas where support is available.
- if has('lambda')
- let l:ChainWith = {buffer, output -> SecondChainCallback(buffer, output)}
- endif
-
- return {'command': '', 'chain_with': l:ChainWith}
- endfunction
-
- function! FirstChainCallbackSecondSkipped(buffer)
- return {'command': 'echo skipit', 'chain_with': 'SecondChainCallback'}
- endfunction
-
- function! SecondChainCallback(buffer, output)
- let l:previous_line = empty(a:output)
- \ ? 'emptydefault'
- \ : join(split(a:output[0]))
-
- if l:previous_line is# 'skipit'
- return {'command': '', 'chain_with': 'ThirdChainCallback'}
- endif
-
- return {
- \ 'command': 'echo ' . l:previous_line,
- \ 'chain_with': 'ThirdChainCallback',
- \}
- endfunction
-
- function! ThirdChainCallback(buffer, output, input)
- let l:previous_line = empty(a:output)
- \ ? 'thirddefault'
- \ : join(split(a:output[0]))
-
- return a:input + [l:previous_line]
- endfunction
-
- function! ChainWhereLastIsSkipped(buffer)
- return {'command': 'echo echoline', 'chain_with': 'ChainEndSkipped'}
- endfunction
-
- function! ChainEndSkipped(buffer, output)
- return {'command': ''}
- endfunction
-
" echo will output a single blank line, and we should ingore it.
function! IgnoredEmptyOutput(buffer, output)
return {'command': has('win32') ? 'echo(' : 'echo'}
@@ -203,16 +161,10 @@ After:
delfunction CatLineDeferred
delfunction ReplaceWithTempFile
delfunction CatWithTempFile
+ delfunction EchoFilename
delfunction RemoveLastLine
delfunction RemoveLastLineOneArg
delfunction TestCallback
- delfunction FirstChainCallback
- delfunction FirstChainCallbackSkipped
- delfunction FirstChainCallbackSecondSkipped
- delfunction SecondChainCallback
- delfunction ThirdChainCallback
- delfunction ChainWhereLastIsSkipped
- delfunction ChainEndSkipped
delfunction SetUpLinters
delfunction GetLastMessage
delfunction IgnoredEmptyOutput
@@ -264,6 +216,25 @@ Expect(The first function should be used):
^b
^c
+Execute(Should apply filename mpapings):
+ " The command echos %s, and we'll map the current path so we can check
+ " that ALEFix applies filename mappings, end-to-end.
+ let g:ale_filename_mappings = {
+ \ 'echo_filename': [
+ \ [expand('%:p:h') . '/', '/some/fake/path/'],
+ \ ],
+ \}
+
+ call ale#fix#registry#Add('echo_filename', 'EchoFilename', [], 'echo filename')
+ let g:ale_fixers.testft = ['echo_filename']
+ ALEFix
+ call ale#test#FlushJobs()
+ " Remote trailing whitespace from the line.
+ call setline(1, substitute(getline(1), '[ \r]\+$', '', ''))
+
+Expect(The mapped filename should be printed):
+ /some/fake/path/test.txt
+
Execute(ALEFix should apply simple functions in a chain):
let g:ale_fixers.testft = ['AddCarets', 'Capitalize']
ALEFix
@@ -756,6 +727,19 @@ Expect(There should be only two lines):
a
b
+Execute(ALEFix should modify a buffer that is not modifiable, if it becomes modifiable later):
+ let g:ale_fixers.testft = ['RemoveLastLineOneArg']
+
+ set nomodifiable
+ ALEFix
+ call ale#test#FlushJobs()
+ set modifiable
+ call ale#fix#ApplyQueuedFixes(bufnr(''))
+
+Expect(There should be only two lines):
+ a
+ b
+
Execute(b:ale_fix_on_save = 1 should override g:ale_fix_on_save = 0):
let g:ale_fix_on_save = 0
let b:ale_fix_on_save = 1
@@ -814,57 +798,6 @@ Execute(ALE should tolerate valid fixers with minuses in the name):
ALEFix
call ale#test#FlushJobs()
-Execute(Test fixing with chained callbacks):
- let g:ale_fixers.testft = ['FirstChainCallback']
- ALEFix
- call ale#test#FlushJobs()
-
- " The buffer shouldn't be piped in for earlier commands in the chain.
- AssertEqual
- \ [
- \ string(ale#job#PrepareCommand(bufnr(''), 'echo echoline')),
- \ string(ale#job#PrepareCommand(bufnr(''), 'echo echoline')),
- \ ],
- \ map(ale#history#Get(bufnr(''))[-2:-1], 'string(v:val.command)')
-
-Expect(The echoed line should be added):
- a
- b
- c
- echoline
-
-Execute(Test fixing with chained callback where the first command is skipped):
- let g:ale_fixers.testft = ['FirstChainCallbackSkipped']
- ALEFix
- call ale#test#FlushJobs()
-
-Expect(The default line should be added):
- a
- b
- c
- emptydefault
-
-Execute(Test fixing with chained callback where the second command is skipped):
- let g:ale_fixers.testft = ['FirstChainCallbackSecondSkipped']
- ALEFix
- call ale#test#FlushJobs()
-
-Expect(The default line should be added):
- a
- b
- c
- thirddefault
-
-Execute(Test fixing with chained callback where the final callback is skipped):
- let g:ale_fixers.testft = ['ChainWhereLastIsSkipped']
- ALEFix
- call ale#test#FlushJobs()
-
-Expect(The lines should be the same):
- a
- b
- c
-
Execute(Empty output should be ignored):
let g:ale_fixers.testft = ['IgnoredEmptyOutput']
ALEFix
diff --git a/test/fixers/test_clangtidy_fixer_callback.vader b/test/fixers/test_clangtidy_fixer_callback.vader
index 68416b36..ca08e6bc 100644
--- a/test/fixers/test_clangtidy_fixer_callback.vader
+++ b/test/fixers/test_clangtidy_fixer_callback.vader
@@ -1,8 +1,19 @@
Before:
+ Save g:ale_c_build_dir
Save g:ale_c_clangtidy_executable
+ Save g:ale_c_clangtidy_checks
+ Save g:ale_c_clangtidy_extra_options
+ Save g:ale_cpp_clangtidy_executable
+ Save g:ale_cpp_clangtidy_checks
+ Save g:ale_cpp_clangtidy_extra_options
" Use an invalid global executable, so we don't match it.
let g:ale_c_clangtidy_executable = 'xxxinvalid'
+ let g:ale_c_clangtidy_checks = []
+ let g:ale_c_clangtidy_extra_options = ''
+ let g:ale_cpp_clangtidy_executable = 'xxxinvalidpp'
+ let g:ale_cpp_clangtidy_checks = []
+ let g:ale_cpp_clangtidy_extra_options = ''
let g:ale_c_build_dir = ''
call ale#test#SetDirectory('/testplugin/test/fixers')
@@ -36,16 +47,3 @@ Execute(The clangtidy callback should include any additional options):
\ . ' -fix -fix-errors --some-option %t',
\ },
\ ale#fixers#clangtidy#Fix(bufnr(''))
-
-Execute(The clangtidy callback should support cpp files):
- call ale#test#SetFilename('c_paths/dummy.cpp')
- let g:ale_cpp_clangtidy_executable = 'invalidpp'
- set filetype=cpp " The test fails without this
-
- AssertEqual
- \ {
- \ 'read_temporary_file': 1,
- \ 'command': ale#Escape(g:ale_cpp_clangtidy_executable)
- \ . ' -fix -fix-errors %t',
- \ },
- \ ale#fixers#clangtidy#Fix(bufnr(''))
diff --git a/test/fixers/test_dhall_fixer_callback.vader b/test/fixers/test_dhall_fixer_callback.vader
new file mode 100644
index 00000000..f27880b7
--- /dev/null
+++ b/test/fixers/test_dhall_fixer_callback.vader
@@ -0,0 +1,11 @@
+Before:
+ call ale#assert#SetUpFixerTest('dhall', 'dhall')
+
+After:
+ call ale#assert#TearDownFixerTest()
+
+Execute(The default command should be correct):
+ AssertFixer
+ \ { 'read_temporary_file': 1,
+ \ 'command': ale#Escape('dhall') . ' format --inplace %t'
+ \ }
diff --git a/test/fixers/test_eslint_fixer_callback.vader b/test/fixers/test_eslint_fixer_callback.vader
index 400267ac..50fc6672 100644
--- a/test/fixers/test_eslint_fixer_callback.vader
+++ b/test/fixers/test_eslint_fixer_callback.vader
@@ -1,7 +1,11 @@
Before:
call ale#assert#SetUpFixerTest('javascript', 'eslint')
+ Save g:ale_command_wrapper
+
runtime autoload/ale/handlers/eslint.vim
+ let g:ale_command_wrapper = ''
+
After:
call ale#assert#TearDownFixerTest()
diff --git a/test/fixers/test_isort_fixer_callback.vader b/test/fixers/test_isort_fixer_callback.vader
index 50818621..7f389dcf 100644
--- a/test/fixers/test_isort_fixer_callback.vader
+++ b/test/fixers/test_isort_fixer_callback.vader
@@ -4,6 +4,7 @@ Before:
" Use an invalid global executable, so we don't match it.
let g:ale_python_isort_executable = 'xxxinvalid'
+ let g:ale_python_isort_options = ''
call ale#test#SetDirectory('/testplugin/test/fixers')
silent cd ..
@@ -27,7 +28,7 @@ Execute(The isort callback should return the correct default values):
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
AssertEqual
\ {
- \ 'command': ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir/foo'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/isort')) . ' -',
\ },
\ ale#fixers#isort#Fix(bufnr(''))
@@ -42,7 +43,7 @@ Execute(The isort callback should respect custom options):
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
AssertEqual
\ {
- \ 'command': ale#path#CdString(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/subdir/foo'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/isort'))
\ . ' --multi-line=3 --trailing-comma -',
\ },
diff --git a/test/fixers/test_latexindent_fixer_callback.vader b/test/fixers/test_latexindent_fixer_callback.vader
index d0da94a1..a440ed65 100644
--- a/test/fixers/test_latexindent_fixer_callback.vader
+++ b/test/fixers/test_latexindent_fixer_callback.vader
@@ -18,10 +18,8 @@ Execute(The latexindent callback should return the correct default values):
AssertEqual
\ {
- \ 'read_temporary_file': 1,
\ 'command': ale#Escape('xxxinvalid')
- \ . ' -l -w'
- \ . ' %t',
+ \ . ' -l'
\ },
\ ale#fixers#latexindent#Fix(bufnr(''))
@@ -31,10 +29,8 @@ Execute(The latexindent callback should include custom gofmt options):
AssertEqual
\ {
- \ 'read_temporary_file': 1,
\ 'command': ale#Escape('xxxinvalid')
- \ . ' -l -w'
+ \ . ' -l'
\ . ' ' . g:ale_tex_latexindent_options
- \ . ' %t',
\ },
\ ale#fixers#latexindent#Fix(bufnr(''))
diff --git a/test/fixers/test_ocamlformat_fixer_callback.vader b/test/fixers/test_ocamlformat_fixer_callback.vader
index f0c36ed7..0ccdb070 100644
--- a/test/fixers/test_ocamlformat_fixer_callback.vader
+++ b/test/fixers/test_ocamlformat_fixer_callback.vader
@@ -19,8 +19,7 @@ Execute(The ocamlformat callback should return the correct default values):
AssertEqual
\ {
\ 'command': ale#Escape('xxxinvalid')
- \ . ' --name=' . ale#Escape(bufname(bufnr('')))
- \ . ' -',
+ \ . ' --name=%s -',
\ },
\ ale#fixers#ocamlformat#Fix(bufnr(''))
@@ -32,7 +31,6 @@ Execute(The ocamlformat callback should include custom ocamlformat options):
\ {
\ 'command': ale#Escape('xxxinvalid')
\ . ' ' . g:ale_ocaml_ocamlformat_options
- \ . ' --name=' . ale#Escape(bufname(bufnr('')))
- \ . ' -',
+ \ . ' --name=%s -',
\ },
\ ale#fixers#ocamlformat#Fix(bufnr(''))
diff --git a/test/fixers/test_prettier_eslint_fixer.callback.vader b/test/fixers/test_prettier_eslint_fixer.callback.vader
index 90e11672..be8f04e3 100644
--- a/test/fixers/test_prettier_eslint_fixer.callback.vader
+++ b/test/fixers/test_prettier_eslint_fixer.callback.vader
@@ -1,5 +1,8 @@
Before:
call ale#assert#SetUpFixerTest('javascript', 'prettier_eslint')
+ Save g:ale_command_wrapper
+
+ let g:ale_command_wrapper = ''
After:
call ale#assert#TearDownFixerTest()
@@ -70,7 +73,7 @@ Execute(The new --stdin-filepath option should be used when the version is new e
GivenCommandOutput ['4.4.0']
AssertFixer
\ {
- \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('prettier-eslint')
\ . ' --eslint-config-path ' . ale#Escape(ale#path#Simplify(g:dir . '/eslint-test-files/react-app/.eslintrc.js'))
\ . ' --stdin-filepath %s --stdin',
@@ -80,7 +83,7 @@ Execute(The version number should be cached):
GivenCommandOutput ['4.4.0']
AssertFixer
\ {
- \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('prettier-eslint')
\ . ' --stdin-filepath %s --stdin',
\ }
@@ -88,7 +91,7 @@ Execute(The version number should be cached):
GivenCommandOutput []
AssertFixer
\ {
- \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . ale#Escape('prettier-eslint')
\ . ' --stdin-filepath %s --stdin',
\ }
diff --git a/test/fixers/test_prettier_fixer_callback.vader b/test/fixers/test_prettier_fixer_callback.vader
index 062ae8cf..fdd97df3 100644
--- a/test/fixers/test_prettier_fixer_callback.vader
+++ b/test/fixers/test_prettier_fixer_callback.vader
@@ -1,5 +1,8 @@
Before:
call ale#assert#SetUpFixerTest('javascript', 'prettier')
+ Save g:ale_command_wrapper
+
+ let g:ale_command_wrapper = ''
silent cd ..
silent cd command_callback
@@ -294,6 +297,17 @@ Execute(Should set --parser for experimental language, Handlebars):
\ . ' --stdin-filepath %s --stdin',
\ }
+Execute(Changes to directory where .prettierignore is found):
+ call ale#test#SetFilename('../prettier-test-files/with_prettierignore/src/testfile.js')
+
+ GivenCommandOutput ['1.6.0']
+ AssertFixer
+ \ {
+ \ 'command': ale#path#CdString(expand('%:p:h:h'))
+ \ . ale#Escape(g:ale_javascript_prettier_executable)
+ \ . ' --stdin-filepath %s --stdin',
+ \ }
+
Execute(The prettier_d post-processor should permit regular JavaScript content):
AssertEqual
\ [
diff --git a/test/fixers/test_prettier_standard_callback.vader b/test/fixers/test_prettier_standard_callback.vader
new file mode 100644
index 00000000..ab33fe20
--- /dev/null
+++ b/test/fixers/test_prettier_standard_callback.vader
@@ -0,0 +1,19 @@
+Before:
+ call ale#assert#SetUpFixerTest('javascript', 'prettier_standard')
+
+ silent cd ..
+ silent cd command_callback
+ let g:dir = getcwd()
+
+After:
+ call ale#assert#TearDownFixerTest()
+
+Execute(The prettier callback should return the correct default values):
+ call ale#test#SetFilename('../prettier-test-files/testfile.js')
+
+ AssertFixer
+ \ {
+ \ 'command': ale#Escape(g:ale_javascript_prettier_standard_executable)
+ \ . ' --stdin'
+ \ . ' --stdin-filepath=%s ',
+ \ }
diff --git a/test/fixers/test_remark_lint_fixer_callback.vader b/test/fixers/test_remark_lint_fixer_callback.vader
new file mode 100644
index 00000000..5e2e342d
--- /dev/null
+++ b/test/fixers/test_remark_lint_fixer_callback.vader
@@ -0,0 +1,24 @@
+Before:
+ Save g:ale_markdown_remark_lint_executable
+ Save g:ale_markdown_remark_lint_options
+
+After:
+ Restore
+
+Execute(The remark callback should return the correct default values):
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('remark')
+ \ },
+ \ ale#fixers#remark_lint#Fix(bufnr(''))
+
+Execute(The remark executable and options should be configurable):
+ let g:ale_markdown_remark_lint_executable = '/path/to/remark'
+ let g:ale_markdown_remark_lint_options = '-h'
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('/path/to/remark')
+ \ . ' -h',
+ \ },
+ \ ale#fixers#remark_lint#Fix(bufnr(''))
diff --git a/test/fixers/test_rubocop_fixer_callback.vader b/test/fixers/test_rubocop_fixer_callback.vader
index 305881e0..84579d31 100644
--- a/test/fixers/test_rubocop_fixer_callback.vader
+++ b/test/fixers/test_rubocop_fixer_callback.vader
@@ -23,8 +23,7 @@ Execute(The rubocop callback should return the correct default values):
\ {
\ 'process_with': 'ale#fixers#rubocop#PostProcess',
\ 'command': ale#Escape(g:ale_ruby_rubocop_executable)
- \ . ' --auto-correct --force-exclusion --stdin '
- \ . ale#Escape(expand('#' . bufnr('') . ':p')),
+ \ . ' --auto-correct --force-exclusion --stdin %s',
\ },
\ ale#fixers#rubocop#Fix(bufnr(''))
@@ -36,8 +35,7 @@ Execute(The rubocop callback should include configuration files):
\ 'process_with': 'ale#fixers#rubocop#PostProcess',
\ 'command': ale#Escape(g:ale_ruby_rubocop_executable)
\ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml'))
- \ . ' --auto-correct --force-exclusion --stdin '
- \ . ale#Escape(expand('#' . bufnr('') . ':p')),
+ \ . ' --auto-correct --force-exclusion --stdin %s',
\ },
\ ale#fixers#rubocop#Fix(bufnr(''))
@@ -51,8 +49,7 @@ Execute(The rubocop callback should include custom rubocop options):
\ 'command': ale#Escape(g:ale_ruby_rubocop_executable)
\ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml'))
\ . ' --except Lint/Debugger'
- \ . ' --auto-correct --force-exclusion --stdin '
- \ . ale#Escape(expand('#' . bufnr('') . ':p')),
+ \ . ' --auto-correct --force-exclusion --stdin %s',
\ },
\ ale#fixers#rubocop#Fix(bufnr(''))
@@ -65,8 +62,7 @@ Execute(The rubocop callback should use auto-correct-all option when set):
\ 'process_with': 'ale#fixers#rubocop#PostProcess',
\ 'command': ale#Escape(g:ale_ruby_rubocop_executable)
\ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.rubocop.yml'))
- \ . ' --auto-correct-all --force-exclusion --stdin '
- \ . ale#Escape(expand('#' . bufnr('') . ':p')),
+ \ . ' --auto-correct-all --force-exclusion --stdin %s'
\ },
\ ale#fixers#rubocop#Fix(bufnr(''))
diff --git a/test/fixers/test_standard_fixer_callback.vader b/test/fixers/test_standard_fixer_callback.vader
index db9f20f6..f5e9c487 100644
--- a/test/fixers/test_standard_fixer_callback.vader
+++ b/test/fixers/test_standard_fixer_callback.vader
@@ -15,7 +15,7 @@ Execute(The executable path should be correct):
\ 'read_temporary_file': 1,
\ 'command': (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/standard/bin/cmd.js'))
- \ . ' --fix %t',
+ \ . ' --fix --stdin < %s > %t',
\ },
\ ale#fixers#standard#Fix(bufnr(''))
@@ -26,6 +26,6 @@ Execute(Custom options should be supported):
AssertEqual
\ {
\ 'read_temporary_file': 1,
- \ 'command': ale#Escape('standard') . ' --foo-bar --fix %t',
+ \ 'command': ale#Escape('standard') . ' --foo-bar --fix --stdin < %s > %t',
\ },
\ ale#fixers#standard#Fix(bufnr(''))
diff --git a/test/fixers/test_stylelint_fixer_callback.vader b/test/fixers/test_stylelint_fixer_callback.vader
index f677cdf7..8fbd8a51 100644
--- a/test/fixers/test_stylelint_fixer_callback.vader
+++ b/test/fixers/test_stylelint_fixer_callback.vader
@@ -1,4 +1,8 @@
Before:
+ Save g:ale_stylelint_options
+
+ let g:ale_stylelint_options = ''
+
call ale#assert#SetUpFixerTest('css', 'stylelint')
After:
@@ -10,7 +14,7 @@ Execute(The stylelint callback should return the correct default values):
AssertFixer
\ {
\ 'read_temporary_file': 1,
- \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js'))
\ . ' %t'
@@ -24,7 +28,7 @@ Execute(The stylelint callback should include custom stylelint options):
AssertFixer
\ {
\ 'read_temporary_file': 1,
- \ 'command': ale#path#CdString(expand('%:p:h'))
+ \ 'command': ale#path#BufferCdString(bufnr(''))
\ . (has('win32') ? 'node.exe ' : '')
\ . ale#Escape(ale#path#Simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js'))
\ . ' %t'
diff --git a/test/handler/test_gcc_handler.vader b/test/handler/test_gcc_handler.vader
index b67483a6..a4231cab 100644
--- a/test/handler/test_gcc_handler.vader
+++ b/test/handler/test_gcc_handler.vader
@@ -279,3 +279,38 @@ Execute(The GCC handler should handle errors for inlined header functions):
\ ' __open_too_many_args ();',
\ ' ^~~~~~~~~~~~~~~~~~~~~~~',
\ ])
+
+Execute(The GCC handler should handle macro expansion errors in current file):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'col': 19,
+ \ 'type': 'E',
+ \ 'text': 'error message',
+ \ 'detail': "error message\n:1:19: note: in expansion of macro 'TEST'",
+ \ },
+ \ ],
+ \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [
+ \ ': error: error message',
+ \ ':1:19: note: in expansion of macro ‘TEST’',
+ \ ' 1 | std::string str = TEST;',
+ \ ' | ^~~~',
+ \ ])
+
+Execute(The GCC handler should handle macro expansion errors in other files):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 0,
+ \ 'type': 'E',
+ \ 'text': 'Error found in macro expansion. See :ALEDetail',
+ \ 'detail': "error message\ninc.h:1:19: note: in expansion of macro 'TEST'",
+ \ },
+ \ ],
+ \ ale#handlers#gcc#HandleGCCFormatWithIncludes(347, [
+ \ ': error: error message',
+ \ 'inc.h:1:19: note: in expansion of macro ‘TEST’',
+ \ ' 1 | std::string str = TEST;',
+ \ ' | ^~~~',
+ \ ])
diff --git a/test/handler/test_glslang_handler.vader b/test/handler/test_glslang_handler.vader
index d51c9852..6d3a7999 100644
--- a/test/handler/test_glslang_handler.vader
+++ b/test/handler/test_glslang_handler.vader
@@ -1,3 +1,6 @@
+Before:
+ runtime ale_linters/glsl/glslang.vim
+
Execute(The glsl glslang handler should parse lines correctly):
AssertEqual
\ [
diff --git a/test/handler/test_sqllint_handler.vader b/test/handler/test_sqllint_handler.vader
new file mode 100644
index 00000000..2f2283c8
--- /dev/null
+++ b/test/handler/test_sqllint_handler.vader
@@ -0,0 +1,23 @@
+Before:
+ " Load the file which defines the linter.
+ runtime ale_linters/sql/sqllint.vim
+
+After:
+ " Unload all linters again.
+ call ale#linter#Reset()
+
+Execute (The output should be correct):
+
+ " Test that the right loclist items are parsed from the handler.
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 1,
+ \ 'col': 0,
+ \ 'type': '',
+ \ 'text': 'stdin:1 [ER_NO_DB_ERROR] No database selected'
+ \ },
+ \ ],
+ \ ale_linters#sql#sqllint#Handle(bufnr(''), [
+ \ 'stdin:1 [ER_NO_DB_ERROR] No database selected'
+ \ ])
diff --git a/test/handler/test_standard_handler.vader b/test/handler/test_standard_handler.vader
index 59ebe531..31e3a36b 100644
--- a/test/handler/test_standard_handler.vader
+++ b/test/handler/test_standard_handler.vader
@@ -1,3 +1,11 @@
+Before:
+ Save g:ale_javascript_eslint_suppress_eslintignore
+
+ let g:ale_javascript_eslint_suppress_eslintignore = 0
+
+After:
+ Restore
+
Execute(The standard handler should parse lines correctly):
AssertEqual
\ [
diff --git a/test/handler/test_swiftformat_handler.vader b/test/handler/test_swiftformat_handler.vader
new file mode 100644
index 00000000..3dcc4f1a
--- /dev/null
+++ b/test/handler/test_swiftformat_handler.vader
@@ -0,0 +1,28 @@
+Before:
+ runtime ale_linters/swift/swiftformat.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The swiftformat handler should parse lines correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 4,
+ \ 'col': 21,
+ \ 'type': 'W',
+ \ 'code': 'DoNotUseSemicolons',
+ \ 'text': 'remove '';'' and move the next statement to the new line',
+ \ },
+ \ {
+ \ 'lnum': 3,
+ \ 'col': 12,
+ \ 'type': 'W',
+ \ 'code': 'Spacing',
+ \ 'text': 'remove 1 space'
+ \ },
+ \ ],
+ \ ale_linters#swift#swiftformat#Handle(bufnr(''), [
+ \ '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',
+ \ ])
diff --git a/test/handler/test_vlog_handler.vader b/test/handler/test_vlog_handler.vader
index daf3cdcf..7262f63d 100644
--- a/test/handler/test_vlog_handler.vader
+++ b/test/handler/test_vlog_handler.vader
@@ -10,12 +10,14 @@ Execute(The vlog handler should parse old-style lines correctly):
\ {
\ 'lnum': 7,
\ 'type': 'W',
- \ 'text': '(vlog-2623) Undefined variable: C.'
+ \ 'text': '(vlog-2623) Undefined variable: C.',
+ \ 'filename': 'add.v'
\ },
\ {
\ 'lnum': 1,
\ 'type': 'E',
- \ 'text': '(vlog-13294) Identifier must be declared with a port mode: C.'
+ \ 'text': '(vlog-13294) Identifier must be declared with a port mode: C.',
+ \ 'filename': 'file.v'
\ },
\ ],
\ ale_linters#verilog#vlog#Handle(bufnr(''), [
@@ -29,12 +31,14 @@ Execute(The vlog handler should parse new-style lines correctly):
\ {
\ 'lnum': 7,
\ 'type': 'W',
- \ 'text': '(vlog-2623) Undefined variable: C.'
+ \ 'text': '(vlog-2623) Undefined variable: C.',
+ \ 'filename': 'add.v'
\ },
\ {
\ 'lnum': 1,
\ 'type': 'E',
- \ 'text': '(vlog-13294) Identifier must be declared with a port mode: C.'
+ \ 'text': '(vlog-13294) Identifier must be declared with a port mode: C.',
+ \ 'filename': 'file.v'
\ },
\ ],
\ ale_linters#verilog#vlog#Handle(bufnr(''), [
diff --git a/test/lsp/test_did_save_event.vader b/test/lsp/test_did_save_event.vader
index bdea6d98..1d811363 100644
--- a/test/lsp/test_did_save_event.vader
+++ b/test/lsp/test_did_save_event.vader
@@ -33,8 +33,8 @@ Before:
\ 'lsp': 'stdio',
\ 'command': 'cat - > /dev/null',
\ 'executable': has('win32') ? 'cmd' : 'echo',
- \ 'language_callback': 'LanguageCallback',
- \ 'project_root_callback': 'ProjectRootCallback',
+ \ 'language': function('LanguageCallback'),
+ \ 'project_root': function('ProjectRootCallback'),
\ })
let g:ale_linters = {'foobar': ['dummy_linter']}
diff --git a/test/lsp/test_engine_lsp_response_handling.vader b/test/lsp/test_engine_lsp_response_handling.vader
index 9abfa087..8261f1da 100644
--- a/test/lsp/test_engine_lsp_response_handling.vader
+++ b/test/lsp/test_engine_lsp_response_handling.vader
@@ -44,9 +44,21 @@ After:
Given foobar(An empty file):
Execute(tsserver syntax error responses should be handled correctly):
runtime ale_linters/typescript/tsserver.vim
- call ale#test#SetFilename('filename.ts')
+
+ if has('win32')
+ call ale#test#SetFilename('filename,[]^$.ts')
+ else
+ call ale#test#SetFilename('filename*?,{}[]^$.ts')
+ endif
+
call ale#engine#InitBufferInfo(bufnr(''))
+ if has('win32')
+ AssertEqual 'filename,[]^$.ts', expand('%:p:t')
+ else
+ AssertEqual 'filename*?,{}[]^$.ts', expand('%:p:t')
+ endif
+
" When we get syntax errors and no semantic errors, we should keep the
" syntax errors.
call ale#lsp_linter#HandleLSPResponse(1, {
@@ -54,7 +66,7 @@ Execute(tsserver syntax error responses should be handled correctly):
\ 'type': 'event',
\ 'event': 'syntaxDiag',
\ 'body': {
- \ 'file': g:dir . '/filename.ts',
+ \ 'file': expand('%:p'),
\ 'diagnostics':[
\ {
\ 'start': {
@@ -76,7 +88,7 @@ Execute(tsserver syntax error responses should be handled correctly):
\ 'type': 'event',
\ 'event': 'semanticDiag',
\ 'body': {
- \ 'file': g:dir . '/filename.ts',
+ \ 'file': expand('%:p'),
\ 'diagnostics':[
\ ],
\ },
@@ -104,7 +116,7 @@ Execute(tsserver syntax error responses should be handled correctly):
\ 'type': 'event',
\ 'event': 'syntaxDiag',
\ 'body': {
- \ 'file': g:dir . '/filename.ts',
+ \ 'file': expand('%:p'),
\ 'diagnostics':[
\ ],
\ },
@@ -146,9 +158,21 @@ Execute(tsserver syntax error responses should be handled correctly):
Execute(tsserver semantic error responses should be handled correctly):
runtime ale_linters/typescript/tsserver.vim
- call ale#test#SetFilename('filename.ts')
+
+ if has('win32')
+ call ale#test#SetFilename('filename,[]^$.ts')
+ else
+ call ale#test#SetFilename('filename*?,{}[]^$.ts')
+ endif
+
call ale#engine#InitBufferInfo(bufnr(''))
+ if has('win32')
+ AssertEqual 'filename,[]^$.ts', expand('%:p:t')
+ else
+ AssertEqual 'filename*?,{}[]^$.ts', expand('%:p:t')
+ endif
+
" When we get syntax errors and no semantic errors, we should keep the
" syntax errors.
call ale#lsp_linter#HandleLSPResponse(1, {
@@ -156,7 +180,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ 'type': 'event',
\ 'event': 'syntaxDiag',
\ 'body': {
- \ 'file': g:dir . '/filename.ts',
+ \ 'file': expand('%:p'),
\ 'diagnostics':[
\ ],
\ },
@@ -166,7 +190,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ 'type': 'event',
\ 'event': 'semanticDiag',
\ 'body': {
- \ 'file': g:dir . '/filename.ts',
+ \ 'file': expand('%:p'),
\ 'diagnostics':[
\ {
\ 'start': {
@@ -206,7 +230,7 @@ Execute(tsserver semantic error responses should be handled correctly):
\ 'type': 'event',
\ 'event': 'semanticDiag',
\ 'body': {
- \ 'file': g:dir . '/filename.ts',
+ \ 'file': expand('%:p'),
\ 'diagnostics':[
\ ],
\ },
@@ -270,15 +294,27 @@ Execute(tsserver errors should mark tsserver no longer active):
Execute(LSP diagnostics responses should be handled correctly):
let b:ale_linters = ['eclipselsp']
runtime ale_linters/java/eclipselsp.vim
- call ale#test#SetFilename('filename.java')
+
+ if has('win32')
+ call ale#test#SetFilename('filename,[]^$.ts')
+ else
+ call ale#test#SetFilename('filename*?,{}[]^$.java')
+ endif
+
call ale#engine#InitBufferInfo(bufnr(''))
call ale#lsp_linter#SetLSPLinterMap({'1': 'eclipselsp'})
+ if has('win32')
+ AssertEqual 'filename,[]^$.ts', expand('%:p:t')
+ else
+ AssertEqual 'filename*?,{}[]^$.java', expand('%:p:t')
+ endif
+
call ale#lsp_linter#HandleLSPResponse(1, {
\ 'jsonrpc':'2.0',
\ 'method':'textDocument/publishDiagnostics',
\ 'params': {
- \ 'uri':'file://' . g:dir . '/filename.java',
+ \ 'uri': ale#path#ToURI(expand('%:p')),
\ 'diagnostics': [
\ {
\ 'range': {
diff --git a/test/lsp/test_lsp_command_formatting.vader b/test/lsp/test_lsp_command_formatting.vader
index ec3b4120..e99e1dad 100644
--- a/test/lsp/test_lsp_command_formatting.vader
+++ b/test/lsp/test_lsp_command_formatting.vader
@@ -1,6 +1,10 @@
Before:
+ Save g:ale_command_wrapper
+
runtime autoload/ale/lsp.vim
+ let g:ale_command_wrapper = ''
+
let g:args = []
" Mock the StartProgram function so we can just capture the arguments.
@@ -9,6 +13,8 @@ Before:
endfunction
After:
+ Restore
+
unlet! g:args
runtime autoload/ale/lsp.vim
@@ -18,8 +24,8 @@ Execute(Command formatting should be applied correctly for LSP linters):
\ bufnr(''),
\ {
\ 'name': 'linter',
- \ 'language_callback': {-> 'x'},
- \ 'project_root_callback': {-> '/foo/bar'},
+ \ 'language': {-> 'x'},
+ \ 'project_root': {-> '/foo/bar'},
\ 'lsp': 'stdio',
\ 'executable': has('win32') ? 'cmd': 'true',
\ 'command': '%e --foo',
diff --git a/test/lsp/test_lsp_custom_request.vader b/test/lsp/test_lsp_custom_request.vader
index 04f044af..c8767e59 100644
--- a/test/lsp/test_lsp_custom_request.vader
+++ b/test/lsp/test_lsp_custom_request.vader
@@ -25,7 +25,6 @@ Before:
\ 'name': g:linter_name,
\ 'project_root': {b -> g:project_root},
\ 'aliases': [],
- \ 'language_callback': {b -> 'cpp'},
\ 'read_buffer': 1,
\ 'command': '%e'
\ }]
diff --git a/test/lsp/test_lsp_startup.vader b/test/lsp/test_lsp_startup.vader
index c29690bf..cd9b59dd 100644
--- a/test/lsp/test_lsp_startup.vader
+++ b/test/lsp/test_lsp_startup.vader
@@ -422,3 +422,13 @@ Execute(Deferred addresses should be handled correctly):
Assert Start()
call ale#test#FlushJobs()
call AssertInitSuccess('foo', 'localhost:1234', 'foobar', '/foo/bar', '')
+
+Execute(Servers that have crashed should be restarted):
+ call ale#lsp#Register('foo', '/foo/bar', {})
+ call extend(ale#lsp#GetConnections()['foo:/foo/bar'], {'initialized': 1})
+
+ " Starting the program again should reset initialized to `0`.
+ call ale#lsp#StartProgram('foo:/foo/bar', 'foobar', 'foobar --start')
+
+ AssertEqual 0, ale#lsp#GetConnections()['foo:/foo/bar']['initialized']
+ AssertEqual ['initialize'], map(PopMessages(), 'v:val[''method'']')
diff --git a/test/prettier-test-files/with_prettierignore/.prettierignore b/test/prettier-test-files/with_prettierignore/.prettierignore
new file mode 100644
index 00000000..e69de29b
diff --git a/test/prettier-test-files/with_prettierignore/src/testfile.js b/test/prettier-test-files/with_prettierignore/src/testfile.js
new file mode 100644
index 00000000..e69de29b
diff --git a/test/sign/test_linting_sets_signs.vader b/test/sign/test_linting_sets_signs.vader
index 1d1f9802..1624449a 100644
--- a/test/sign/test_linting_sets_signs.vader
+++ b/test/sign/test_linting_sets_signs.vader
@@ -10,7 +10,9 @@ Before:
Save g:ale_set_loclist
Save g:ale_set_quickfix
Save g:ale_set_signs
+ Save g:ale_command_wrapper
+ let g:ale_command_wrapper = ''
let g:ale_buffer_info = {}
let g:ale_run_synchronously = 1
unlet! g:ale_run_synchronously_callbacks
diff --git a/test/sign/test_sign_placement.vader b/test/sign/test_sign_placement.vader
index d8d05b28..7b80d83c 100644
--- a/test/sign/test_sign_placement.vader
+++ b/test/sign/test_sign_placement.vader
@@ -6,7 +6,9 @@ Before:
Save g:ale_set_loclist
Save g:ale_set_quickfix
Save g:ale_set_signs
+ Save g:ale_command_wrapper
+ let g:ale_command_wrapper = ''
let g:ale_buffer_info = {}
let g:ale_run_synchronously = 1
let g:ale_set_signs = 1
diff --git a/test/smoke_test.vader b/test/smoke_test.vader
index 53e08a8d..0b126cc6 100644
--- a/test/smoke_test.vader
+++ b/test/smoke_test.vader
@@ -1,8 +1,10 @@
Before:
+ Save g:ale_enabled
Save g:ale_set_lists_synchronously
Save g:ale_buffer_info
Save &shell
+ let g:ale_enabled = 1
let g:ale_buffer_info = {}
let g:ale_set_lists_synchronously = 1
diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader
index a69333d4..2f0a893f 100644
--- a/test/test_autocmd_commands.vader
+++ b/test/test_autocmd_commands.vader
@@ -92,8 +92,8 @@ Execute (All events should be set up when everything is on):
\ 'FileType * call ale#events#FileTypeEvent( str2nr(expand('''')), expand(''''))',
\ 'InsertLeave * if ale#Var(str2nr(expand('''')), ''lint_on_insert_leave'') | call ale#Queue(0) | endif',
\ 'InsertLeave if exists(''*ale#engine#Cleanup'') | call ale#cursor#EchoCursorWarning() | endif',
- \ 'TextChanged * call ale#Queue(g:ale_lint_delay)',
- \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)',
+ \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
+ \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
\ ],
\ CheckAutocmd('ALEEvents')
@@ -145,8 +145,8 @@ Execute (g:ale_lint_on_text_changed = 1 bind both events):
AssertEqual
\ [
- \ 'TextChanged * call ale#Queue(g:ale_lint_delay)',
- \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)',
+ \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
+ \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
\ ],
\ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''')
@@ -155,8 +155,8 @@ Execute (g:ale_lint_on_text_changed = 'always' should bind both events):
AssertEqual
\ [
- \ 'TextChanged * call ale#Queue(g:ale_lint_delay)',
- \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)',
+ \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
+ \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
\ ],
\ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''')
@@ -165,7 +165,7 @@ Execute (g:ale_lint_on_text_changed = 'normal' should bind only TextChanged):
AssertEqual
\ [
- \ 'TextChanged * call ale#Queue(g:ale_lint_delay)',
+ \ 'TextChanged * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
\ ],
\ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''')
@@ -174,7 +174,7 @@ Execute (g:ale_lint_on_text_changed = 'insert' should bind only TextChangedI):
AssertEqual
\ [
- \ 'TextChangedI * call ale#Queue(g:ale_lint_delay)',
+ \ 'TextChangedI * call ale#Queue(ale#Var(str2nr(expand('''')), ''lint_delay''))',
\ ],
\ filter(CheckAutocmd('ALEEvents'), 'v:val =~ ''^TextChanged''')
diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader
index 8ae6f9dc..99722b17 100644
--- a/test/test_c_flag_parsing.vader
+++ b/test/test_c_flag_parsing.vader
@@ -1,15 +1,43 @@
Before:
Save g:ale_c_parse_makefile
+ Save g:ale_c_always_make
+ Save b:ale_c_always_make
call ale#test#SetDirectory('/testplugin/test')
let g:ale_c_parse_makefile = 1
+ let g:ale_c_always_make = 1
+ let b:ale_c_always_make = 1
+
+ function SplitAndParse(path_prefix, command) abort
+ let l:args = ale#c#ShellSplit(a:command)
+
+ return ale#c#ParseCFlags(a:path_prefix, 0, l:args)
+ endfunction
After:
+ delfunction SplitAndParse
+
Restore
call ale#test#RestoreDirectory()
+Execute(The make command should be correct):
+ call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
+
+ AssertEqual
+ \ ale#path#CdString(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'))
+ \ . 'make -n --always-make',
+ \ ale#c#GetMakeCommand(bufnr(''))
+
+ " You should be able to disable --always-make for a buffer.
+ let b:ale_c_always_make = 0
+
+ AssertEqual
+ \ ale#path#CdString(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'))
+ \ . 'make -n',
+ \ ale#c#GetMakeCommand(bufnr(''))
+
Execute(The CFlags parser should be able to parse include directives):
call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c')
@@ -18,7 +46,7 @@ Execute(The CFlags parser should be able to parse include directives):
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c'])
AssertEqual
- \ '-isystem ' . '/usr/include/dir',
+ \ '-isystem ' . ale#Escape('/usr/include/dir'),
\ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -isystem /usr/include/dir -c file.c'])
Execute(ParseCFlags should ignore -c and -o):
@@ -57,48 +85,21 @@ Execute(ParseCFlags should be able to parse flags with relative paths):
\ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
\ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
+ \ SplitAndParse(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Isubdir '
\ . '-I'. ale#path#Simplify('kernel/include')
\ . ' -DTEST=`date +%s` -c file.c'
\ )
-Execute(ParseCFlags should be able to parse -Dgoal):
- AssertEqual
- \ '-Dgoal=9'
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
- \ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
- \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Isubdir '
- \ . '-I'. ale#path#Simplify('kernel/include')
- \ . ' -DTEST=`date +%s` -c file.c'
- \ )
-
-Execute(ParseCFlags should ignore -T and other arguments):
- AssertEqual
- \ '-Dgoal=9'
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . '--sysroot=subdir'
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
- \ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
- \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir --sysroot=subdir '
- \ . '-I'. ale#path#Simplify('kernel/include')
- \ . ' -DTEST=`date +%s` -c file.c'
- \ )
-
-Execute(ParseCFlags should handle paths with spaces in double quotes):
+Execute(We should handle paths with spaces in double quotes):
AssertEqual
\ '-Dgoal=9'
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
\ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
+ \ SplitAndParse(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include')
@@ -112,7 +113,7 @@ Execute(ParseCFlags should handle paths with spaces in single quotes):
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
\ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
+ \ SplitAndParse(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include')
@@ -127,7 +128,7 @@ Execute(ParseCFlags should handle paths with minuses):
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
\ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
+ \ SplitAndParse(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-I''dir with spaces''' . ' -Idir-with-dash'
@@ -135,7 +136,7 @@ Execute(ParseCFlags should handle paths with minuses):
\ . ' -DTEST=`date +%s` -c file.c'
\ )
-Execute(ParseCFlags should handle -D with minuses):
+Execute(We should handle -D with minuses):
AssertEqual
\ '-Dgoal=9'
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
@@ -144,7 +145,7 @@ Execute(ParseCFlags should handle -D with minuses):
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'))
\ . ' -DTEST=`date +%s`',
- \ ale#c#ParseCFlags(
+ \ SplitAndParse(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-Dmacro-with-dash '
@@ -153,7 +154,7 @@ Execute(ParseCFlags should handle -D with minuses):
\ . ' -DTEST=`date +%s` -c file.c'
\ )
-Execute(ParseCFlags should handle flags at the end of the line):
+Execute(We should handle flags at the end of the line):
AssertEqual
\ '-Dgoal=9'
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
@@ -161,7 +162,7 @@ Execute(ParseCFlags should handle flags at the end of the line):
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
\ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')),
- \ ale#c#ParseCFlags(
+ \ SplitAndParse(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . '-Dmacro-with-dash '
@@ -178,50 +179,170 @@ Execute(ParseCompileCommandsFlags should tolerate empty values):
Execute(ParseCompileCommandsFlags should parse some basic flags):
silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+ " We should read the absolute path filename entry, not the other ones.
AssertEqual
- \ '-I ' . ale#path#Simplify('/usr/include/xmms2'),
- \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [
+ \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')),
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
\ {
- \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
- \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
- \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
- \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
- \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'): [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ },
+ \ ],
+ \ "xmms2-mpris.c": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ },
+ \ ],
\ },
- \ ] }, {})
+ \ {
+ \ ale#path#Simplify('/foo/bar/xmms2-mpris/src'): [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris/src'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': 'other.c',
+ \ },
+ \ ],
+ \ "src": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'),
+ \ },
+ \ ],
+ \ },
+ \ )
+
+Execute(ParseCompileCommandsFlags should fall back to files with the same name):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+
+ " We should prefer the basename file flags, not the base dirname flags.
+ AssertEqual
+ \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')),
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {
+ \ "xmms2-mpris.c": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ "src": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'),
+ \ },
+ \ ],
+ \ },
+ \ )
+
+Execute(ParseCompileCommandsFlags should parse flags for exact directory matches):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+
+ " We should ues the exact directory flags, not the file basename flags.
+ AssertEqual
+ \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')),
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {
+ \ "xmms2-mpris.c": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ },
+ \ ],
+ \ },
+ \ {
+ \ ale#path#Simplify('/foo/bar/xmms2-mpris/src'): [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris/src'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': 'other.c',
+ \ },
+ \ ],
+ \ "src": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/ignoreme')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'),
+ \ },
+ \ ],
+ \ },
+ \ )
+
+Execute(ParseCompileCommandsFlags should fall back to files in the same directory):
+ silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
+
+ AssertEqual
+ \ '-I ' . ale#Escape(ale#path#Simplify('/usr/include/xmms2')),
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
+ \ {},
+ \ {
+ \ "src": [
+ \ {
+ \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
+ \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
+ \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
+ \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
+ \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'),
+ \ },
+ \ ],
+ \ },
+ \ )
Execute(ParseCompileCommandsFlags should tolerate items without commands):
silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
AssertEqual
\ '',
- \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [
+ \ ale#c#ParseCompileCommandsFlags(
+ \ bufnr(''),
\ {
- \ 'directory': '/foo/bar/xmms2-mpris',
- \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ "xmms2-mpris.c": [
+ \ {
+ \ 'directory': '/foo/bar/xmms2-mpris',
+ \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c',
+ \ },
+ \ ],
\ },
- \ ] }, {})
-
-Execute(ParseCompileCommandsFlags should fall back to files in the same directory):
- silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'))
-
- AssertEqual
- \ '-I ' . ale#path#Simplify('/usr/include/xmms2'),
- \ ale#c#ParseCompileCommandsFlags(bufnr(''), {}, { "src": [
- \ {
- \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'),
- \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2')
- \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o'
- \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'),
- \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'),
- \ },
- \ ] })
+ \ {},
+ \ )
Execute(ParseCompileCommandsFlags should take commands from matching .c files for .h files):
silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h'))
AssertEqual
- \ '-I /usr/include/xmms2',
+ \ '-I ' . ale#Escape('/usr/include/xmms2'),
\ ale#c#ParseCompileCommandsFlags(
\ bufnr(''),
\ {
@@ -235,15 +356,14 @@ Execute(ParseCompileCommandsFlags should take commands from matching .c files fo
\ },
\ ],
\ },
- \ {
- \ },
+ \ {},
\ )
Execute(ParseCompileCommandsFlags should take commands from matching .cpp files for .hpp files):
silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.hpp'))
AssertEqual
- \ '-I /usr/include/xmms2',
+ \ '-I ' . ale#Escape('/usr/include/xmms2'),
\ ale#c#ParseCompileCommandsFlags(
\ bufnr(''),
\ {
@@ -265,7 +385,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .cpp files
silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h'))
AssertEqual
- \ '-I /usr/include/xmms2',
+ \ '-I ' . ale#Escape('/usr/include/xmms2'),
\ ale#c#ParseCompileCommandsFlags(
\ bufnr(''),
\ {
@@ -305,42 +425,69 @@ Execute(ParseCompileCommandsFlags should not take commands from .c files for .h
\ },
\ )
-Execute(ParseCFlags should not merge flags):
+Execute(ShellSplit should not merge flags):
AssertEqual
- \ '-Dgoal=9'
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'))
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces'))
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash'))
- \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')),
- \ ale#c#ParseCFlags(
- \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
+ \ [
+ \ 'gcc',
+ \ '-Dgoal=9',
+ \ '-Tlinkerfile.ld',
+ \ 'blabla',
+ \ '-Isubdir',
+ \ 'subdir/somedep1.o',
+ \ 'subdir/somedep2.o',
+ \ '-I''dir with spaces''',
+ \ '-Idir-with-dash',
+ \ 'subdir/somedep3.o',
+ \ 'subdir/somedep4.o',
+ \ '-I' . ale#path#Simplify('kernel/include'),
+ \ 'subdir/somedep5.o',
+ \ 'subdir/somedep6.o',
+ \ ],
+ \ ale#c#ShellSplit(
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir '
\ . 'subdir/somedep1.o ' . 'subdir/somedep2.o '
\ . '-I''dir with spaces''' . ' -Idir-with-dash '
\ . 'subdir/somedep3.o ' . 'subdir/somedep4.o '
\ . ' -I'. ale#path#Simplify('kernel/include') . ' '
- \ . 'subdir/somedep5.o ' . 'subdir/somedep6.o '
+ \ . 'subdir/somedep5.o ' . 'subdir/somedep6.o'
\ )
-Execute(ParseCFlags should handle parenthesis and quotes):
+Execute(ShellSplit should handle parenthesis and quotes):
AssertEqual
- \ '-Dgoal=9 -Dtest1="('' '')" -Dtest2=''(` `)'' -Dtest3=`(" ")`',
- \ ale#c#ParseCFlags(
- \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
+ \ [
+ \ 'gcc',
+ \ '-Dgoal=9',
+ \ '-Tlinkerfile.ld',
+ \ 'blabla',
+ \ '-Dtest1="('' '')"',
+ \ 'file1.o',
+ \ '-Dtest2=''(` `)''',
+ \ 'file2.o',
+ \ '-Dtest3=`(" ")`',
+ \ 'file3.o',
+ \ ] ,
+ \ ale#c#ShellSplit(
\ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla '
\ . '-Dtest1="('' '')" file1.o '
\ . '-Dtest2=''(` `)'' file2.o '
- \ . '-Dtest3=`(" ")` file3.o '
+ \ . '-Dtest3=`(" ")` file3.o'
\ )
-Execute(CFlags we want to pass):
+Execute(We should include several important flags):
AssertEqual
- \ '-I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/inc'))
- \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/include'))
- \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incquote'))
- \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incsystem'))
+ \ '-I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/inc'))
+ \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include'))
+ \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incquote'))
+ \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incsystem'))
\ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter'))
- \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2'
+ \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incframework'))
+ \ . ' -include ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/foo bar'))
+ \ . ' -Dmacro="value"'
+ \ . ' -DGoal=9'
+ \ . ' -D macro2'
+ \ . ' -D macro3="value"'
+ \ . ' -Bbdir'
+ \ . ' -B bdir2'
\ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3'
\ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir'
\ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi'
@@ -348,21 +495,175 @@ Execute(CFlags we want to pass):
\ . ' -iplugindir=dir -march=native -w',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc'
- \ . ' -Iinc -I include -iquote incquote -isystem incsystem -idirafter incafter'
- \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2'
- \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3'
- \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir'
- \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi'
- \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++'
- \ . ' -iplugindir=dir -march=native -w'
+ \ 0,
+ \ [
+ \ 'gcc',
+ \ '-Iinc',
+ \ '-I',
+ \ 'include',
+ \ '-iquote',
+ \ 'incquote',
+ \ '-isystem',
+ \ 'incsystem',
+ \ '-idirafter',
+ \ 'incafter',
+ \ '-iframework',
+ \ 'incframework',
+ \ '-include',
+ \ '''foo bar''',
+ \ '-Dmacro="value"',
+ \ '-DGoal=9',
+ \ '-D',
+ \ 'macro2',
+ \ '-D',
+ \ 'macro3="value"',
+ \ '-Bbdir',
+ \ '-B',
+ \ 'bdir2',
+ \ '-iprefix',
+ \ 'prefix',
+ \ '-iwithprefix',
+ \ 'prefix2',
+ \ '-iwithprefixbefore',
+ \ 'prefix3',
+ \ '-isysroot',
+ \ 'sysroot',
+ \ '--sysroot=test',
+ \ '--no-sysroot-suffix',
+ \ '-imultilib',
+ \ 'multidir',
+ \ '-Wsome-warning',
+ \ '-std=c89',
+ \ '-pedantic',
+ \ '-pedantic-errors',
+ \ '-ansi',
+ \ '-foption',
+ \ '-O2',
+ \ '-C',
+ \ '-CC',
+ \ '-trigraphs',
+ \ '-nostdinc',
+ \ '-nostdinc++',
+ \ '-iplugindir=dir',
+ \ '-march=native',
+ \ '-w',
+ \ ],
\ )
-Execute(CFlags we dont want to pass):
+Execute(We should quote the flags we need to quote):
+ AssertEqual
+ \ '-I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/inc'))
+ \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/include'))
+ \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incquote'))
+ \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incsystem'))
+ \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter'))
+ \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incframework'))
+ \ . ' -include ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/foo bar'))
+ \ . ' ' . ale#Escape('-Dmacro="value"')
+ \ . ' -DGoal=9'
+ \ . ' -D macro2'
+ \ . ' -D ' . ale#Escape('macro3="value"')
+ \ . ' -Bbdir'
+ \ . ' -B bdir2'
+ \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3'
+ \ . ' -isysroot sysroot --sysroot=test'
+ \ . ' ' . ale#Escape('--sysroot="quoted"')
+ \ . ' ' . ale#Escape('--sysroot=foo bar')
+ \ . ' --no-sysroot-suffix -imultilib multidir'
+ \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi'
+ \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++'
+ \ . ' -iplugindir=dir -march=native -w',
+ \ ale#c#ParseCFlags(
+ \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
+ \ 1,
+ \ [
+ \ 'gcc',
+ \ '-Iinc',
+ \ '-I',
+ \ 'include',
+ \ '-iquote',
+ \ 'incquote',
+ \ '-isystem',
+ \ 'incsystem',
+ \ '-idirafter',
+ \ 'incafter',
+ \ '-iframework',
+ \ 'incframework',
+ \ '-include',
+ \ '''foo bar''',
+ \ '-Dmacro="value"',
+ \ '-DGoal=9',
+ \ '-D',
+ \ 'macro2',
+ \ '-D',
+ \ 'macro3="value"',
+ \ '-Bbdir',
+ \ '-B',
+ \ 'bdir2',
+ \ '-iprefix',
+ \ 'prefix',
+ \ '-iwithprefix',
+ \ 'prefix2',
+ \ '-iwithprefixbefore',
+ \ 'prefix3',
+ \ '-isysroot',
+ \ 'sysroot',
+ \ '--sysroot=test',
+ \ '--sysroot="quoted"',
+ \ '--sysroot=foo bar',
+ \ '--no-sysroot-suffix',
+ \ '-imultilib',
+ \ 'multidir',
+ \ '-Wsome-warning',
+ \ '-std=c89',
+ \ '-pedantic',
+ \ '-pedantic-errors',
+ \ '-ansi',
+ \ '-foption',
+ \ '-O2',
+ \ '-C',
+ \ '-CC',
+ \ '-trigraphs',
+ \ '-nostdinc',
+ \ '-nostdinc++',
+ \ '-iplugindir=dir',
+ \ '-march=native',
+ \ '-w',
+ \ ],
+ \ )
+
+Execute(We should exclude other flags that cause problems):
AssertEqual
\ '',
\ ale#c#ParseCFlags(
\ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
- \ 'gcc -Wl,option -Wa,option -Wp,option filename.c somelib.a '
- \ . '-fdump-file=name -fdiagnostics-arg -fno-show-column'
+ \ 0,
+ \ [
+ \ 'gcc',
+ \ '-Wl,option',
+ \ '-Wa,option',
+ \ '-Wp,option',
+ \ '-c',
+ \ 'filename.c',
+ \ 'somelib.a',
+ \ '-fdump-file=name',
+ \ '-fdiagnostics-arg',
+ \ '-fno-show-column',
+ \ '-fstack-usage',
+ \ '-Tlinkerfile.ld',
+ \ ],
+ \ )
+
+Execute(We should expand @file in CFlags):
+ AssertEqual
+ \ '-DARGS1 -DARGS2 -O2',
+ \ ale#c#ParseCFlags(
+ \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'),
+ \ 0,
+ \ [
+ \ 'gcc',
+ \ '-g',
+ \ '@./args',
+ \ '-O2',
+ \ ],
\ )
diff --git a/test/test_c_projects/makefile_project/args b/test/test_c_projects/makefile_project/args
new file mode 100644
index 00000000..ccaf82ad
--- /dev/null
+++ b/test/test_c_projects/makefile_project/args
@@ -0,0 +1,3 @@
+foolib.a
+-DARGS1
+@subdir/args
diff --git a/test/test_c_projects/makefile_project/subdir/args b/test/test_c_projects/makefile_project/subdir/args
new file mode 100644
index 00000000..3fe9c3fe
--- /dev/null
+++ b/test/test_c_projects/makefile_project/subdir/args
@@ -0,0 +1 @@
+-DARGS2
diff --git a/test/test_code_action.vader b/test/test_code_action.vader
index b47f24ff..19de7268 100644
--- a/test/test_code_action.vader
+++ b/test/test_code_action.vader
@@ -1,4 +1,8 @@
Before:
+ Save g:ale_enabled
+
+ let g:ale_enabled = 0
+
runtime autoload/ale/code_action.vim
runtime autoload/ale/util.vim
@@ -35,6 +39,8 @@ Before:
endfunction!
After:
+ Restore
+
" Close the extra buffers if we opened it.
if bufnr(g:file1) != -1
execute ':bp! | :bd! ' . bufnr(g:file1)
@@ -50,9 +56,10 @@ After:
call delete(g:file2)
endif
- unlet g:file1
- unlet g:file2
- unlet g:test
+ unlet! g:file1
+ unlet! g:file2
+ unlet! g:test
+ unlet! g:changes
delfunction WriteFileAndEdit
runtime autoload/ale/code_action.vim
@@ -350,3 +357,36 @@ Execute(It should just modify file when should_save is set to v:false):
\ ' value: string',
\ '}',
\], getline(1, '$')
+
+Given typescript(An example TypeScript file):
+ type Foo = {}
+
+ export interface ISomething {
+ fooLongName: Foo | null
+ }
+
+ export class SomethingElse implements ISomething {
+ // Bindings
+ fooLongName!: ISomething['fooLongName']
+ }
+
+Execute():
+ let g:changes = [
+ \ {'end': {'offset': 14, 'line': 4}, 'newText': 'foo', 'start': {'offset': 3, 'line': 4}},
+ \ {'end': {'offset': 40, 'line': 9}, 'newText': 'foo', 'start': {'offset': 29, 'line': 9}},
+ \ {'end': {'offset': 14, 'line': 9}, 'newText': 'foo', 'start': {'offset': 3, 'line': 9}},
+ \]
+
+ call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0)
+
+Expect(The changes should be applied correctly):
+ type Foo = {}
+
+ export interface ISomething {
+ foo: Foo | null
+ }
+
+ export class SomethingElse implements ISomething {
+ // Bindings
+ foo!: ISomething['foo']
+ }
diff --git a/test/test_command_chain.vader b/test/test_command_chain.vader
deleted file mode 100644
index 329ebc97..00000000
--- a/test/test_command_chain.vader
+++ /dev/null
@@ -1,73 +0,0 @@
-Before:
- Save &shell, g:ale_run_synchronously
- let g:ale_run_synchronously = 1
- unlet! g:ale_run_synchronously_callbacks
-
- if !has('win32')
- set shell=/bin/sh
- endif
-
- let g:linter_output = []
- let g:first_echo_called = 0
- let g:second_echo_called = 0
- let g:final_callback_called = 0
-
- function! CollectResults(buffer, output)
- let g:final_callback_called = 1
- let g:linter_output = map(copy(a:output), 'join(split(v:val))')
- return []
- endfunction
- function! RunFirstEcho(buffer)
- let g:first_echo_called = 1
-
- return 'echo foo'
- endfunction
- function! RunSecondEcho(buffer, output)
- let g:second_echo_called = 1
-
- return 'echo bar'
- endfunction
-
- call ale#linter#Define('foobar', {
- \ 'name': 'testlinter',
- \ 'callback': 'CollectResults',
- \ 'executable': has('win32') ? 'cmd' : 'echo',
- \ 'command_chain': [
- \ {
- \ 'callback': 'RunFirstEcho',
- \ 'output_stream': 'stdout',
- \ 'read_buffer': 0,
- \ },
- \ {
- \ 'callback': 'RunSecondEcho',
- \ 'output_stream': 'stdout',
- \ 'read_buffer': 0,
- \ },
- \ ],
- \})
-
-After:
- Restore
- unlet! g:ale_run_synchronously_callbacks
- unlet! g:first_echo_called
- unlet! g:second_echo_called
- unlet! g:final_callback_called
- unlet! g:linter_output
- let g:ale_buffer_info = {}
- call ale#linter#Reset()
- delfunction CollectResults
- delfunction RunFirstEcho
- delfunction RunSecondEcho
-
-Given foobar (Some imaginary filetype):
- anything
-
-Execute(Check the results of running the chain):
- AssertEqual 'foobar', &filetype
- call ale#Queue(0)
- call ale#test#FlushJobs()
-
- Assert g:first_echo_called, 'The first chain item was not called'
- Assert g:second_echo_called, 'The second chain item was not called'
- Assert g:final_callback_called, 'The final callback was not called'
- AssertEqual ['bar'], g:linter_output
diff --git a/test/test_computed_lint_file_values.vader b/test/test_computed_lint_file_values.vader
new file mode 100644
index 00000000..399e96fe
--- /dev/null
+++ b/test/test_computed_lint_file_values.vader
@@ -0,0 +1,134 @@
+Before:
+ Save g:ale_enabled
+ Save g:ale_run_synchronously
+ Save g:ale_set_lists_synchronously
+ Save g:ale_buffer_info
+
+ let g:ale_enabled = 1
+ let g:ale_buffer_info = {}
+ let g:ale_run_synchronously = 1
+ let g:ale_set_lists_synchronously = 1
+
+ function! TestCallback(buffer, output)
+ " Windows adds extra spaces to the text from echo.
+ return [{
+ \ 'lnum': 2,
+ \ 'col': 3,
+ \ 'text': 'testlinter1',
+ \}]
+ endfunction
+ function! TestCallback2(buffer, output)
+ " Windows adds extra spaces to the text from echo.
+ return [{
+ \ 'lnum': 1,
+ \ 'col': 3,
+ \ 'text': 'testlinter2',
+ \}]
+ endfunction
+ function! TestCallback3(buffer, output)
+ " Windows adds extra spaces to the text from echo.
+ return [{
+ \ 'lnum': 3,
+ \ 'col': 3,
+ \ 'text': 'testlinter3',
+ \}]
+ endfunction
+
+ " These two linters computer their lint_file values after running commands.
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter1',
+ \ 'callback': 'TestCallback',
+ \ 'executable': has('win32') ? 'cmd' : 'echo',
+ \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''',
+ \ 'lint_file': {b -> ale#command#Run(b, 'echo', {-> 1})},
+ \})
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter2',
+ \ 'callback': 'TestCallback2',
+ \ 'executable': has('win32') ? 'cmd' : 'echo',
+ \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''',
+ \ 'lint_file': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 1})})},
+ \})
+ " This one directly computes the result.
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter3',
+ \ 'callback': 'TestCallback3',
+ \ 'executable': has('win32') ? 'cmd' : 'echo',
+ \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''',
+ \ 'lint_file': {b -> 1},
+ \})
+
+ let g:filename = tempname()
+ call writefile([], g:filename)
+ call ale#test#SetFilename(g:filename)
+
+After:
+ delfunction TestCallback
+
+ call ale#engine#Cleanup(bufnr(''))
+ Restore
+ call ale#linter#Reset()
+
+ " Items and markers, etc.
+ call setloclist(0, [])
+ call clearmatches()
+ call ale#sign#Clear()
+
+ if filereadable(g:filename)
+ call delete(g:filename)
+ endif
+
+ unlet g:filename
+
+Given foobar(A file with some lines):
+ foo
+ bar
+ baz
+
+Execute(lint_file results where the result is eventually computed should be run):
+ call ale#Queue(0, 'lint_file')
+ call ale#test#FlushJobs()
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'bufnr': bufnr('%'),
+ \ 'lnum': 1,
+ \ 'vcol': 0,
+ \ 'col': 3,
+ \ 'text': 'testlinter2',
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'pattern': '',
+ \ 'valid': 1,
+ \ },
+ \ {
+ \ 'bufnr': bufnr('%'),
+ \ 'lnum': 2,
+ \ 'vcol': 0,
+ \ 'col': 3,
+ \ 'text': 'testlinter1',
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'pattern': '',
+ \ 'valid': 1,
+ \ },
+ \ {
+ \ 'bufnr': bufnr('%'),
+ \ 'lnum': 3,
+ \ 'vcol': 0,
+ \ 'col': 3,
+ \ 'text': 'testlinter3',
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'pattern': '',
+ \ 'valid': 1,
+ \ },
+ \ ],
+ \ ale#test#GetLoclistWithoutModule()
+
+Execute(Linters where lint_file eventually evaluates to 1 shouldn't be run if we don't want to run them):
+ call ale#Queue(0, '')
+ call ale#test#FlushJobs()
+
+ AssertEqual [], ale#test#GetLoclistWithoutModule()
diff --git a/test/test_cursor_warnings.vader b/test/test_cursor_warnings.vader
index 339cd71e..ef385061 100644
--- a/test/test_cursor_warnings.vader
+++ b/test/test_cursor_warnings.vader
@@ -30,7 +30,7 @@ Before:
\ 'nr': -1,
\ 'type': 'E',
\ 'code': 'semi',
- \ 'text': 'Missing semicolon.',
+ \ 'text': "Missing semicolon.\r",
\ 'detail': "Every statement should end with a semicolon\nsecond line",
\ },
\ {
diff --git a/test/test_deferred_command_string.vader b/test/test_deferred_command_string.vader
index 026be6fe..173b6bb2 100644
--- a/test/test_deferred_command_string.vader
+++ b/test/test_deferred_command_string.vader
@@ -12,7 +12,7 @@ Before:
call ale#linter#Define('foobar', {
\ 'name': 'lint_file_linter',
\ 'callback': 'LintFileCallback',
- \ 'executable': 'echo',
+ \ 'executable': has('win32') ? 'cmd' : 'echo',
\ 'command': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 'foo'})})},
\ 'read_buffer': 0,
\})
@@ -28,7 +28,7 @@ After:
Given foobar (Some imaginary filetype):
Execute(It should be possible to compute an executable to check based on the result of commands):
- AssertLinter 'echo', 'foo'
+ AssertLinter has('win32') ? 'cmd' : 'echo', 'foo'
ALELint
call ale#test#FlushJobs()
@@ -40,7 +40,7 @@ Execute(It should be possible to compute an executable to check based on the res
Execute(It handle the deferred command failing):
let g:ale_emulate_job_failure = 1
- AssertLinter 'echo', 0
+ AssertLinter has('win32') ? 'cmd' : 'echo', 0
ALELint
call ale#test#FlushJobs()
diff --git a/test/test_engine_invocation.vader b/test/test_engine_invocation.vader
deleted file mode 100644
index af713953..00000000
--- a/test/test_engine_invocation.vader
+++ /dev/null
@@ -1,108 +0,0 @@
-Before:
- function! CollectResults(buffer, output)
- return []
- endfunction
-
- function! FirstChainFunction(buffer)
- return 'first'
- endfunction
-
- function! SecondChainFunction(buffer, output)
- " We'll skip this command
- return ''
- endfunction
-
- function! ThirdChainFunction(buffer, output)
- return 'third'
- endfunction
-
- function! FourthChainFunction(buffer, output)
- return 'fourth'
- endfunction
-
- let g:linter = {
- \ 'name': 'testlinter',
- \ 'callback': 'CollectResults',
- \ 'executable': 'echo',
- \ 'command_chain': [
- \ {'callback': 'FirstChainFunction'},
- \ {'callback': 'SecondChainFunction'},
- \ {'callback': 'ThirdChainFunction'},
- \ {'callback': 'FourthChainFunction'},
- \ ],
- \ 'read_buffer': 1,
- \}
-
- function! ProcessIndex(chain_index)
- let [l:command, l:options] = ale#engine#ProcessChain(347, '', g:linter, a:chain_index, [])
- let l:options.command = l:command
-
- return l:options
- endfunction
-
-After:
- delfunction CollectResults
- delfunction FirstChainFunction
- delfunction SecondChainFunction
- delfunction ThirdChainFunction
- delfunction ProcessIndex
- unlet! g:linter
- unlet! g:result
-
-Execute(Engine invocation should return the command for the first item correctly):
- let g:result = ProcessIndex(0)
-
- AssertEqual 'first', g:result.command
- AssertEqual 1, g:result.next_chain_index
-
-Execute(Engine invocation should return the command for the second item correctly):
- let g:result = ProcessIndex(1)
-
- AssertEqual 'third', g:result.command
- AssertEqual 3, g:result.next_chain_index
-
-Execute(Engine invocation should return the command for the fourth item correctly):
- let g:result = ProcessIndex(3)
-
- AssertEqual 'fourth', g:result.command
- AssertEqual 4, g:result.next_chain_index
-
-Execute(Engine invocation should allow read_buffer to be enabled for a command in the middle of a chain):
- let g:linter.command_chain[2].read_buffer = 1
-
- let g:result = ProcessIndex(2)
-
- AssertEqual g:result.command, 'third'
- AssertEqual g:result.read_buffer, 1
-
-Execute(Engine invocation should allow read_buffer to be disabled for the end of a chain):
- let g:linter.command_chain[3].read_buffer = 0
-
- let g:result = ProcessIndex(3)
-
- AssertEqual g:result.command, 'fourth'
- AssertEqual g:result.read_buffer, 0
-
-Execute(Engine invocation should not use read_buffer from earlier items in a chain):
- let g:linter.command_chain[1].read_buffer = 1
-
- let g:result = ProcessIndex(1)
-
- AssertEqual g:result.command, 'third'
- AssertEqual g:result.read_buffer, 0
-
-Execute(Engine invocation should allow the output_stream setting to be changed in the middle of a chain):
- let g:linter.command_chain[2].output_stream = 'both'
-
- let g:result = ProcessIndex(2)
-
- AssertEqual g:result.command, 'third'
- AssertEqual g:result.output_stream, 'both'
-
-Execute(Engine invocation should not use output_stream from earlier items in a chain):
- let g:linter.command_chain[1].output_stream = 'both'
-
- let g:result = ProcessIndex(1)
-
- AssertEqual g:result.command, 'third'
- AssertEqual g:result.output_stream, 'stdout'
diff --git a/test/test_filename_mapping.vader b/test/test_filename_mapping.vader
new file mode 100644
index 00000000..e9af539a
--- /dev/null
+++ b/test/test_filename_mapping.vader
@@ -0,0 +1,62 @@
+Before:
+ Save g:ale_filename_mappings
+ Save b:ale_filename_mappings
+
+ let g:ale_filename_mappings = {}
+ unlet! b:ale_filename_mappings
+
+After:
+ Restore
+
+Execute(ale#GetFilenameMappings should return the correct mappings for given linters/fixers):
+ let g:ale_filename_mappings = {'a': [['foo', 'bar']], 'b': [['baz', 'foo']]}
+
+ AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a')
+ AssertEqual [['baz', 'foo']], ale#GetFilenameMappings(bufnr(''), 'b')
+ AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'c')
+
+ let b:ale_filename_mappings = {'b': [['abc', 'xyz']]}
+
+ AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'a')
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'b')
+ AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'c')
+
+Execute(ale#GetFilenameMappings should return Lists set for use with all tools):
+ let g:ale_filename_mappings = [['foo', 'bar']]
+
+ AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a')
+ AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), '')
+ AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), v:null)
+
+ let b:ale_filename_mappings = [['abc', 'xyz']]
+
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'a')
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), '')
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), v:null)
+
+Execute(ale#GetFilenameMappings should let you use * as a fallback):
+ let g:ale_filename_mappings = {'a': [['foo', 'bar']], '*': [['abc', 'xyz']]}
+
+ AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a')
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'b')
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), '')
+ AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), v:null)
+
+Execute(ale#filename_mapping#Invert should invert filename mappings):
+ AssertEqual
+ \ [['b', 'a'], ['y', 'x']],
+ \ ale#filename_mapping#Invert([['a', 'b'], ['x', 'y']])
+ \
+Execute(ale#filename_mapping#Map return the filename as-is if there are no mappings):
+ AssertEqual
+ \ '/foo//bar',
+ \ ale#filename_mapping#Map('/foo//bar', [['/bar', '/data/']])
+
+Execute(ale#filename_mapping#Map should map filenames):
+ AssertEqual
+ \ '/data/bar',
+ \ ale#filename_mapping#Map('/foo//bar', [
+ \ ['/data/', '/baz/'],
+ \ ['/foo/', '/data/'],
+ \ ['/foo/', '/xyz/'],
+ \ ])
diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader
index 842cc394..e9980536 100644
--- a/test/test_filetype_linter_defaults.vader
+++ b/test/test_filetype_linter_defaults.vader
@@ -39,7 +39,7 @@ Execute(The defaults for the python filetype should be correct):
AssertEqual [], GetLinterNames('python')
Execute(The defaults for the rust filetype should be correct):
- AssertEqual ['cargo'], GetLinterNames('rust')
+ AssertEqual ['cargo', 'rls'], GetLinterNames('rust')
let g:ale_linters_explicit = 1
diff --git a/test/test_find_references.vader b/test/test_find_references.vader
index 9949362a..ca05f631 100644
--- a/test/test_find_references.vader
+++ b/test/test_find_references.vader
@@ -63,6 +63,8 @@ Before:
let g:preview_called = 1
let g:item_list = a:item_list
let g:options = a:options
+
+ call ale#preview#SetLastSelection(a:item_list, a:options)
endfunction
After:
@@ -110,7 +112,16 @@ Given typescript(Some typescript file):
bazxyzxyzxyz
Execute(Results should be shown for tsserver responses):
- call ale#references#SetMap({3: {}})
+ " We should remember these options when we repeat the selection.
+ call ale#references#SetMap(
+ \ {
+ \ 3: {
+ \ 'ignorethis': 'x',
+ \ 'open_in': 'tab',
+ \ 'use_relative_paths': 1,
+ \ }
+ \ }
+ \)
call ale#references#HandleTSServerResponse(1, {
\ 'command': 'references',
\ 'request_seq': 3,
@@ -158,8 +169,7 @@ Execute(Results should be shown for tsserver responses):
AssertEqual {}, ale#references#GetMap()
" We should be able to repeat selections with ALERepeatSelection
- let g:ale_item_list = []
-
+ let g:item_list = []
ALERepeatSelection
AssertEqual
@@ -170,6 +180,12 @@ Execute(Results should be shown for tsserver responses):
\ ],
\ g:item_list
AssertEqual {}, ale#references#GetMap()
+ AssertEqual
+ \ {
+ \ 'open_in': 'tab',
+ \ 'use_relative_paths': 1,
+ \ },
+ \ g:options
Execute(The preview window should not be opened for empty tsserver responses):
call ale#references#SetMap({3: {}})
diff --git a/test/test_format_command.vader b/test/test_format_command.vader
index 15435326..9d730fce 100644
--- a/test/test_format_command.vader
+++ b/test/test_format_command.vader
@@ -25,12 +25,12 @@ After:
Execute(FormatCommand should do nothing to basic command strings):
AssertEqual
\ ['', 'awesome-linter do something', 0],
- \ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null, [])
Execute(FormatCommand should handle %%, and ignore other percents):
AssertEqual
\ ['', '% %%d %%f %x %', 0],
- \ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null, [])
Execute(FormatCommand should convert %s to the current filename):
AssertEqual
@@ -39,10 +39,10 @@ Execute(FormatCommand should convert %s to the current filename):
\ 'foo ' . ale#Escape(expand('%:p')) . ' bar ' . ale#Escape(expand('%:p')),
\ 0,
\ ],
- \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null, [])
Execute(FormatCommand should convert %t to a new temporary filename):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:null)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:null, [])
call CheckTempFile(g:result[0])
@@ -56,21 +56,21 @@ Execute(FormatCommand should convert %t to a new temporary filename):
AssertEqual g:match[1], g:match[2]
Execute(FormatCommand should not convert %t to a new temporary filename when the input is given as v:false):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false, [])
AssertEqual ['', 'foo %t bar %t', 0], g:result
Execute(FormatCommand should signal that files are created when temporary files are needed):
AssertEqual
\ 1,
- \ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null)[2]
+ \ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null, [])[2]
AssertEqual
\ 0,
- \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null)[2]
+ \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null, [])[2]
Execute(FormatCommand should let you combine %s and %t):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0, v:null)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0, v:null, [])
call CheckTempFile(g:result[0])
@@ -87,30 +87,30 @@ Execute(FormatCommand should replace %e with the escaped executable):
if has('win32')
AssertEqual
\ ['', 'foo foo', 0],
- \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null, [])
AssertEqual
\ ['', '"foo bar"', 0],
- \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null, [])
AssertEqual
\ ['', '%e %e', 0],
- \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null, [])
else
AssertEqual
\ ['', '''foo'' ''foo''', 0],
- \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null, [])
AssertEqual
\ ['', '''foo bar''', 0],
- \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null, [])
AssertEqual
\ ['', '%e %e', 0],
- \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null)
+ \ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null, [])
endif
Execute(EscapeCommandPart should escape all percent signs):
AssertEqual '%%s %%t %%%% %%s %%t %%%%', ale#engine#EscapeCommandPart('%s %t %% %s %t %%')
Execute(EscapeCommandPart should pipe in temporary files appropriately):
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1, v:null)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1, v:null, [])
call CheckTempFile(g:result[0])
@@ -118,10 +118,57 @@ Execute(EscapeCommandPart should pipe in temporary files appropriately):
Assert !empty(g:match), 'No match found! Result was: ' . g:result[1]
AssertEqual ale#Escape(g:result[0]), g:match[1]
- let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1, v:null)
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1, v:null, [])
call CheckTempFile(g:result[0])
let g:match = matchlist(g:result[1], '\v^foo bar (.*)$')
Assert !empty(g:match), 'No match found! Result was: ' . g:result[1]
AssertEqual ale#Escape(g:result[0]), g:match[1]
+
+Execute(FormatCommand should apply filename modifiers to the current file):
+ AssertEqual
+ \ ale#Escape(expand('%:p:h'))
+ \ . ' ' . ale#Escape('dummy.txt')
+ \ . ' ' . ale#Escape(expand('%:p:h:t'))
+ \ . ' ' . ale#Escape('txt')
+ \ . ' ' . ale#Escape(expand('%:p:r')),
+ \ ale#command#FormatCommand(bufnr(''), '', '%s:h %s:t %s:h:t %s:e %s:r', 0, v:null, [])[1]
+
+Execute(FormatCommand should apply filename modifiers to the temporary file):
+ let g:result = ale#command#FormatCommand(bufnr(''), '', '%t:h %t:t %t:h:t %t:e %t:r', 0, v:null, [])
+
+ AssertEqual
+ \ ale#Escape(fnamemodify(g:result[0], ':h'))
+ \ . ' ' . ale#Escape('dummy.txt')
+ \ . ' ' . ale#Escape(fnamemodify(g:result[0], ':h:t'))
+ \ . ' ' . ale#Escape('txt')
+ \ . ' ' . ale#Escape(fnamemodify(g:result[0], ':r')),
+ \ g:result[1]
+
+Execute(FormatCommand should apply filename mappings the current file):
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', '%s', 0, v:null, [
+ \ [expand('%:p:h'), '/foo/bar'],
+ \])
+
+ Assert g:result[1] =~# '/foo/bar'
+
+Execute(FormatCommand should apply filename mappings to temporary files):
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', '%t', 0, v:null, [
+ \ [fnamemodify(tempname(), ':h:h'), '/foo/bar']
+ \])
+
+ Assert g:result[1] =~# '/foo/bar'
+
+Execute(FormatCommand should apply filename modifiers to mapped filenames):
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', '%s:h', 0, v:null, [
+ \ [expand('%:p:h'), '/foo/bar'],
+ \])
+
+ AssertEqual ale#Escape('/foo/bar'), g:result[1]
+
+ let g:result = ale#command#FormatCommand(bufnr('%'), '', '%t:h:h:h', 0, v:null, [
+ \ [fnamemodify(tempname(), ':h:h'), '/foo/bar']
+ \])
+
+ AssertEqual ale#Escape('/foo/bar'), g:result[1]
diff --git a/test/test_go_to_definition.vader b/test/test_go_to_definition.vader
index a517bd54..f2f34280 100644
--- a/test/test_go_to_definition.vader
+++ b/test/test_go_to_definition.vader
@@ -514,7 +514,7 @@ Execute(LSP tab type definition requests should be sent):
let b:ale_linters = ['pyls']
call setpos('.', [bufnr(''), 1, 5, 0])
- ALEGoToTypeDefinitionInTab
+ ALEGoToTypeDefinition -tab
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader
index cd32ebc8..d000b158 100644
--- a/test/test_linter_defintion_processing.vader
+++ b/test/test_linter_defintion_processing.vader
@@ -39,13 +39,13 @@ Execute (PreProcess should throw when then callback is not a function):
\})
AssertEqual '`callback` must be defined with a callback to accept output', g:vader_exception
-Execute (PreProcess should throw when there is no executable or executable_callback):
+Execute (PreProcess should throw when there is no executable):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
\ 'callback': 'SomeFunction',
\ 'command': 'echo',
\})
- AssertEqual 'Either `executable` or `executable_callback` must be defined', g:vader_exception
+ AssertEqual '`executable` must be defined', g:vader_exception
Execute (PreProcess should throw when executable is not a string):
AssertThrows call ale#linter#PreProcess('testft', {
@@ -56,15 +56,6 @@ Execute (PreProcess should throw when executable is not a string):
\})
AssertEqual '`executable` must be a String or Function if defined', g:vader_exception
-Execute (PreProcess should throw when executable_callback is not a callback):
- AssertThrows call ale#linter#PreProcess('testft', {
- \ 'name': 'foo',
- \ 'callback': 'SomeFunction',
- \ 'executable_callback': 123,
- \ 'command': 'echo',
- \})
- AssertEqual '`executable_callback` must be a callback if defined', g:vader_exception
-
Execute (PreProcess should allow executable to be a callback):
call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
@@ -79,7 +70,7 @@ Execute (PreProcess should throw when there is no command):
\ 'callback': 'SomeFunction',
\ 'executable': 'echo',
\})
- AssertEqual 'Either `command`, `executable_callback`, `command_chain` must be defined', g:vader_exception
+ AssertEqual '`command` must be defined', g:vader_exception
Execute (PreProcess should throw when command is not a string):
AssertThrows call ale#linter#PreProcess('testft', {
@@ -98,15 +89,6 @@ Execute (PreProcess should allow command to be a callback):
\ 'command': function('type'),
\})
-Execute (PreProcess should throw when command_callback is not a callback):
- AssertThrows call ale#linter#PreProcess('testft', {
- \ 'name': 'foo',
- \ 'callback': 'SomeFunction',
- \ 'executable': 'echo',
- \ 'command_callback': 123,
- \})
- AssertEqual '`command_callback` must be a callback if defined', g:vader_exception
-
Execute (PreProcess should when the output stream isn't a valid string):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
@@ -152,117 +134,12 @@ Execute (PreProcess should accept a 'both' output_stream):
\ 'output_stream': 'both',
\})
-Execute(PreProcess should complain if the command_chain is not a List):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': 'x',
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`command_chain` must be a List', g:vader_exception
-
-Execute(PreProcess should complain if the command_chain is empty):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [],
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`command_chain` must contain at least one item', g:vader_exception
-
-Execute(PreProcess should complain if the command_chain has no callback):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [{}],
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'The `command_chain` item 0 must define a `callback` function', g:vader_exception
-
-Execute(PreProcess should complain if the command_chain callback is not a function):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [{'callback': 2}],
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'The `command_chain` item 0 must define a `callback` function', g:vader_exception
-
-Execute(PreProcess should accept a chain with one callback):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [{'callback': 'foo'}],
- \}
- call ale#linter#PreProcess('testft', g:linter)
-
-Execute(PreProcess should complain about invalid output_stream values in the chain):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [{'callback': 'foo', 'output_stream': ''}],
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual "The `command_chain` item 0 `output_stream` flag must be 'stdout', 'stderr', or 'both'", g:vader_exception
-
-Execute(PreProcess should complain about valid output_stream values in the chain):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [{'callback': 'foo', 'output_stream': 'stdout'}],
- \}
- call ale#linter#PreProcess('testft', g:linter)
- let g:linter.command_chain[0].output_stream = 'stderr'
- call ale#linter#PreProcess('testft', g:linter)
- let g:linter.command_chain[0].output_stream = 'both'
- call ale#linter#PreProcess('testft', g:linter)
-
-Execute(PreProcess should complain about invalid chain items at higher indices):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command_chain': [{'callback': 'foo'}, {'callback': 123}],
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'The `command_chain` item 1 must define a `callback` function', g:vader_exception
-
-Execute(PreProcess should complain when conflicting command options are used):
- let g:linter = {
- \ 'name': 'x',
- \ 'callback': 'x',
- \ 'executable': 'x',
- \ 'command': 'foo',
- \ 'command_chain': [{'callback': 'foo'}],
- \}
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `command`, `command_callback`, or `command_chain` should be set', g:vader_exception
-
- unlet g:linter.command
- let g:linter.command_callback = 'foo'
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `command`, `command_callback`, or `command_chain` should be set', g:vader_exception
-
- let g:linter.command = 'foo'
- unlet g:linter.command_chain
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `command`, `command_callback`, or `command_chain` should be set', g:vader_exception
-
Execute(PreProcess should process the read_buffer option correctly):
let g:linter = {
\ 'name': 'x',
\ 'callback': 'x',
\ 'executable': 'x',
- \ 'command_chain': [{'callback': 'foo'}, {'callback': 'bar'}],
+ \ 'command': 'x',
\ 'read_buffer': '0',
\}
@@ -277,25 +154,6 @@ Execute(PreProcess should process the read_buffer option correctly):
call ale#linter#PreProcess('testft', g:linter)
- unlet g:linter.read_buffer
- let g:linter.command_chain[0].read_buffer = '0'
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'The `command_chain` item 0 value for `read_buffer` must be `0` or `1`', g:vader_exception
-
- let g:linter.command_chain[0].read_buffer = 0
-
- call ale#linter#PreProcess('testft', g:linter)
-
- let g:linter.command_chain[1].read_buffer = '0'
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'The `command_chain` item 1 value for `read_buffer` must be `0` or `1`', g:vader_exception
-
- let g:linter.command_chain[1].read_buffer = 1
-
- call ale#linter#PreProcess('testft', g:linter)
-
Execute(PreProcess should set a default value for read_buffer):
let g:linter = {
\ 'name': 'x',
@@ -316,7 +174,7 @@ Execute(PreProcess should process the lint_file option correctly):
\}
AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`lint_file` must be `0` or `1`', g:vader_exception
+ AssertEqual '`lint_file` must be `0`, `1`, or a Function', g:vader_exception
let g:linter.lint_file = 0
@@ -327,14 +185,17 @@ Execute(PreProcess should process the lint_file option correctly):
let g:linter.lint_file = 1
AssertEqual 1, ale#linter#PreProcess('testft', g:linter).lint_file
- " The default for read_buffer should change to 0 when lint_file is 1.
- AssertEqual 0, ale#linter#PreProcess('testft', g:linter).read_buffer
+ " The default for read_buffer should still be 1
+ AssertEqual 1, ale#linter#PreProcess('testft', g:linter).read_buffer
let g:linter.read_buffer = 1
- " We shouldn't be able to set both options to 1 at the same time.
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `lint_file` or `read_buffer` can be `1`', g:vader_exception
+ " We should be able to set `read_buffer` and `lint_file` at the same time.
+ AssertEqual 1, ale#linter#PreProcess('testft', g:linter).read_buffer
+
+ let g:linter.lint_file = function('type')
+
+ Assert type(ale#linter#PreProcess('testft', g:linter).lint_file) is v:t_func
Execute(PreProcess should set a default value for lint_file):
let g:linter = {
@@ -394,151 +255,96 @@ Execute(PreProcess should accept tsserver LSP configuration):
\ 'executable': 'x',
\ 'command': 'x',
\ 'lsp': 'tsserver',
- \ 'language_callback': 'x',
- \ 'project_root_callback': 'x',
+ \ 'language': 'x',
+ \ 'project_root': 'x',
\}
AssertEqual 'tsserver', ale#linter#PreProcess('testft', g:linter).lsp
- call remove(g:linter, 'executable')
- let g:linter.executable_callback = 'X'
-
- call ale#linter#PreProcess('testft', g:linter)
-
- call remove(g:linter, 'command')
- let g:linter.command_callback = 'X'
-
- call ale#linter#PreProcess('testft', g:linter)
-
Execute(PreProcess should accept stdio LSP configuration):
let g:linter = {
\ 'name': 'x',
\ 'executable': 'x',
\ 'command': 'x',
\ 'lsp': 'stdio',
- \ 'language_callback': 'x',
- \ 'project_root_callback': 'x',
+ \ 'language': 'x',
+ \ 'project_root': 'x',
\}
AssertEqual 'stdio', ale#linter#PreProcess('testft', g:linter).lsp
- call remove(g:linter, 'executable')
- let g:linter.executable_callback = 'X'
-
- call ale#linter#PreProcess('testft', g:linter)
-
- call remove(g:linter, 'command')
- let g:linter.command_callback = 'X'
-
- call ale#linter#PreProcess('testft', g:linter)
-
Execute(PreProcess should accept LSP server configurations):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language_callback': 'x',
- \ 'project_root_callback': 'x',
+ \ 'address': 'X',
+ \ 'language': 'foobar',
+ \ 'project_root': 'x',
\}
AssertEqual 'socket', ale#linter#PreProcess('testft', g:linter).lsp
-Execute(PreProcess should accept let you specify the language as just a string):
+Execute(PreProcess should accept let you specify the `language` as a Function):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language': 'foobar',
- \ 'project_root_callback': 'x',
+ \ 'address': 'X',
+ \ 'language': {-> 'foobar'},
+ \ 'project_root': 'x',
\}
- AssertEqual 'foobar', ale#linter#PreProcess('testft', g:linter).language_callback(0)
-
-Execute(PreProcess should complain about using language and language_callback together):
- let g:linter = {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language': 'x',
- \ 'language_callback': 'x',
- \ 'project_root_callback': 'x',
- \}
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `language` or `language_callback` should be set', g:vader_exception
+ AssertEqual 'foobar', ale#linter#PreProcess('testft', g:linter).language(bufnr(''))
Execute(PreProcess should complain about invalid language values):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\ 'language': 0,
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\}
AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`language` must be a String or Funcref', g:vader_exception
+ AssertEqual '`language` must be a String or Funcref if defined', g:vader_exception
Execute(PreProcess should use the filetype as the language string by default):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'project_root_callback': 'x',
+ \ 'address': 'X',
+ \ 'project_root': 'x',
\}
- AssertEqual 'testft', ale#linter#PreProcess('testft', g:linter).language_callback(0)
+ AssertEqual 'testft', ale#linter#PreProcess('testft', g:linter).language
-Execute(PreProcess should allow language to be set to a callback):
- let g:linter = {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language': {-> 'foo'},
- \ 'project_root_callback': 'x',
- \}
-
- AssertEqual 'foo', ale#linter#PreProcess('testft', g:linter).language_callback(0)
-
-Execute(PreProcess should require an address_callback for LSP socket configurations):
+Execute(PreProcess should require an `address` for LSP socket configurations):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
\}
AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`address` or `address_callback` must be defined for getting the LSP address', g:vader_exception
+ AssertEqual '`address` must be defined for getting the LSP address', g:vader_exception
-Execute(PreProcess should complain about address_callback for non-LSP linters):
+Execute(PreProcess should complain about `address` for non-LSP linters):
let g:linter = {
\ 'name': 'x',
\ 'callback': 'SomeFunction',
\ 'executable': 'echo',
\ 'command': 'echo',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\}
AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual '`address` or `address_callback` cannot be used when lsp != ''socket''', g:vader_exception
+ AssertEqual '`address` cannot be used when lsp != ''socket''', g:vader_exception
-Execute(PreProcess accept valid address_callback values):
- let g:linter = ale#linter#PreProcess('testft', {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address_callback': {-> 'foo:123'},
- \ 'language': 'x',
- \ 'project_root_callback': 'x',
- \})
-
- AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)
-
-Execute(PreProcess accept address as a String):
+Execute(PreProcess accept `address` as a String):
let g:linter = ale#linter#PreProcess('testft', {
\ 'name': 'x',
\ 'lsp': 'socket',
\ 'address': 'foo:123',
\ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\})
AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)
@@ -549,7 +355,7 @@ Execute(PreProcess accept address as a Function):
\ 'lsp': 'socket',
\ 'address': {-> 'foo:123'},
\ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\})
AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)
@@ -560,11 +366,11 @@ Execute(PreProcess should complain about invalid address values):
\ 'lsp': 'socket',
\ 'address': 0,
\ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\})
AssertEqual '`address` must be a String or Function if defined', g:vader_exception
-Execute(PreProcess should accept allow the project root be set as a String):
+Execute(PreProcess should allow the `project_root` to be set as a String):
let g:linter = ale#linter#PreProcess('testft', {
\ 'name': 'x',
\ 'lsp': 'socket',
@@ -575,7 +381,7 @@ Execute(PreProcess should accept allow the project root be set as a String):
AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)
-Execute(PreProcess should accept allow the project root be set as a Function):
+Execute(PreProcess should `project_root` be set as a Function):
let g:linter = ale#linter#PreProcess('testft', {
\ 'name': 'x',
\ 'lsp': 'socket',
@@ -586,7 +392,7 @@ Execute(PreProcess should accept allow the project root be set as a Function):
AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)
-Execute(PreProcess should complain when the project_root valid is invalid):
+Execute(PreProcess should complain when `project_root` is invalid):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'x',
\ 'lsp': 'socket',
@@ -594,154 +400,74 @@ Execute(PreProcess should complain when the project_root valid is invalid):
\ 'language': 'x',
\ 'project_root': 0,
\})
- AssertEqual '`project_root` must be a String or Function if defined', g:vader_exception
+ AssertEqual '`project_root` must be a String or Function', g:vader_exception
-Execute(PreProcess should accept project_root_callback as a String):
- call ale#linter#PreProcess('testft', {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address': 'foo:123',
- \ 'language': 'x',
- \ 'project_root_callback': 'Foobar',
- \})
-
-Execute(PreProcess should accept project_root_callback as a Function):
- let g:linter = ale#linter#PreProcess('testft', {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address': 'foo:123',
- \ 'language': 'x',
- \ 'project_root_callback': {-> '/foo/bar'},
- \})
-
- AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)
-
-Execute(PreProcess should complain when the project_root_callback valid is invalid):
- AssertThrows call ale#linter#PreProcess('testft', {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address': 'foo:123',
- \ 'language': 'x',
- \ 'project_root_callback': 0,
- \})
- AssertEqual '`project_root_callback` must be a callback if defined', g:vader_exception
-
-Execute(PreProcess should complain about using initialization_options and initialization_options_callback together):
- let g:linter = {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language': 'x',
- \ 'project_root_callback': 'x',
- \ 'initialization_options': 'x',
- \ 'initialization_options_callback': 'x',
- \}
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `initialization_options` or `initialization_options_callback` should be set', g:vader_exception
-
-Execute(PreProcess should throw when initialization_options_callback is not a callback):
+Execute(PreProcess should throw when `initialization_options` is not a Dictionary or callback):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\ 'language': 'x',
- \ 'project_root_callback': 'x',
- \ 'initialization_options_callback': {},
- \})
- AssertEqual '`initialization_options_callback` must be a callback if defined', g:vader_exception
-
-Execute(PreProcess should throw when initialization_options is not a Dictionary or callback):
- AssertThrows call ale#linter#PreProcess('testft', {
- \ 'name': 'foo',
- \ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\ 'initialization_options': 0,
\})
- AssertEqual '`initialization_options` must be a String or Function if defined', g:vader_exception
+ AssertEqual '`initialization_options` must be a Dictionary or Function if defined', g:vader_exception
-Execute(PreProcess should accept initialization_options as a Dictionary):
+Execute(PreProcess should accept `initialization_options` as a Dictionary):
let g:linter = ale#linter#PreProcess('testft', {
\ 'name': 'foo',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\ 'initialization_options': {'foo': v:true},
\})
AssertEqual {'foo': v:true}, ale#lsp_linter#GetOptions(0, g:linter)
-Execute(PreProcess should accept initialization_options as a Funcref):
+Execute(PreProcess should accept `initialization_options` as a Function):
let g:linter = ale#linter#PreProcess('testft', {
\ 'name': 'foo',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\ 'initialization_options': {-> {'foo': v:true}},
\})
AssertEqual {'foo': v:true}, ale#lsp_linter#GetOptions(0, g:linter)
-Execute(PreProcess should complain about using lsp_config and lsp_config_callback together):
+Execute(PreProcess should accept `lsp_config` as a Dictionary):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\ 'language': 'x',
- \ 'project_root_callback': 'x',
- \ 'lsp_config': 'x',
- \ 'lsp_config_callback': 'x',
- \}
-
- AssertThrows call ale#linter#PreProcess('testft', g:linter)
- AssertEqual 'Only one of `lsp_config` or `lsp_config_callback` should be set', g:vader_exception
-
-Execute(PreProcess should throw when lsp_config_callback is not a callback):
- AssertThrows call ale#linter#PreProcess('testft', {
- \ 'name': 'foo',
- \ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language': 'x',
- \ 'project_root_callback': 'x',
- \ 'lsp_config_callback': {},
- \})
- AssertEqual '`lsp_config_callback` must be a callback if defined', g:vader_exception
-
-Execute(PreProcess should accept LSP configuration options via lsp_config):
- let g:linter = {
- \ 'name': 'x',
- \ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language_callback': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\ 'lsp_config': {'foo': 'bar'},
\}
AssertEqual {'foo': 'bar'}, ale#lsp_linter#GetConfig(0, g:linter)
-Execute(PreProcess should accept LSP configuration options via lsp_config as a function):
+Execute(PreProcess should accept `lsp_config` as a Function):
let g:linter = {
\ 'name': 'x',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
- \ 'language_callback': 'x',
- \ 'project_root_callback': 'x',
+ \ 'address': 'X',
+ \ 'language': 'x',
+ \ 'project_root': 'x',
\ 'lsp_config': {-> {'foo': 'bar'}},
\}
AssertEqual {'foo': 'bar'}, ale#lsp_linter#GetConfig(0, g:linter)
-Execute(PreProcess should throw when lsp_config is not a Dictionary or Function):
+Execute(PreProcess should throw when `lsp_config` is not a Dictionary or Function):
AssertThrows call ale#linter#PreProcess('testft', {
\ 'name': 'foo',
\ 'lsp': 'socket',
- \ 'address_callback': 'X',
+ \ 'address': 'X',
\ 'language': 'x',
- \ 'project_root_callback': 'x',
+ \ 'project_root': 'x',
\ 'lsp_config': 'x',
\})
AssertEqual '`lsp_config` must be a Dictionary or Function if defined', g:vader_exception
diff --git a/test/test_list_formatting.vader b/test/test_list_formatting.vader
index dcefac53..d79a664b 100644
--- a/test/test_list_formatting.vader
+++ b/test/test_list_formatting.vader
@@ -36,7 +36,7 @@ After:
call setqflist([])
Execute(Formatting with codes should work for the loclist):
- call AddItem({'text': 'nocode'})
+ call AddItem({'text': "nocode\r"})
call ale#list#SetLists(bufnr(''), g:loclist)
AssertEqual
@@ -79,7 +79,7 @@ Execute(Formatting with codes should work for the quickfix list):
let g:ale_set_loclist = 0
let g:ale_set_quickfix = 1
- call AddItem({'text': 'nocode'})
+ call AddItem({'text': "nocode\r"})
call ale#list#SetLists(bufnr(''), g:loclist)
AssertEqual
diff --git a/test/test_loclist_corrections.vader b/test/test_loclist_corrections.vader
index 343620a5..d53b1411 100644
--- a/test/test_loclist_corrections.vader
+++ b/test/test_loclist_corrections.vader
@@ -1,7 +1,50 @@
+Before:
+ Save g:ale_filename_mappings
+
+ let g:ale_filename_mappings = {}
+
After:
unlet! b:temp_name
unlet! b:other_bufnr
+ Restore
+
+
+Execute(FixLocList should map filenames):
+ " Paths converted back into temporary filenames shouldn't be included.
+ let g:ale_filename_mappings = {
+ \ 'linter2': [['/xxx/', '/data/']],
+ \ 'linter1': [
+ \ ['/bar/', '/data/special/'],
+ \ ['/foo/', '/data/'],
+ \ [
+ \ ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h')) . '/',
+ \ '/x-tmp/',
+ \ ],
+ \ ],
+ \}
+
+ AssertEqual
+ \ [
+ \ '/foo/file.txt',
+ \ v:null,
+ \ '/bar/file.txt',
+ \ ],
+ \ map(
+ \ ale#engine#FixLocList(
+ \ bufnr('%'),
+ \ 'linter1',
+ \ 0,
+ \ [
+ \ {'text': 'x', 'lnum': 1, 'filename': '/data/file.txt'},
+ \ {'text': 'x', 'lnum': 1, 'filename': '/x-tmp/file.txt'},
+ \ {'text': 'x', 'lnum': 1, 'filename': '/data/special/file.txt'},
+ \ ],
+ \ ),
+ \ 'get(v:val, ''filename'', v:null)',
+ \ )
+
+
Given foo (Some file with lines to count):
foo12345678
bar12345678
@@ -37,7 +80,7 @@ Execute(FixLocList should set all the default values correctly):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -58,7 +101,7 @@ Execute(FixLocList should use the values we supply):
\ 'nr': 42,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -87,7 +130,7 @@ Execute(FixLocList should set items with lines beyond the end to the last line):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -108,7 +151,7 @@ Execute(FixLocList should move line 0 to line 1):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -130,7 +173,7 @@ Execute(FixLocList should convert line and column numbers correctly):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -164,7 +207,7 @@ Execute(FixLocList should pass on end_col values):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -202,7 +245,7 @@ Execute(FixLocList should pass on end_lnum values):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -227,7 +270,7 @@ Execute(FixLocList should allow subtypes to be set):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -289,7 +332,7 @@ Execute(FixLocList should accept filenames):
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -327,7 +370,7 @@ Execute(FixLocList should interpret temporary filenames as being the current buf
\ 'nr': -1,
\ 'linter_name': 'foobar',
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr(''),
\ 'foobar',
@@ -352,7 +395,7 @@ Execute(The error code should be passed on):
\ 'linter_name': 'foobar',
\ 'code': 'some-code'
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -385,7 +428,7 @@ Execute(FixLocList should mark problems as coming from other sources if requeste
\ 'linter_name': 'foobar',
\ 'from_other_source': 1,
\ },
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
@@ -407,7 +450,7 @@ Execute(character positions should be converted to byte positions):
\ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 13, 'end_lnum': 1, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
\ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 17, 'end_lnum': 2, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
\ {'lnum': 2, 'bufnr': bufnr(''), 'col': 17, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
- \],
+ \ ],
\ ale#engine#FixLocList(
\ bufnr('%'),
\ 'foobar',
diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader
index 6452287f..697054d0 100644
--- a/test/test_shell_detection.vader
+++ b/test/test_shell_detection.vader
@@ -98,6 +98,16 @@ Execute(The ksh dialect should be used for shellcheck if b:is_kornshell is 1):
AssertEqual 'ksh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+Execute(The filetype should be used as the default shell type when there is no hashbang line):
+ set filetype=zsh
+ AssertEqual 'zsh', ale#handlers#sh#GetShellType(bufnr(''))
+
+ set filetype=tcsh
+ AssertEqual 'tcsh', ale#handlers#sh#GetShellType(bufnr(''))
+
+ set filetype=python
+ AssertEqual '', ale#handlers#sh#GetShellType(bufnr(''))
+
Given(A file with /bin/ash):
#!/bin/ash
diff --git a/test/test_swiftlint_executable_detection.vader b/test/test_swiftlint_executable_detection.vader
index dfd4930b..ac83ff8f 100644
--- a/test/test_swiftlint_executable_detection.vader
+++ b/test/test_swiftlint_executable_detection.vader
@@ -23,22 +23,22 @@ Execute(React Native apps using CocoaPods should take precedence over the defaul
call ale#test#SetFilename('swiftlint-test-files/react-native/testfile.swift')
AssertEqual
- \ ale#path#Simplify(g:dir . '/swiftlint-test-files/react-native/ios/Pods/SwiftLint/swiftlint'),
- \ ale_linters#swift#swiftlint#GetExecutable(bufnr(''))
+ \ tolower(ale#path#Simplify(g:dir . '/swiftlint-test-files/react-native/ios/Pods/SwiftLint/swiftlint')),
+ \ tolower(ale_linters#swift#swiftlint#GetExecutable(bufnr('')))
Execute(CocoaPods installation should take precedence over the default executable):
call ale#test#SetFilename('swiftlint-test-files/cocoapods/testfile.swift')
AssertEqual
- \ ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods/Pods/SwiftLint/swiftlint'),
- \ ale_linters#swift#swiftlint#GetExecutable(bufnr(''))
+ \ tolower(ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods/Pods/SwiftLint/swiftlint')),
+ \ tolower(ale_linters#swift#swiftlint#GetExecutable(bufnr('')))
Execute(Top level CocoaPods installation should take precedence over React Native installation):
call ale#test#SetFilename('swiftlint-test-files/cocoapods-and-react-native/testfile.swift')
AssertEqual
- \ ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods-and-react-native/Pods/SwiftLint/swiftlint'),
- \ ale_linters#swift#swiftlint#GetExecutable(bufnr(''))
+ \ tolower(ale#path#Simplify(g:dir . '/swiftlint-test-files/cocoapods-and-react-native/Pods/SwiftLint/swiftlint')),
+ \ tolower(ale_linters#swift#swiftlint#GetExecutable(bufnr('')))
Execute(use-global should override other versions):
let g:ale_swift_swiftlint_use_global = 1
diff --git a/test/test_temporary_file_management.vader b/test/test_temporary_file_management.vader
index 9fff1ace..bb735886 100644
--- a/test/test_temporary_file_management.vader
+++ b/test/test_temporary_file_management.vader
@@ -40,7 +40,7 @@ Before:
\ 'name': 'testlinter',
\ 'executable': has('win32') ? 'cmd' : 'echo',
\ 'callback': 'TestCallback',
- \ 'command_callback': 'TestCommandCallback',
+ \ 'command': function('TestCommandCallback'),
\})
call ale#command#ClearData()
diff --git a/test/test_writefile_function.vader b/test/test_writefile_function.vader
index 811d59e8..53a88331 100644
--- a/test/test_writefile_function.vader
+++ b/test/test_writefile_function.vader
@@ -69,3 +69,49 @@ Execute(Unix file lines should be written as normal):
AssertEqual
\ ['first', 'second', 'third', ''],
\ readfile(g:new_line_test_file, 'b')
+
+Execute(Newline at end of file should be preserved even when nofixeol):
+ call ale#test#SetFilename(g:new_line_test_file)
+
+ setlocal buftype=
+ noautocmd :w
+ noautocmd :e! ++ff=unix
+ set eol
+ set nofixeol
+
+ call ale#util#Writefile(bufnr(''), getline(1, '$'), g:new_line_test_file)
+
+ AssertEqual
+ \ ['first', 'second', 'third', ''],
+ \ readfile(g:new_line_test_file, 'b')
+
+Execute(Newline should not be appended on write when noeol and nofixeol):
+ call ale#test#SetFilename(g:new_line_test_file)
+
+ setlocal buftype=
+ noautocmd :w
+ noautocmd :e! ++ff=unix
+ set noeol
+ set nofixeol
+
+ call ale#util#Writefile(bufnr(''), getline(1, '$'), g:new_line_test_file)
+
+ AssertEqual
+ \ ['first', 'second', 'third'],
+ \ readfile(g:new_line_test_file, 'b')
+
+Execute(Newline should be appended on write when noeol and fixeol):
+ call ale#test#SetFilename(g:new_line_test_file)
+
+ setlocal buftype=
+ noautocmd :w
+ noautocmd :e! ++ff=unix
+ set noeol
+ set fixeol
+
+ call ale#util#Writefile(bufnr(''), getline(1, '$'), g:new_line_test_file)
+
+ AssertEqual
+ \ ['first', 'second', 'third', ''],
+ \ readfile(g:new_line_test_file, 'b')
+
diff --git a/test/util/test_cd_string_commands.vader b/test/util/test_cd_string_commands.vader
index f2102e48..85c5065f 100644
--- a/test/util/test_cd_string_commands.vader
+++ b/test/util/test_cd_string_commands.vader
@@ -16,5 +16,5 @@ Execute(BufferCdString should output the correct command string):
call ale#test#SetFilename('foo.txt')
AssertEqual
- \ has('unix') ? 'cd ' . ale#Escape(g:dir) . ' && ' : 'cd /d ' . ale#Escape(g:dir) . ' && ',
+ \ has('unix') ? 'cd %s:h && ' : 'cd /d %s:h && ',
\ ale#path#BufferCdString(bufnr(''))