" Author: Takuya Fujiwara " Description: swipl syntax / semantic check for Prolog files call ale#Set('prolog_swipl_executable', 'swipl') call ale#Set('prolog_swipl_load', 'current_prolog_flag(argv, [File]), load_files(File, [sandboxed(true)]), halt.') call ale#Set('prolog_swipl_timeout', 3) call ale#Set('prolog_swipl_alarm', 'alarm(%t, (%h), _, [])') call ale#Set('prolog_swipl_alarm_handler', 'writeln(user_error, "ERROR: Exceeded %t seconds, Please change g:prolog_swipl_timeout to modify the limit."), halt(1)') function! ale_linters#prolog#swipl#GetCommand(buffer) abort let l:goals = ale#Var(a:buffer, 'prolog_swipl_load') let l:goals = l:goals =~# '^\s*$' ? 'halt' : l:goals let l:timeout = ale#Var(a:buffer, 'prolog_swipl_timeout') + 0 if l:timeout > 0 let l:goals = s:GetAlarm(a:buffer, l:timeout) . ', ' . l:goals endif return '%e -g ' . ale#Escape(l:goals) . ' -- %s' endfunction function! s:GetAlarm(buffer, timeout) abort let l:handler = ale#Var(a:buffer, 'prolog_swipl_alarm_handler') let l:handler = s:Subst(l:handler, {'t': a:timeout}) let l:alarm = ale#Var(a:buffer, 'prolog_swipl_alarm') let l:alarm = s:Subst(l:alarm, {'t': a:timeout, 'h': l:handler}) return l:alarm endfunction function! s:Subst(format, vars) abort let l:vars = extend(copy(a:vars), {'%': '%'}) return substitute(a:format, '%\(.\)', '\=get(l:vars, submatch(1), "")', 'g') endfunction let s:pattern = '\v^(ERROR|Warning)+%(:\s*[^:]+:(\d+)%(:(\d+))?)?:\s*(.*)$' function! ale_linters#prolog#swipl#Handle(buffer, lines) abort let l:output = [] let l:i = 0 while l:i < len(a:lines) let l:match = matchlist(a:lines[l:i], s:pattern) if empty(l:match) let l:i += 1 continue endif let [l:i, l:text] = s:GetErrMsg(l:i, a:lines, l:match[4]) let l:item = { \ 'lnum': (l:match[2] + 0 ? l:match[2] + 0 : 1), \ 'col': l:match[3] + 0, \ 'text': l:text, \ 'type': (l:match[1] is# 'ERROR' ? 'E' : 'W'), \} if !s:Ignore(l:item) call add(l:output, l:item) endif endwhile return l:output endfunction " This returns [, ] function! s:GetErrMsg(i, lines, text) abort let next_line = get(a:lines, a:i+1, '') let matches = matchlist(next_line, s:pattern) if !empty(matchlist) && empty(matches[2]) return s:GetContErrMsg(a:i+1, a:lines, a:text.matches[4]) endif if a:text !~# '^\s*$' return [a:i + 1, a:text] endif let l:i = a:i + 1 let l:text = [] while l:i < len(a:lines) && a:lines[l:i] =~# '^\s' call add(l:text, s:Trim(a:lines[l:i])) let l:i += 1 endwhile return [l:i, join(l:text, '. ')] endfunction function! s:GetErrMsg(i, lines, text) abort if a:text !~# '^\s*$' return [a:i + 1, a:text] endif let l:i = a:i + 1 let l:text = [] while l:i < len(a:lines) && a:lines[l:i] =~# s:pattern let matches = matchlist(a:lines[l:i], s:pattern) if !empty(matches[2]) break end call add(l:text, s:Trim(matches[4])) let l:i += 1 endwhile return [l:i, join(l:text, '. ')] endfunction function! s:Trim(str) abort return substitute(a:str, '\v^\s+|\s+$', '', 'g') endfunction " Skip sandbox error which is caused by directives " because what we want is syntactic or semantic check. function! s:Ignore(item) abort return a:item.type is# 'E' \ && a:item.text =~# '\vNo permission to (call|directive|assert) sandboxed' endfunction call ale#linter#Define('prolog', { \ 'name': 'swipl', \ 'output_stream': 'stderr', \ 'executable': {b -> ale#Var(b, 'prolog_swipl_executable')}, \ 'command': function('ale_linters#prolog#swipl#GetCommand'), \ 'callback': 'ale_linters#prolog#swipl#Handle', \})