From 1ca32fce79d64974357dd74fc297b739364013b6 Mon Sep 17 00:00:00 2001 From: Wang Shidong Date: Sun, 14 Mar 2021 15:12:00 +0800 Subject: [PATCH] Add task problem matcher (#4127) --- .SpaceVim.d/tasks.toml | 17 ++++++ autoload/SpaceVim/api/vim/buffer.vim | 2 +- autoload/SpaceVim/layers/shell.vim | 2 +- autoload/SpaceVim/mapping.vim | 3 +- autoload/SpaceVim/plugins/iedit.vim | 3 +- autoload/SpaceVim/plugins/runner.vim | 84 +++++++++++++++++++++++--- bundle/git.vim/autoload/git/commit.vim | 3 +- bundle/git.vim/autoload/git/rebase.vim | 3 +- docs/documentation.md | 37 ++++++++++++ 9 files changed, 139 insertions(+), 15 deletions(-) diff --git a/.SpaceVim.d/tasks.toml b/.SpaceVim.d/tasks.toml index 493ea9574..aed69eca5 100644 --- a/.SpaceVim.d/tasks.toml +++ b/.SpaceVim.d/tasks.toml @@ -3,3 +3,20 @@ isBackground = false [file-run.options] cwd = '${workspaceFolder}bin/' +[test_problemMatcher] + command = "echo" + args = ['.SpaceVim.d/tasks.toml:6:1 test error message'] + isBackground = true +[test_problemMatcher.problemMatcher] + errorformat = '%f:%l:%c\ %m' +[test_regexp] + command = "echo" + args = ['.SpaceVim.d/tasks.toml:12:1 test error message'] + isBackground = true +[test_regexp.problemMatcher.pattern] + regexp = '\(.*\):\(\d\+\):\(\d\+\)\s\(\S.*\)' + file = 1 + line = 2 + column = 3 + #severity = 4 + message = 4 diff --git a/autoload/SpaceVim/api/vim/buffer.vim b/autoload/SpaceVim/api/vim/buffer.vim index 4d2dd1ae6..8aa3d9896 100644 --- a/autoload/SpaceVim/api/vim/buffer.vim +++ b/autoload/SpaceVim/api/vim/buffer.vim @@ -92,7 +92,7 @@ if exists('*nvim_create_buf') else function! s:self.create_buf(listed, scratch) abort let bufnr = self.bufadd('') - " in vim, a:listed must be number, what the fuck! + " in vim, a:listed must be number " why can not use v:true and v:false call setbufvar(bufnr, '&buflisted', a:listed ? 1 : 0) if a:scratch diff --git a/autoload/SpaceVim/layers/shell.vim b/autoload/SpaceVim/layers/shell.vim index 0f63224ef..fb6537876 100644 --- a/autoload/SpaceVim/layers/shell.vim +++ b/autoload/SpaceVim/layers/shell.vim @@ -158,7 +158,7 @@ function! s:open_default_shell(open_with_file_cwd) abort if getwinvar(window, '&buftype') ==# 'terminal' exe window . 'wincmd w' if getbufvar(winbufnr(window), '_spacevim_shell_cwd') ==# l:path - " fuck gvim bug, startinsert do not work in gvim + " startinsert do not work in gvim if has('nvim') startinsert else diff --git a/autoload/SpaceVim/mapping.vim b/autoload/SpaceVim/mapping.vim index 5280c8d10..19bb87158 100644 --- a/autoload/SpaceVim/mapping.vim +++ b/autoload/SpaceVim/mapping.vim @@ -217,7 +217,8 @@ function! SpaceVim#mapping#close_term_buffer(...) abort if bufexists(abuf) exe 'bd!' . abuf endif - " fuck the terminal windows + " can not close the terminal windows + " close again if get(w:, 'shell_layer_win', 0) == 1 close endif diff --git a/autoload/SpaceVim/plugins/iedit.vim b/autoload/SpaceVim/plugins/iedit.vim index 6d1a494f6..dd1ea4ac5 100644 --- a/autoload/SpaceVim/plugins/iedit.vim +++ b/autoload/SpaceVim/plugins/iedit.vim @@ -84,7 +84,8 @@ function! SpaceVim#plugins#iedit#start(...) abort let curpos = getcurpos() let argv = get(a:000, 0, '') let save_reg_k = @k - " what the fuck, why register " is cleared. + " the register " is cleared + " save the register context before run following command let save_reg_default = @" let use_expr = 0 if !empty(argv) && type(argv) == 4 diff --git a/autoload/SpaceVim/plugins/runner.vim b/autoload/SpaceVim/plugins/runner.vim index 05fc12ab4..99b8e48bf 100644 --- a/autoload/SpaceVim/plugins/runner.vim +++ b/autoload/SpaceVim/plugins/runner.vim @@ -34,6 +34,10 @@ let s:status = { \ 'exit_code' : 0 \ } +let s:task_stdout = {} +let s:task_stderr = {} +let s:task_problem_matcher = {} + function! s:open_win() abort if s:bufnr !=# 0 && bufexists(s:bufnr) && index(tabpagebuflist(), s:bufnr) !=# -1 return @@ -78,7 +82,7 @@ function! s:async_run(runner, ...) abort catch let cmd = a:runner endtry - call SpaceVim#logger#info(' cmd:' . string(cmd)) + call s:LOGGER.info(' cmd:' . string(cmd)) call s:BUFFER.buf_set_lines(s:bufnr, s:lines , -1, 0, ['[Running] ' . cmd, '', repeat('-', 20)]) let s:lines += 3 let s:start_time = reltime() @@ -156,7 +160,7 @@ function! s:async_run(runner, ...) abort else let cmd = exe + a:runner.opt + [get(s:, 'selected_file', bufname('%'))] endif - call SpaceVim#logger#info(' cmd:' . string(cmd)) + call s:LOGGER.info(' cmd:' . string(cmd)) call s:BUFFER.buf_set_lines(s:bufnr, s:lines , -1, 0, ['[Running] ' . join(cmd) . (usestdin ? ' STDIN' : ''), '', repeat('-', 20)]) let s:lines += 3 let s:start_time = reltime() @@ -342,8 +346,8 @@ function! SpaceVim#plugins#runner#select_file() abort let runner = get(a:000, 0, get(s:runners, &filetype, '')) let s:selected_language = &filetype if !empty(runner) - call SpaceVim#logger#info('Code runner startting:') - call SpaceVim#logger#info('selected file :' . s:selected_file) + call s:LOGGER.info('Code runner startting:') + call s:LOGGER.info('selected file :' . s:selected_file) call s:open_win() call s:async_run(runner) call s:update_statusline() @@ -392,16 +396,76 @@ function! SpaceVim#plugins#runner#run_task(task) abort if !empty(opts) && has_key(opts, 'env') && !empty(opts.env) call extend(opt, {'env' : opts.env}) endif + let problemMatcher = get(a:task, 'problemMatcher', {}) if isBackground - call s:run_backgroud(cmd, opt) + call s:run_backgroud(cmd, opt, problemMatcher) else - call SpaceVim#plugins#runner#open(cmd, opt) + call SpaceVim#plugins#runner#open(cmd, opt, problemMatcher) endif endif endfunction +function! s:match_problems(output, matcher) abort + if has_key(a:matcher, 'pattern') + let pattern = a:matcher.pattern + let items = [] + for line in a:output + let rst = matchlist(line, pattern.regexp) + let file = get(rst, get(pattern, 'file', 1), '') + let line = get(rst, get(pattern, 'line', 2), 1) + let column = get(rst, get(pattern, 'column', 3), 1) + let message = get(rst, get(pattern, 'message', 4), '') + if !empty(file) + call add(items, { + \ 'filename' : file, + \ 'lnum' : line, + \ 'col' : column, + \ 'text' : message, + \ }) + endif + endfor + call setqflist([], 'r', {'title' : ' task output', + \ 'items' : items, + \ }) + copen + copen + else + try + let olderrformat = &errorformat + if has_key(a:matcher, 'errorformat') + let &errorformat = a:matcher.errorformat + let cmd = 'noautocmd cexpr a:output' + exe cmd + call setqflist([], 'a', {'title' : ' task output'}) + copen + endif + finally + let &errorformat = olderrformat + endtry + endif +endfunction + +function! s:on_backgroud_stdout(job_id, data, event) abort + let data = get(s:task_stdout, 'task' . a:job_id, []) + a:data + let s:task_stdout['task' . a:job_id] = data +endfunction + +function! s:on_backgroud_stderr(job_id, data, event) abort + let data = get(s:task_stderr, 'task' . a:job_id, []) + a:data + let s:task_stderr['task' . a:job_id] = data +endfunction + function! s:on_backgroud_exit(job_id, data, event) abort let s:end_time = reltime(s:start_time) + let task_problem_matcher = get(s:task_problem_matcher, 'task' . a:job_id, {}) + if get(task_problem_matcher, 'useStdout', 0) + let output = get(s:task_stdout, 'task' . a:job_id, []) + else + let output = get(s:task_stderr, 'task' . a:job_id, []) + endif + if !empty(task_problem_matcher) && !empty(output) + call s:match_problems(output, task_problem_matcher) + endif let exit_code = a:data echo 'task finished with code=' . a:data . ' in ' . s:STRING.trim(reltimestr(s:end_time)) . ' seconds' endfunction @@ -410,7 +474,13 @@ function! s:run_backgroud(cmd, ...) abort echo 'task running' let opts = get(a:000, 0, {}) let s:start_time = reltime() - call s:JOB.start(a:cmd,extend({ + let problemMatcher = get(a:000, 1, {}) + if !has_key(problemMatcher, 'errorformat') && !has_key(problemMatcher, 'regexp') + call extend(problemMatcher, {'errorformat' : &errorformat}) + endif + let task_id = s:JOB.start(a:cmd,extend({ + \ 'on_stdout' : function('s:on_backgroud_stdout'), \ 'on_exit' : function('s:on_backgroud_exit'), \ }, opts)) + call extend(s:task_problem_matcher, {'task' . task_id : problemMatcher}) endfunction diff --git a/bundle/git.vim/autoload/git/commit.vim b/bundle/git.vim/autoload/git/commit.vim index b4b568c24..501ba803a 100644 --- a/bundle/git.vim/autoload/git/commit.vim +++ b/bundle/git.vim/autoload/git/commit.vim @@ -98,8 +98,7 @@ endfunction " :w -- BufWriteCmd " p -- WinLeave " :wq -- QuitPre -> BufWriteCmd -> WinLeave -" fuck when run `:wq` the commit window will not be closed -" @fixme what the fuck +" when run `:wq` the commit window will not be closed " :q -- QuitPre -> WinLeave function! s:BufWriteCmd() abort let commit_file = '.git\COMMIT_EDITMSG' diff --git a/bundle/git.vim/autoload/git/rebase.vim b/bundle/git.vim/autoload/git/rebase.vim index 43e63ee2c..796b62e94 100644 --- a/bundle/git.vim/autoload/git/rebase.vim +++ b/bundle/git.vim/autoload/git/rebase.vim @@ -68,8 +68,7 @@ endfunction " :w -- BufWriteCmd " p -- WinLeave " :wq -- QuitPre -> BufWriteCmd -> WinLeave -" fuck when run `:wq` the commit window will not be closed -" @fixme what the fuck +" when run `:wq` the commit window will not be closed " :q -- QuitPre -> WinLeave function! s:BufWriteCmd() abort let commit_file = '.git\COMMIT_EDITMSG' diff --git a/docs/documentation.md b/docs/documentation.md index 5ef08fddd..4b7715114 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -84,6 +84,7 @@ description: "General documentation about how to using SpaceVim, including the q - [Bookmarks management](#bookmarks-management) - [Tasks](#tasks) - [Custom tasks](#custom-tasks) + - [Task Problems Matcher](#task-problems-matcher) - [Task auto-detection](#task-auto-detection) - [Task provider](#task-provider) - [Replace text with iedit](#replace-text-with-iedit) @@ -2074,6 +2075,7 @@ The task's properties have the following semantic: - **isBackground**: `true` or `false`, specifies whether background running is required, by default, it is `false`. - **description**: short description of the task +- **problemMatcher**: problems matcher of the task When start a new task, it will kill the previous task. If you want to keep the task run in background, set `isBackground` to `true`. @@ -2109,6 +2111,41 @@ So you will have the following values for each variable: - **\${fileExtname}**: - `.ext` - **\${lineNumber}**: - line number of the cursor +#### Task Problems Matcher + +Problem matcher is used to capture the message in the task output +and show a corresponding problem in quickfix windows. + +`problemMatcher` supports `errorformat` and `pattern` property. + +If `errorformat` property is not defined, `&errorformat` option +will be used. + +```toml +[tesk_errorformat] + command = "echo" + args = ['.SpaceVim.d/tasks.toml:6:1 test error message'] + isBackground = true +[tesk_errorformat.problemMatcher] + errorformat = '%f:%l:%c\ %m' +``` + +If `pattern` is defined, the `errorformat` option will be ignored. +Here is an example: + +```toml +[test_regexp] + command = "echo" + args = ['.SpaceVim.d/tasks.toml:12:1 test error message'] + isBackground = true +[test_regexp.problemMatcher.pattern] + regexp = '\(.*\):\(\d\+\):\(\d\+\)\s\(\S.*\)' + file = 1 + line = 2 + column = 3 + message = 4 +``` + #### Task auto-detection Currently, SpaceVim can auto-detect tasks for npm.