diff --git a/README.md b/README.md index fc3d1d35..f0b382b9 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ name. That seems to be the fairest way to arrange this table. | Vim | [vint](https://github.com/Kuniwak/vint) | | Vim help^ | [proselint](http://proselint.com/)| | XHTML | [proselint](http://proselint.com/)| +| XML | [xmllint](http://xmlsoft.org/xmllint.html/)| | YAML | [yamllint](https://yamllint.readthedocs.io/) | * *^ No linters for text or Vim help filetypes are enabled by default.* diff --git a/ale_linters/xml/xmllint.vim b/ale_linters/xml/xmllint.vim new file mode 100644 index 00000000..63d7f768 --- /dev/null +++ b/ale_linters/xml/xmllint.vim @@ -0,0 +1,69 @@ +" Author: q12321q +" Description: This file adds support for checking XML code with xmllint. + +" CLI options +let g:ale_xml_xmllint_executable = get(g:, 'ale_xml_xmllint_executable', 'xmllint') +let g:ale_xml_xmllint_options = get(g:, 'ale_xml_xmllint_options', '') + +function! ale_linters#xml#xmllint#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'xml_xmllint_executable') +endfunction + +function! ale_linters#xml#xmllint#GetCommand(buffer) abort + return ale#Escape(ale_linters#xml#xmllint#GetExecutable(a:buffer)) + \ . ' ' . ale#Var(a:buffer, 'xml_xmllint_options') + \ . ' --noout -' +endfunction + +function! ale_linters#xml#xmllint#Handle(buffer, lines) abort + " Matches patterns lines like the following: + " file/path:123: error level : error message + let l:pattern_message = '\v^([^:]+):(\d+):\s*(([^:]+)\s*:\s+.*)$' + + " parse column token line like that: + " file/path:123: parser error : Opening and ending tag mismatch: foo line 1 and bar + " + " ^ + let l:pattern_column_token = '\v^\s*\^$' + + let l:output = [] + + for l:line in a:lines + + " Parse error/warning lines + let l:match_message = matchlist(l:line, l:pattern_message) + if !empty(l:match_message) + let l:line = l:match_message[2] + 0 + let l:type = l:match_message[4] =~? 'warning' ? 'W' : 'E' + let l:text = l:match_message[3] + + call add(l:output, { + \ 'lnum': l:line, + \ 'text': l:text, + \ 'type': l:type, + \}) + + continue + endif + + " Parse column position + let l:match_column_token = matchlist(l:line, l:pattern_column_token) + if !empty(l:output) && !empty(l:match_column_token) + let l:previous = l:output[len(l:output) - 1] + let l:previous['col'] = len(l:match_column_token[0]) + + continue + endif + + endfor + + return l:output +endfunction + +call ale#linter#Define('xml', { +\ 'name': 'xmllint', +\ 'output_stream': 'stderr', +\ 'executable_callback': 'ale_linters#xml#xmllint#GetExecutable', +\ 'command_callback': 'ale_linters#xml#xmllint#GetCommand', +\ 'callback': 'ale_linters#xml#xmllint#Handle', +\ }) diff --git a/doc/ale-xml.txt b/doc/ale-xml.txt new file mode 100644 index 00000000..ee10730f --- /dev/null +++ b/doc/ale-xml.txt @@ -0,0 +1,26 @@ +=============================================================================== +ALE XML Integration *ale-xml-options* + + +------------------------------------------------------------------------------- +xmllint *ale-xml-xmllint* + +g:ale_xml_xmllint_executable *g:ale_xml_xmllint_executable* + *b:ale_xml_xmllint_executable* + Type: |String| + Default: `'xmllint'` + + This variable can be set to change the path to xmllint. + + +g:ale_xml_xmllint_options *g:ale_xml_xmllint_options* + *b:ale_xml_xmllint_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to xmllint. + + +------------------------------------------------------------------------------- + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: + diff --git a/doc/ale.txt b/doc/ale.txt index 52a709bb..615fa271 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -85,6 +85,8 @@ CONTENTS *ale-contents* tslint..............................|ale-typescript-tslint| vim...................................|ale-vim-options| vint................................|ale-vim-vint| + xml...................................|ale-xml-options| + xmllint.............................|ale-xml-xmllint| yaml..................................|ale-yaml-options| yamllint............................|ale-yaml-yamllint| 5. Commands/Keybinds....................|ale-commands| @@ -177,6 +179,7 @@ The following languages and tools are supported. * Vim: 'vint' * Vim help: 'proselint' * XHTML: 'proselint' +* XML: 'xmllint' * YAML: 'yamllint' =============================================================================== diff --git a/test/command_callback/test_xmllint_command_callback.vader b/test/command_callback/test_xmllint_command_callback.vader new file mode 100644 index 00000000..7c0b1963 --- /dev/null +++ b/test/command_callback/test_xmllint_command_callback.vader @@ -0,0 +1,25 @@ +Before: + runtime ale_linters/xml/xmllint.vim + +After: + call ale#linter#Reset() + let g:ale_xml_xmllint_options = '' + let g:ale_xml_xmllint_executable = 'xmllint' + +Execute(The xml xmllint command callback should return the correct default string): + AssertEqual '''xmllint'' --noout -', + \ join(split(ale_linters#xml#xmllint#GetCommand(1))) + +Execute(The xml xmllint command callback should let you set options): + let g:ale_xml_xmllint_options = '--xinclude --postvalid' + + AssertEqual '''xmllint'' --xinclude --postvalid --noout -', + \ join(split(ale_linters#xml#xmllint#GetCommand(1))) + +Execute(The xmllint executable should be configurable): + let g:ale_xml_xmllint_executable = '~/.local/bin/xmllint' + + AssertEqual '~/.local/bin/xmllint', ale_linters#xml#xmllint#GetExecutable(1) + AssertEqual '''~/.local/bin/xmllint'' --noout -', + \ join(split(ale_linters#xml#xmllint#GetCommand(1))) + diff --git a/test/handler/test_xmllint_handler.vader b/test/handler/test_xmllint_handler.vader new file mode 100644 index 00000000..4a377ab3 --- /dev/null +++ b/test/handler/test_xmllint_handler.vader @@ -0,0 +1,30 @@ +Before: + runtime ale_linters/xml/xmllint.vim + +Execute(The xmllint handler should parse error messages correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 22, + \ 'type': 'W', + \ 'text': 'warning: Unsupported version ''dummy''' + \ }, + \ { + \ 'lnum': 34, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'parser error : Start tag expected, ''<'' not found' + \ } + \ ], + \ ale_linters#xml#xmllint#Handle(1, [ + \ 'path/to/file.xml:1: warning: Unsupported version ''dummy''', + \ '', + \ ' ^', + \ '-:34: parser error : Start tag expected, ''<'' not found', + \ 'blahblah>', + \ '^' + \ ]) + +After: + call ale#linter#Reset()