From 7eae06d3f33b8c3cb490a3c08986cc8db9f78843 Mon Sep 17 00:00:00 2001 From: Vincent Dahmen Date: Sat, 9 Mar 2019 14:55:54 +0100 Subject: [PATCH] linter/markdown: adds support for languatool (#2155) --- ale_linters/mail/languagetool.vim | 5 ++ ale_linters/markdown/languagetool.vim | 5 ++ ale_linters/text/languagetool.vim | 4 + autoload/ale/handlers/languagetool.vim | 74 +++++++++++++++++++ .../test_languagetool_command_callback.vader | 15 ++++ test/handler/test_languagetool_handler.vader | 62 ++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 ale_linters/mail/languagetool.vim create mode 100644 ale_linters/markdown/languagetool.vim create mode 100644 ale_linters/text/languagetool.vim create mode 100644 autoload/ale/handlers/languagetool.vim create mode 100644 test/command_callback/test_languagetool_command_callback.vader create mode 100644 test/handler/test_languagetool_handler.vader diff --git a/ale_linters/mail/languagetool.vim b/ale_linters/mail/languagetool.vim new file mode 100644 index 00000000..330fb8ec --- /dev/null +++ b/ale_linters/mail/languagetool.vim @@ -0,0 +1,5 @@ +" Author: Vincent (wahrwolf [ät] wolfpit.net) +" Description: languagetool for mails + + +call ale#handlers#languagetool#DefineLinter('mail') diff --git a/ale_linters/markdown/languagetool.vim b/ale_linters/markdown/languagetool.vim new file mode 100644 index 00000000..d6bca22e --- /dev/null +++ b/ale_linters/markdown/languagetool.vim @@ -0,0 +1,5 @@ +" Author: Vincent (wahrwolf [ät] wolfpit.net) +" Description: languagetool for markdown files + + +call ale#handlers#languagetool#DefineLinter('markdown') diff --git a/ale_linters/text/languagetool.vim b/ale_linters/text/languagetool.vim new file mode 100644 index 00000000..58c99ba2 --- /dev/null +++ b/ale_linters/text/languagetool.vim @@ -0,0 +1,4 @@ +" Author: Vincent (wahrwolf [ät] wolfpit.net) +" Description: languagetool for text files + +call ale#handlers#languagetool#DefineLinter('text') diff --git a/autoload/ale/handlers/languagetool.vim b/autoload/ale/handlers/languagetool.vim new file mode 100644 index 00000000..8b7c5306 --- /dev/null +++ b/autoload/ale/handlers/languagetool.vim @@ -0,0 +1,74 @@ +" Author: Vincent (wahrwolf [ät] wolfpit.net) +" Description: languagetool for markdown files +" +call ale#Set('languagetool_executable', 'languagetool') + +function! ale#handlers#languagetool#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'languagetool_executable') +endfunction + +function! ale#handlers#languagetool#GetCommand(buffer) abort + let l:executable = ale#handlers#languagetool#GetExecutable(a:buffer) + + return ale#Escape(l:executable) . ' --autoDetect ' +endfunction + +function! ale#handlers#languagetool#HandleOutput(buffer, lines) abort + " Match lines like: + " 1.) Line 5, column 1, Rule ID: + let l:head_pattern = '^\v.+.\) Line (\d+), column (\d+), Rule ID. (.+)$' + let l:head_matches = ale#util#GetMatches(a:lines, l:head_pattern) + + " Match lines like: + " Message: Did you forget a comma after a conjunctive/linking adverb? + let l:message_pattern = '^\vMessage. (.+)$' + let l:message_matches = ale#util#GetMatches(a:lines, l:message_pattern) + + " Match lines like: + " ^^^^^ " + let l:markers_pattern = '^\v *(\^+) *$' + let l:markers_matches = ale#util#GetMatches(a:lines, l:markers_pattern) + + let l:output = [] + + + " Okay tbh I was to lazy to figure out a smarter solution here + " We just check that the arrays are same sized and merge everything + " together + let l:i = 0 + + while l:i < len(l:head_matches) + \ && ( + \ (len(l:head_matches) == len(l:markers_matches)) + \ && (len(l:head_matches) == len(l:message_matches)) + \ ) + let l:item = { + \ 'lnum' : str2nr(l:head_matches[l:i][1]), + \ 'col' : str2nr(l:head_matches[l:i][2]), + \ 'end_col' : str2nr(l:head_matches[l:i][2]) + len(l:markers_matches[l:i][1])-1, + \ 'type' : 'W', + \ 'code' : l:head_matches[l:i][3], + \ 'text' : l:message_matches[l:i][1] + \} + call add(l:output, l:item) + let l:i+=1 + endwhile + + return l:output +endfunction + +" Define the languagetool linter for a given filetype. +" TODO: +" - Add language detection settings based on user env (for mothertongue) +" - Add fixer +" - Add config options for rules +function! ale#handlers#languagetool#DefineLinter(filetype) abort + call ale#linter#Define(a:filetype, { + \ 'name': 'languagetool', + \ 'executable_callback': 'ale#handlers#languagetool#GetExecutable', + \ 'command_callback': 'ale#handlers#languagetool#GetCommand', + \ 'output_stream': 'stdout', + \ 'callback': 'ale#handlers#languagetool#HandleOutput', + \ 'lint_file': 1, + \}) +endfunction diff --git a/test/command_callback/test_languagetool_command_callback.vader b/test/command_callback/test_languagetool_command_callback.vader new file mode 100644 index 00000000..1b5f0e00 --- /dev/null +++ b/test/command_callback/test_languagetool_command_callback.vader @@ -0,0 +1,15 @@ +Before: + call ale#assert#SetUpLinterTest('text', 'languagetool') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'languagetool', ale#Escape('languagetool') + \ . ' --autoDetect ' + +Execute(Should be able to set a custom executable): + let g:ale_languagetool_executable = 'foobar' + + AssertLinter 'foobar' , ale#Escape('foobar') + \ . ' --autoDetect ' diff --git a/test/handler/test_languagetool_handler.vader b/test/handler/test_languagetool_handler.vader new file mode 100644 index 00000000..61d3abfd --- /dev/null +++ b/test/handler/test_languagetool_handler.vader @@ -0,0 +1,62 @@ +Before: + runtime! ale_linters/text/languagetool.vim + +After: + call ale#linter#Reset() + +Execute(languagetool handler should report 3 errors): + AssertEqual + \ [ + \ { + \ 'lnum': 3, + \ 'col': 19, + \ 'end_col': 20, + \ 'text': 'This sentence does not start with an uppercase letter', + \ 'type': 'W', + \ 'code': 'UPPERCASE_SENTENCE_START', + \ }, + \ { + \ 'lnum': 3, + \ 'col': 36, + \ 'end_col': 42, + \ 'text': "Did you mean 'to see'?", + \ 'type': 'W', + \ 'code': 'TOO_TO[1]', + \ }, + \ { + \ 'lnum': 3, + \ 'col': 44, + \ 'end_col': 45, + \ 'text': "Use 'a' instead of 'an' if the following word doesn't start with a vowel sound, e.g. 'a sentence', 'a university'", + \ 'type': 'W', + \ 'code': 'EN_A_VS_AN', + \ } + \ ], + \ ale#handlers#languagetool#HandleOutput(bufnr(''), [ + \ '1.) Line 3, column 19, Rule ID: UPPERCASE_SENTENCE_START', + \ 'Message: This sentence does not start with an uppercase letter', + \ 'Suggestion: Or', + \ '...red phrases for details on potential errors. or use this text too see an few of of the probl...', + \ ' ^^ ', + \ '', + \ '2.) Line 3, column 36, Rule ID: TOO_TO[1]', + \ "Message: Did you mean 'to see'?", + \ 'Suggestion: to see', + \ '...etails on potential errors. or use this text too see an few of of the problems that LanguageTool ...', + \ ' ^^^^^^^ ', + \ '', + \ '3.) Line 3, column 44, Rule ID: EN_A_VS_AN', + \ "Message: Use 'a' instead of 'an' if the following word doesn't start with a vowel sound, e.g. 'a sentence', 'a university'", + \ 'Suggestion: a', + \ '...n potential errors. or use this text too see an few of of the problems that LanguageTool can...', + \ ' ^^ ', + \ 'Time: 2629ms for 8 sentences (3.0 sentences/sec)' + \ ]) + +Execute(languagetool handler should report no errors on empty input): + AssertEqual + \ [], + \ ale#handlers#languagetool#HandleOutput(bufnr(''), [ + \ '', + \ 'Time: 2629ms for 8 sentences (3.0 sentences/sec)' + \ ])