2023-03-28 23:43:55 +08:00
|
|
|
let s:save_cpo = &cpo
|
|
|
|
set cpo&vim
|
|
|
|
|
|
|
|
let s:V = vital#grammarous#new()
|
|
|
|
let s:XML = s:V.import('Web.XML')
|
|
|
|
let s:O = s:V.import('OptionParser')
|
|
|
|
let s:P = s:V.import('Process')
|
|
|
|
let s:is_cygwin = has('win32unix')
|
|
|
|
let s:is_windows = has('win32') || has('win64')
|
|
|
|
let s:job_is_available = has('job') && has('patch-8.0.0027')
|
|
|
|
|
|
|
|
let s:grammarous_root = fnamemodify(expand('<sfile>'), ':p:h:h')
|
|
|
|
|
|
|
|
let g:grammarous#jar_dir = get(g:, 'grammarous#jar_dir', s:grammarous_root . '/misc')
|
2023-03-29 00:09:27 +08:00
|
|
|
let g:grammarous#jar_url = get(g:, 'grammarous#jar_url', 'https://www.languagetool.org/download/LanguageTool-5.9.zip')
|
2023-03-28 23:43:55 +08:00
|
|
|
let g:grammarous#java_cmd = get(g:, 'grammarous#java_cmd', 'java')
|
|
|
|
let g:grammarous#default_lang = get(g:, 'grammarous#default_lang', 'en')
|
|
|
|
let g:grammarous#use_vim_spelllang = get(g:, 'grammarous#use_vim_spelllang', 0)
|
|
|
|
let g:grammarous#info_window_height = get(g:, 'grammarous#info_window_height', 10)
|
|
|
|
let g:grammarous#info_win_direction = get(g:, 'grammarous#info_win_direction', 'botright')
|
|
|
|
let g:grammarous#use_fallback_highlight = get(g:, 'grammarous#use_fallback_highlight', !exists('*matchaddpos'))
|
|
|
|
let g:grammarous#enabled_rules = get(g:, 'grammarous#enabled_rules', {})
|
|
|
|
let g:grammarous#disabled_rules = get(g:, 'grammarous#disabled_rules', {'*' : ['WHITESPACE_RULE', 'EN_QUOTES']})
|
|
|
|
let g:grammarous#enabled_categories = get(g:, 'grammarous#enabled_categories', {})
|
|
|
|
let g:grammarous#disabled_categories = get(g:, 'grammarous#disabled_categories', {})
|
|
|
|
let g:grammarous#default_comments_only_filetypes = get(g:, 'grammarous#default_comments_only_filetypes', {'*' : 0})
|
|
|
|
let g:grammarous#enable_spell_check = get(g:, 'grammarous#enable_spell_check', 0)
|
|
|
|
let g:grammarous#move_to_first_error = get(g:, 'grammarous#move_to_first_error', 1)
|
|
|
|
let g:grammarous#hooks = get(g:, 'grammarous#hooks', {})
|
|
|
|
let g:grammarous#languagetool_cmd = get(g:, 'grammarous#languagetool_cmd', '')
|
|
|
|
let g:grammarous#show_first_error = get(g:, 'grammarous#show_first_error', 0)
|
|
|
|
let g:grammarous#use_location_list = get(g:, 'grammarous#use_location_list', 0)
|
|
|
|
|
|
|
|
highlight default link GrammarousError SpellBad
|
|
|
|
highlight default link GrammarousInfoError ErrorMsg
|
|
|
|
highlight default link GrammarousInfoSection Keyword
|
|
|
|
highlight default link GrammarousInfoHelp Special
|
|
|
|
|
|
|
|
augroup pluging-rammarous-highlight
|
|
|
|
autocmd ColorScheme * highlight default link GrammarousError SpellBad
|
|
|
|
autocmd ColorScheme * highlight default link GrammarousInfoError ErrorMsg
|
|
|
|
autocmd ColorScheme * highlight default link GrammarousInfoSection Keyword
|
|
|
|
autocmd ColorScheme * highlight default link GrammarousInfoHelp Special
|
|
|
|
augroup END
|
|
|
|
|
|
|
|
function! s:get_SID() abort
|
|
|
|
return matchstr(expand('<sfile>'), '<SNR>\d\+_\zeget_SID$')
|
|
|
|
endfunction
|
|
|
|
let s:SID = s:get_SID()
|
|
|
|
delfunction s:get_SID
|
|
|
|
|
|
|
|
function! grammarous#_import_vital_modules()
|
|
|
|
return [s:XML, s:O, s:P]
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#error(...)
|
|
|
|
echohl ErrorMsg
|
|
|
|
try
|
|
|
|
if a:0 > 1
|
|
|
|
let msg = 'vim-grammarous: ' . call('printf', a:000)
|
|
|
|
else
|
|
|
|
let msg = 'vim-grammarous: ' . a:1
|
|
|
|
endif
|
|
|
|
for l in split(msg, "\n")
|
|
|
|
echomsg l
|
|
|
|
endfor
|
|
|
|
finally
|
|
|
|
echohl None
|
|
|
|
endtry
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:delete_jar_dir() abort
|
|
|
|
if !isdirectory(g:grammarous#jar_dir)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
let dir = g:grammarous#jar_dir
|
|
|
|
if s:is_cygwin
|
|
|
|
let dir = s:cygpath(dir)
|
|
|
|
endif
|
|
|
|
|
|
|
|
if dir ==# '' || !isdirectory(dir)
|
|
|
|
call grammarous#error("Directory '%s' does not exist", dir)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
if s:is_windows && !s:is_cygwin
|
|
|
|
let cmd = 'rmdir /s /q ' . dir
|
|
|
|
else
|
|
|
|
let cmd = 'rm -rf ' . dir
|
|
|
|
endif
|
|
|
|
|
|
|
|
let out = system(cmd)
|
|
|
|
if v:shell_error
|
|
|
|
call grammarous#error("Cannot remove the directory '%s': %s", dir, out)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
echomsg 'Deleted ' . dir
|
|
|
|
unlet! s:jar_file
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:find_jar(dir)
|
|
|
|
return findfile('languagetool-commandline.jar', a:dir . '/**')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:prepare_jar(dir)
|
|
|
|
let jar = s:find_jar(a:dir)
|
|
|
|
if jar ==# ''
|
|
|
|
if grammarous#downloader#download(a:dir)
|
|
|
|
let jar = s:find_jar(a:dir)
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
return fnamemodify(jar, ':p')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:find_jar_path()
|
|
|
|
if exists('s:jar_file')
|
|
|
|
return s:jar_file
|
|
|
|
endif
|
|
|
|
|
|
|
|
if !executable(g:grammarous#java_cmd)
|
|
|
|
call grammarous#error('"java" command not found. Please install Java 8+')
|
|
|
|
return ''
|
|
|
|
endif
|
|
|
|
|
|
|
|
" TODO:
|
|
|
|
" Check java version
|
|
|
|
|
|
|
|
let jar = s:prepare_jar(g:grammarous#jar_dir)
|
|
|
|
if jar ==# ''
|
|
|
|
call grammarous#error('Failed to get LanguageTool')
|
|
|
|
return ''
|
|
|
|
endif
|
|
|
|
|
|
|
|
if s:is_cygwin
|
|
|
|
let jar = s:cygpath(jar)
|
|
|
|
endif
|
|
|
|
|
|
|
|
let s:jar_file = jar
|
|
|
|
return jar
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:cygpath(path) abort
|
|
|
|
if !executable('cygpath')
|
|
|
|
return a:path
|
|
|
|
endif
|
|
|
|
|
|
|
|
" On Cygwin environment, paths should be converted with cygpath.
|
|
|
|
" /cygdrive/c/... -> C:/...
|
|
|
|
" https://github.com/rhysd/vim-grammarous/issues/30
|
|
|
|
let converted = substitute(s:P.system('cygpath -aw ' . a:path), '\n\+$', '', '')
|
|
|
|
|
|
|
|
if s:P.get_last_status()
|
|
|
|
return a:path
|
|
|
|
endif
|
|
|
|
|
|
|
|
return converted
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:make_text(text)
|
|
|
|
if type(a:text) == type('')
|
|
|
|
return a:text
|
|
|
|
else
|
|
|
|
return join(a:text, "\n")
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:set_errors_to_location_list() abort
|
|
|
|
let f = expand('%:p')
|
|
|
|
let saved_efm = &l:errorformat
|
|
|
|
try
|
|
|
|
setlocal errorformat=%f:%l:%c:%m
|
|
|
|
let lines = map(copy(b:grammarous_result), '
|
|
|
|
\ printf("%s:%s:%s:%s [%s]", f, v:val.fromy + 1, v:val.fromx + 1, v:val.msg, v:val.category)
|
|
|
|
\')
|
|
|
|
lgetexpr lines
|
|
|
|
finally
|
|
|
|
let &l:errorformat = saved_efm
|
|
|
|
endtry
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:set_errors_from_xml_string(xml) abort
|
|
|
|
let b:grammarous_result = grammarous#get_errors_from_xml(s:XML.parse(substitute(a:xml, "\n", '', 'g')))
|
|
|
|
let parsed = s:last_parsed_options
|
|
|
|
|
|
|
|
if s:is_comment_only(parsed['comments-only'])
|
|
|
|
call filter(b:grammarous_result, 'synIDattr(synID(v:val.fromy+1, v:val.fromx+1, 0), "name") =~? "comment"')
|
|
|
|
endif
|
|
|
|
|
|
|
|
redraw!
|
|
|
|
if empty(b:grammarous_result)
|
|
|
|
echomsg 'Yay! No grammatical errors detected.'
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
let len = len(b:grammarous_result)
|
|
|
|
echomsg printf('Detected %d grammatical error%s', len, len > 1 ? 's' : '')
|
|
|
|
call grammarous#highlight_errors_in_current_buffer(b:grammarous_result)
|
|
|
|
if parsed['move-to-first-error']
|
|
|
|
call cursor(b:grammarous_result[0].fromy+1, b:grammarous_result[0].fromx+1)
|
|
|
|
endif
|
|
|
|
|
|
|
|
if g:grammarous#enable_spell_check
|
|
|
|
let s:saved_spell = &l:spell
|
|
|
|
setlocal spell
|
|
|
|
endif
|
|
|
|
|
|
|
|
if g:grammarous#use_location_list
|
|
|
|
call s:set_errors_to_location_list()
|
|
|
|
endif
|
|
|
|
|
|
|
|
if g:grammarous#show_first_error
|
|
|
|
call grammarous#create_update_info_window_of(b:grammarous_result)
|
|
|
|
endif
|
|
|
|
|
|
|
|
if has_key(g:grammarous#hooks, 'on_check')
|
|
|
|
call call(g:grammarous#hooks.on_check, [b:grammarous_result], g:grammarous#hooks)
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:on_check_done_vim8(channel) abort
|
|
|
|
let xml = ''
|
|
|
|
while ch_status(a:channel, {'part' : 'out'}) ==# 'buffered'
|
|
|
|
let xml .= ch_read(a:channel)
|
|
|
|
endwhile
|
|
|
|
if xml ==# ''
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
call s:set_errors_from_xml_string(xml)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:on_check_exit_vim8(channel, status) abort
|
|
|
|
if a:status == 0
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
let err = ''
|
|
|
|
while ch_status(a:channel, {'part' : 'err'}) ==# 'buffered'
|
|
|
|
let err .= ch_read(a:channel, {'part' : 'err'})
|
|
|
|
endwhile
|
|
|
|
call grammarous#error('Grammar check failed with exit status ' . a:status . ': ' . err)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:on_exit_nvim(job, status, event) abort dict
|
|
|
|
if a:status != 0
|
|
|
|
call grammarous#error('Grammar check failed: ' . self._stderr)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
call s:set_errors_from_xml_string(self._stdout)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:on_output_nvim(job, lines, event) abort dict
|
|
|
|
let output = join(a:lines, "\n")
|
|
|
|
if a:event ==# 'stdout'
|
|
|
|
let self._stdout .= output
|
|
|
|
else
|
|
|
|
let self._stderr .= output
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:invoke_check(range_start, ...)
|
|
|
|
if g:grammarous#languagetool_cmd ==# ''
|
|
|
|
let jar = s:find_jar_path()
|
|
|
|
if jar ==# ''
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
|
|
|
if a:0 < 1
|
|
|
|
call grammarous#error('Invalid argument. At least one argument is required.')
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
if g:grammarous#use_vim_spelllang
|
|
|
|
" Convert vim spelllang to languagetool spelllang
|
|
|
|
if len(split(&spelllang, '_')) == 1
|
|
|
|
let lang = split(&spelllang, '_')[0]
|
|
|
|
elseif len(split(&spelllang, '_')) == 2
|
|
|
|
let lang = split(&spelllang, '_')[0].'-'.toupper(split(&spelllang, '_')[1])
|
|
|
|
endif
|
|
|
|
else
|
|
|
|
let lang = a:0 == 1 ? g:grammarous#default_lang : a:1
|
|
|
|
endif
|
|
|
|
let text = s:make_text(a:0 == 1 ? a:1 : a:2)
|
|
|
|
|
|
|
|
let tmpfile = tempname()
|
|
|
|
execute 'redir! >' tmpfile
|
|
|
|
let l = 1
|
|
|
|
while l < a:range_start
|
|
|
|
silent echo ""
|
|
|
|
let l += 1
|
|
|
|
endwhile
|
|
|
|
silent echon text
|
|
|
|
redir END
|
|
|
|
|
|
|
|
if s:is_cygwin
|
|
|
|
let tmpfile = s:cygpath(tmpfile)
|
|
|
|
endif
|
|
|
|
|
|
|
|
let cmdargs = printf(
|
|
|
|
\ '-c %s -l %s --api %s',
|
|
|
|
\ &fileencoding ? &fileencoding : &encoding,
|
|
|
|
\ lang,
|
|
|
|
\ substitute(tmpfile, '\\\s\@!', '\\\\', 'g')
|
|
|
|
\ )
|
|
|
|
|
|
|
|
let disabled_rules = get(g:grammarous#disabled_rules, &filetype, get(g:grammarous#disabled_rules, '*', []))
|
|
|
|
if !empty(disabled_rules)
|
|
|
|
let cmdargs = '-d ' . join(disabled_rules, ',') . ' ' . cmdargs
|
|
|
|
endif
|
|
|
|
|
|
|
|
let enabled_rules = get(g:grammarous#enabled_rules, &filetype, get(g:grammarous#enabled_rules, '*', []))
|
|
|
|
if !empty(enabled_rules)
|
|
|
|
let cmdargs = '-e ' . join(enabled_rules, ',') . ' ' . cmdargs
|
|
|
|
endif
|
|
|
|
|
|
|
|
let disabled_categories = get(g:grammarous#disabled_categories, &filetype, get(g:grammarous#disabled_categories, '*', []))
|
|
|
|
if !empty(disabled_categories)
|
|
|
|
let cmdargs = '--disablecategories ' . join(disabled_categories, ',') . ' ' . cmdargs
|
|
|
|
endif
|
|
|
|
|
|
|
|
let enabled_categories = get(g:grammarous#enabled_categories, &filetype, get(g:grammarous#enabled_categories, '*', []))
|
|
|
|
if !empty(enabled_categories)
|
|
|
|
let cmdargs = '--enablecategories ' . join(enabled_categories, ',') . ' ' . cmdargs
|
|
|
|
endif
|
|
|
|
|
|
|
|
if g:grammarous#languagetool_cmd !=# ''
|
|
|
|
let cmd = printf('%s %s', g:grammarous#languagetool_cmd, cmdargs)
|
|
|
|
else
|
|
|
|
let cmd = printf('%s -jar %s %s', g:grammarous#java_cmd, substitute(jar, '\\\s\@!', '\\\\', 'g'), cmdargs)
|
|
|
|
endif
|
|
|
|
|
|
|
|
if s:job_is_available
|
|
|
|
let job = job_start(cmd, {'close_cb' : s:SID . 'on_check_done_vim8', 'exit_cb' : s:SID . 'on_check_exit_vim8'})
|
|
|
|
echo 'Grammar check has started with job(' . job . ')...'
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
if has('nvim')
|
|
|
|
let opts = {
|
|
|
|
\ 'on_stdout' : function('s:on_output_nvim'),
|
|
|
|
\ 'on_stderr' : function('s:on_output_nvim'),
|
|
|
|
\ 'on_exit' : function('s:on_exit_nvim'),
|
|
|
|
\ '_stdout' : '',
|
|
|
|
\ '_stderr' : '',
|
|
|
|
\ }
|
|
|
|
let job = jobstart(cmd, opts)
|
|
|
|
echo 'Grammar check has started with job(id: ' . job . ')...'
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
let xml = s:P.system(cmd)
|
|
|
|
call delete(tmpfile)
|
|
|
|
|
|
|
|
if s:P.get_last_status()
|
|
|
|
call grammarous#error("Command '%s' failed:\n%s", cmd, xml)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
call s:set_errors_from_xml_string(xml)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:sanitize(s)
|
|
|
|
return substitute(escape(a:s, "'\\"), ' ', '\\_\\s', 'g')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#generate_highlight_pattern(error)
|
|
|
|
let line = a:error.fromy + 1
|
|
|
|
let prefix = a:error.contextoffset > 0 ? s:sanitize(a:error.context[: a:error.contextoffset-1]) : ''
|
|
|
|
let rest = a:error.context[a:error.contextoffset :]
|
|
|
|
let the_error = s:sanitize(rest[: a:error.errorlength-1])
|
|
|
|
let rest = s:sanitize(rest[a:error.errorlength :])
|
|
|
|
return '\V' . prefix . '\zs' . the_error . '\ze' . rest
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:unescape_xml(str)
|
|
|
|
let s = substitute(a:str, '"', '"', 'g')
|
|
|
|
let s = substitute(s, ''', "'", 'g')
|
|
|
|
let s = substitute(s, '>', '>', 'g')
|
|
|
|
let s = substitute(s, '<', '<', 'g')
|
|
|
|
return substitute(s, '&', '\&', 'g')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:unescape_error(err)
|
|
|
|
for e in ['context', 'msg', 'replacements']
|
|
|
|
let a:err[e] = s:unescape_xml(a:err[e])
|
|
|
|
endfor
|
|
|
|
return a:err
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#get_errors_from_xml(xml)
|
|
|
|
return map(filter(a:xml.childNodes(), 'v:val.name ==# "error"'), 's:unescape_error(v:val.attr)')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:matcherrpos(...)
|
|
|
|
return matchaddpos('GrammarousError', [a:000], 999)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:highlight_error(from, to)
|
|
|
|
if a:from[0] == a:to[0]
|
|
|
|
return s:matcherrpos(a:from[0], a:from[1], a:to[1] - a:from[1])
|
|
|
|
endif
|
|
|
|
|
|
|
|
let ids = [s:matcherrpos(a:from[0], a:from[1], strlen(getline(a:from[0]))+1 - a:from[1])]
|
|
|
|
let line = a:from[0] + 1
|
|
|
|
while line < a:to[0]
|
|
|
|
call add(ids, s:matcherrpos(line))
|
|
|
|
let line += 1
|
|
|
|
endwhile
|
|
|
|
call add(ids, s:matcherrpos(a:to[0], 1, a:to[1] - 1))
|
|
|
|
return ids
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:remove_3dots(str)
|
|
|
|
return substitute(substitute(a:str, '\.\.\.$', '', ''), '\\V\zs\.\.\.', '', '')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#highlight_errors_in_current_buffer(errs)
|
|
|
|
if !g:grammarous#use_fallback_highlight
|
|
|
|
for e in a:errs
|
|
|
|
let e.id = s:highlight_error(
|
|
|
|
\ [str2nr(e.fromy)+1, str2nr(e.fromx)+1],
|
|
|
|
\ [str2nr(e.toy)+1, str2nr(e.tox)+1],
|
|
|
|
\ )
|
|
|
|
endfor
|
|
|
|
else
|
|
|
|
for e in a:errs
|
|
|
|
let e.id = matchadd(
|
|
|
|
\ 'GrammarousError',
|
|
|
|
\ s:remove_3dots(grammarous#generate_highlight_pattern(e)),
|
|
|
|
\ 999
|
|
|
|
\ )
|
|
|
|
endfor
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#reset_highlights()
|
|
|
|
for m in filter(getmatches(), 'v:val.group ==# "GrammarousError"')
|
|
|
|
call matchdelete(m.id)
|
|
|
|
endfor
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#find_checked_winnr() abort
|
|
|
|
if exists('b:grammarous_result')
|
|
|
|
return winnr()
|
|
|
|
endif
|
|
|
|
for bufnr in tabpagebuflist()
|
|
|
|
let result = getbufvar(bufnr, 'grammarous_result', [])
|
|
|
|
if empty(result)
|
|
|
|
continue
|
|
|
|
endif
|
|
|
|
|
|
|
|
let winnr = bufwinnr(bufnr)
|
|
|
|
if winnr == -1
|
|
|
|
continue
|
|
|
|
endif
|
|
|
|
|
|
|
|
return winnr
|
|
|
|
endfor
|
|
|
|
return -1
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#reset()
|
|
|
|
let win = grammarous#find_checked_winnr()
|
|
|
|
if win == -1
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
let prev_win = winnr()
|
|
|
|
if win != prev_win
|
|
|
|
execute win . 'wincmd w'
|
|
|
|
endif
|
|
|
|
|
|
|
|
if g:grammarous#use_location_list
|
|
|
|
lclose
|
|
|
|
lgetexpr []
|
|
|
|
endif
|
|
|
|
|
|
|
|
call grammarous#reset_highlights()
|
|
|
|
call grammarous#info_win#stop_auto_preview()
|
|
|
|
call grammarous#info_win#close()
|
|
|
|
if exists('s:saved_spell')
|
|
|
|
let &l:spell = s:saved_spell
|
|
|
|
unlet s:saved_spell
|
|
|
|
endif
|
|
|
|
if has_key(g:grammarous#hooks, 'on_reset')
|
|
|
|
call call(g:grammarous#hooks.on_reset, [b:grammarous_result], g:grammarous#hooks)
|
|
|
|
endif
|
|
|
|
unlet! b:grammarous_result b:grammarous_preview_bufnr
|
|
|
|
|
|
|
|
if win != prev_win
|
|
|
|
wincmd p
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
let s:opt_parser = s:O.new()
|
|
|
|
\.on('--lang=VALUE', 'language to check', {'default' : g:grammarous#default_lang})
|
|
|
|
\.on('--[no-]preview', 'enable auto preview', {'default' : 1})
|
|
|
|
\.on('--[no-]comments-only', 'check comment only', {'default' : ''})
|
|
|
|
\.on('--[no-]move-to-first-error', 'move to first error', {'default' : g:grammarous#move_to_first_error})
|
|
|
|
\.on('--reinstall-languagetool', 'reinstall LanguageTool', {'default' : 0})
|
|
|
|
|
|
|
|
function! grammarous#complete_opt(arglead, cmdline, cursorpos)
|
|
|
|
return s:opt_parser.complete(a:arglead, a:cmdline, a:cursorpos)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:is_comment_only(option)
|
|
|
|
if type(a:option) == type(0)
|
|
|
|
return a:option
|
|
|
|
endif
|
|
|
|
|
|
|
|
return get(
|
|
|
|
\ g:grammarous#default_comments_only_filetypes,
|
|
|
|
\ &filetype,
|
|
|
|
\ get(g:grammarous#default_comments_only_filetypes, '*', 0)
|
|
|
|
\ )
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#check_current_buffer(qargs, range)
|
|
|
|
if exists('b:grammarous_result')
|
|
|
|
call grammarous#reset()
|
|
|
|
redraw!
|
|
|
|
endif
|
|
|
|
|
|
|
|
let parsed = s:opt_parser.parse(a:qargs, a:range, '')
|
|
|
|
if has_key(parsed, 'help')
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
let b:grammarous_auto_preview = parsed.preview
|
|
|
|
if parsed.preview
|
|
|
|
call grammarous#info_win#start_auto_preview()
|
|
|
|
endif
|
|
|
|
|
|
|
|
if parsed['reinstall-languagetool']
|
|
|
|
call s:delete_jar_dir()
|
|
|
|
endif
|
|
|
|
|
|
|
|
" XXX
|
|
|
|
let s:last_parsed_options = parsed
|
|
|
|
|
|
|
|
call s:invoke_check(
|
|
|
|
\ parsed.__range__[0],
|
|
|
|
\ parsed.lang,
|
|
|
|
\ getline(parsed.__range__[0], parsed.__range__[1])
|
|
|
|
\ )
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:less_position(p1, p2)
|
|
|
|
if a:p1[0] != a:p2[0]
|
|
|
|
return a:p1[0] < a:p2[0]
|
|
|
|
endif
|
|
|
|
|
|
|
|
return a:p1[1] < a:p2[1]
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:binary_search_by_pos(errors, the_pos, start, end)
|
|
|
|
if a:start > a:end
|
|
|
|
return {}
|
|
|
|
endif
|
|
|
|
|
|
|
|
let m = (a:start + a:end) / 2
|
|
|
|
let from = [a:errors[m].fromy+1, a:errors[m].fromx+1]
|
|
|
|
let to = [a:errors[m].toy+1, a:errors[m].tox]
|
|
|
|
|
|
|
|
if s:less_position(a:the_pos, from)
|
|
|
|
return s:binary_search_by_pos(a:errors, a:the_pos, a:start, m-1)
|
|
|
|
endif
|
|
|
|
|
|
|
|
if s:less_position(to, a:the_pos)
|
|
|
|
return s:binary_search_by_pos(a:errors, a:the_pos, m+1, a:end)
|
|
|
|
endif
|
|
|
|
|
|
|
|
return a:errors[m]
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
" Note:
|
|
|
|
" It believes all errors are sorted by its position
|
|
|
|
function! grammarous#get_error_at(pos, errs)
|
|
|
|
return s:binary_search_by_pos(a:errs, a:pos, 0, len(a:errs)-1)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#fixit(err)
|
|
|
|
if empty(a:err)
|
|
|
|
\ || !grammarous#move_to_checked_buf(a:err.fromy+1, a:err.fromx+1)
|
|
|
|
\ || a:err.replacements ==# ''
|
|
|
|
call grammarous#error('Cannot fix this error automatically.')
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
let sel_save = &l:selection
|
|
|
|
let &l:selection = 'inclusive'
|
|
|
|
let save_g_reg = getreg('g', 1)
|
|
|
|
let save_g_regtype = getregtype('g')
|
|
|
|
try
|
|
|
|
normal! v
|
|
|
|
call cursor(a:err.toy+1, a:err.tox)
|
|
|
|
noautocmd normal! "gy
|
|
|
|
let from = getreg('g')
|
|
|
|
let to = split(a:err.replacements, '#', 1)[0]
|
|
|
|
call setreg('g', to, 'v')
|
|
|
|
normal! gv"gp
|
|
|
|
|
|
|
|
call grammarous#remove_error(a:err, get(a:, 1, b:grammarous_result))
|
|
|
|
|
|
|
|
echomsg printf("Fixed: '%s' -> '%s'", from, to)
|
|
|
|
finally
|
|
|
|
call setreg('g', save_g_reg, save_g_regtype)
|
|
|
|
let &l:selection = sel_save
|
|
|
|
endtry
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#fixall(errs)
|
|
|
|
for e in a:errs
|
|
|
|
call grammarous#fixit(e)
|
|
|
|
endfor
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:move_to_pos(pos)
|
|
|
|
let p = type(a:pos[0]) == type([]) ? a:pos[0] : a:pos
|
|
|
|
return cursor(a:pos[0], a:pos[1]) != -1
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:move_to(buf, pos)
|
|
|
|
if a:buf != bufnr('%')
|
|
|
|
let winnr = bufwinnr(a:buf)
|
|
|
|
if winnr == -1
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
|
|
|
|
execute winnr . 'wincmd w'
|
|
|
|
endif
|
|
|
|
return s:move_to_pos(a:pos)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#move_to_checked_buf(...)
|
|
|
|
if exists('b:grammarous_result')
|
|
|
|
return s:move_to_pos(a:000)
|
|
|
|
endif
|
|
|
|
|
|
|
|
if exists('b:grammarous_preview_original_bufnr')
|
|
|
|
return s:move_to(b:grammarous_preview_original_bufnr, a:000)
|
|
|
|
endif
|
|
|
|
|
|
|
|
for b in tabpagebuflist()
|
|
|
|
if !empty(getbufvar(b, 'grammarous_result', []))
|
|
|
|
return s:move_to(b, a:000)
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
|
|
|
return 0
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#create_update_info_window_of(errs)
|
|
|
|
let e = grammarous#get_error_at(getpos('.')[1 : 2], a:errs)
|
|
|
|
if empty(e)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
if exists('b:grammarous_preview_bufnr')
|
|
|
|
let winnr = bufwinnr(b:grammarous_preview_bufnr)
|
|
|
|
if winnr == -1
|
|
|
|
let bufnr = grammarous#info_win#open(e, bufnr('%'))
|
|
|
|
else
|
|
|
|
execute winnr . 'wincmd w'
|
|
|
|
let bufnr = grammarous#info_win#update(e)
|
|
|
|
endif
|
|
|
|
else
|
|
|
|
let bufnr = grammarous#info_win#open(e, bufnr('%'))
|
|
|
|
endif
|
|
|
|
|
|
|
|
wincmd p
|
|
|
|
let b:grammarous_preview_bufnr = bufnr
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#create_and_jump_to_info_window_of(errs)
|
|
|
|
call grammarous#create_update_info_window_of(a:errs)
|
|
|
|
wincmd p
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:remove_error_highlight(e)
|
|
|
|
let ids = type(a:e.id) == type([]) ? a:e.id : [a:e.id]
|
|
|
|
for i in ids
|
|
|
|
silent! if matchdelete(i) == -1
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
return 1
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#remove_error(e, errs)
|
|
|
|
if !s:remove_error_highlight(a:e)
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
|
|
|
|
for i in range(len(a:errs))
|
|
|
|
if type(a:errs[i].id) == type(a:e.id) && a:errs[i].id == a:e.id
|
|
|
|
call grammarous#info_win#close()
|
|
|
|
unlet a:errs[i]
|
|
|
|
return 1
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
|
|
|
return 0
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#remove_error_at(pos, errs)
|
|
|
|
let e = grammarous#get_error_at(a:pos, a:errs)
|
|
|
|
if empty(e)
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
|
|
|
|
return grammarous#remove_error(e, a:errs)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#disable_rule(rule, errs)
|
|
|
|
call grammarous#info_win#close()
|
|
|
|
|
|
|
|
" Note:
|
|
|
|
" reverse() is needed because of removing elements in list
|
|
|
|
for i in reverse(range(len(a:errs)))
|
|
|
|
let e = a:errs[i]
|
|
|
|
if e.ruleId ==# a:rule
|
|
|
|
if !s:remove_error_highlight(e)
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
unlet a:errs[i]
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
|
|
|
echomsg 'Disabled rule: ' . a:rule
|
|
|
|
|
|
|
|
return 1
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#disable_rule_at(pos, errs)
|
|
|
|
let e = grammarous#get_error_at(a:pos, a:errs)
|
|
|
|
if empty(e)
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
|
|
|
|
return grammarous#disable_rule(e.ruleId, a:errs)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#disable_category(category, errs)
|
|
|
|
call grammarous#info_win#close()
|
|
|
|
|
|
|
|
" Note:
|
|
|
|
" reverse() is needed because of removing elements in list
|
|
|
|
for i in reverse(range(len(a:errs)))
|
|
|
|
let e = a:errs[i]
|
|
|
|
|
|
|
|
if e.categoryid ==# a:category
|
|
|
|
if !s:remove_error_highlight(e)
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
unlet a:errs[i]
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
|
|
|
echomsg 'Disabled category: ' . a:category
|
|
|
|
|
|
|
|
return 1
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#disable_category_at(pos, errs)
|
|
|
|
let e = grammarous#get_error_at(a:pos, a:errs)
|
|
|
|
if empty(e)
|
|
|
|
return 0
|
|
|
|
endif
|
|
|
|
|
|
|
|
return grammarous#disable_category(e.categoryid, a:errs)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#move_to_next_error(pos, errs)
|
|
|
|
for e in a:errs
|
|
|
|
let p = [e.fromy+1, e.fromx+1]
|
|
|
|
if s:less_position(a:pos, p)
|
|
|
|
return s:move_to_pos(p)
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
call grammarous#error('No next error found.')
|
|
|
|
return 0
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! grammarous#move_to_previous_error(pos, errs)
|
|
|
|
for e in reverse(copy(a:errs))
|
|
|
|
let p = [e.fromy+1, e.fromx+1]
|
|
|
|
if s:less_position(p, a:pos)
|
|
|
|
return s:move_to_pos(p)
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
call grammarous#error('No previous error found.')
|
|
|
|
return 0
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
let &cpo = s:save_cpo
|
|
|
|
unlet s:save_cpo
|