From 4e071060d102d074704d0efb6a1d091d6d67cf88 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Fri, 20 Nov 2015 01:34:46 +0200 Subject: [PATCH 01/36] jobcontrol: first implementation of jobstart on Neovim --- autoload/go/jobcontrol.vim | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 autoload/go/jobcontrol.vim diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim new file mode 100644 index 0000000000..0fe4eada5c --- /dev/null +++ b/autoload/go/jobcontrol.vim @@ -0,0 +1,73 @@ +let Go = {} + +function Go.on_stdout(job_id, data) + call extend(self.stdout, a:data) +endfunction + +function Go.on_stderr(job_id, data) + call extend(self.stderr, a:data) +endfunction + +function Go.on_exit(job_id, data) + call self.quickfix() +endfunction + +function Go.quickfix() + " echo self.stderr + " return + + call go#tool#ShowErrors(join(self.stderr, "\n")) + let errors = getqflist() + call go#util#Cwindow(len(errors)) + if !empty(errors) + cc 1 "jump to first error if there is any + endif +endfunction + +function Go.cmd(...) + let argv = ['go'] + if a:0 > 0 + call extend(argv, a:000) " append the subcommand, such as 'build' + else + redraws! | echon "vim-go: " | echohl ErrorMsg | echon "subcommand not passed"| echohl None + return -1 + endif + + " each Go instance has these local fields. + " name: defines the subcommand + " stderr: stores the stderr + " stdout: stores the stdout + let fields = { + \ 'name': a:1, + \ 'stderr':[], + \ 'stdout':[] + \ } + + " populate the instace with on_stdout, on_stderr ... functions. These are + " needed by jobstart. + let instance = extend(copy(g:Go), fields) + + " modify GOPATH if needed + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + " execute go build in the files directory + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + " run, forrest, run! + let instance.id = jobstart(argv, instance) + finally + execute cd . fnameescape(dir) + endtry + + " restore back GOPATH + let $GOPATH = old_gopath + + return instance +endfunction + +let job1 = Go.cmd('build', '.', 'errors') + +" vim:ts=4:sw=4:et From 386344507a65d18a827e60b84376bd9d4568f948 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Fri, 20 Nov 2015 01:38:28 +0200 Subject: [PATCH 02/36] jobcontrol: separate setting and showing processes --- autoload/go/jobcontrol.vim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 0fe4eada5c..81592fd438 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -9,14 +9,11 @@ function Go.on_stderr(job_id, data) endfunction function Go.on_exit(job_id, data) - call self.quickfix() + call self.set_quickfix() + call self.show_quickfix() endfunction -function Go.quickfix() - " echo self.stderr - " return - - call go#tool#ShowErrors(join(self.stderr, "\n")) +function Go.show_quickfix() let errors = getqflist() call go#util#Cwindow(len(errors)) if !empty(errors) @@ -24,6 +21,10 @@ function Go.quickfix() endif endfunction +function Go.set_quickfix() + call go#tool#ShowErrors(join(self.stderr, "\n")) +endfunction + function Go.cmd(...) let argv = ['go'] if a:0 > 0 From f4f8cd7a62188d7d871b9e52e39b824edd583ef8 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Fri, 20 Nov 2015 11:28:40 +0200 Subject: [PATCH 03/36] jobcontrol: working version of build, need testing though --- autoload/go/cmd.vim | 5 ++++- autoload/go/jobcontrol.vim | 31 +++++++++++++++---------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 3720598c70..3f46833bd8 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -33,12 +33,15 @@ function! go#cmd#Build(bang, ...) echon "vim-go: " | echohl Identifier | echon "building ..."| echohl None if g:go_dispatch_enabled && exists(':Make') == 2 silent! exe 'Make' + elseif has('nvim') + let job1 = go#jobcontrol#Run(['build', '.', 'errors']) + " rest is handled by go#jobcontrol#run :) + return else silent! exe 'make!' endif redraw! - let errors = getqflist() call go#util#Cwindow(len(errors)) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 81592fd438..6e21f8946d 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -1,19 +1,19 @@ -let Go = {} +let GoCommandJob = {} -function Go.on_stdout(job_id, data) +function GoCommandJob.on_stdout(job_id, data) call extend(self.stdout, a:data) endfunction -function Go.on_stderr(job_id, data) +function GoCommandJob.on_stderr(job_id, data) call extend(self.stderr, a:data) endfunction -function Go.on_exit(job_id, data) +function GoCommandJob.on_exit(job_id, data) call self.set_quickfix() call self.show_quickfix() endfunction -function Go.show_quickfix() +function GoCommandJob.show_quickfix() let errors = getqflist() call go#util#Cwindow(len(errors)) if !empty(errors) @@ -21,32 +21,27 @@ function Go.show_quickfix() endif endfunction -function Go.set_quickfix() +function GoCommandJob.set_quickfix() call go#tool#ShowErrors(join(self.stderr, "\n")) endfunction -function Go.cmd(...) +function GoCommandJob.cmd(args) let argv = ['go'] - if a:0 > 0 - call extend(argv, a:000) " append the subcommand, such as 'build' - else - redraws! | echon "vim-go: " | echohl ErrorMsg | echon "subcommand not passed"| echohl None - return -1 - endif + call extend(argv, a:args) " append the subcommand, such as 'build' " each Go instance has these local fields. " name: defines the subcommand " stderr: stores the stderr " stdout: stores the stdout let fields = { - \ 'name': a:1, + \ 'name': a:args[0], \ 'stderr':[], \ 'stdout':[] \ } " populate the instace with on_stdout, on_stderr ... functions. These are " needed by jobstart. - let instance = extend(copy(g:Go), fields) + let instance = extend(copy(g:GoCommandJob), fields) " modify GOPATH if needed let old_gopath = $GOPATH @@ -69,6 +64,10 @@ function Go.cmd(...) return instance endfunction -let job1 = Go.cmd('build', '.', 'errors') +function! go#jobcontrol#Run(args) + let job1 = g:GoCommandJob.cmd(a:args) + return job1.id +endfunction + " vim:ts=4:sw=4:et From 9a40bbc3d2eb73f9ea5e017227159cd314b32dbb Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Fri, 20 Nov 2015 13:49:22 +0200 Subject: [PATCH 04/36] jobcontrol: more improvements --- autoload/go/cmd.vim | 5 ++++- autoload/go/jobcontrol.vim | 16 ++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 3f46833bd8..fb1cea8785 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -34,8 +34,11 @@ function! go#cmd#Build(bang, ...) if g:go_dispatch_enabled && exists(':Make') == 2 silent! exe 'Make' elseif has('nvim') + " autowrite is not enabled for jobs + call go#cmd#autowrite() let job1 = go#jobcontrol#Run(['build', '.', 'errors']) - " rest is handled by go#jobcontrol#run :) + + " rest is handled by go#jobcontrol#Run :) return else silent! exe 'make!' diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 6e21f8946d..d0b479aefc 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -9,20 +9,24 @@ function GoCommandJob.on_stderr(job_id, data) endfunction function GoCommandJob.on_exit(job_id, data) - call self.set_quickfix() + call self.update_quickfix() call self.show_quickfix() endfunction function GoCommandJob.show_quickfix() let errors = getqflist() call go#util#Cwindow(len(errors)) - if !empty(errors) - cc 1 "jump to first error if there is any - endif + " redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None endfunction -function GoCommandJob.set_quickfix() - call go#tool#ShowErrors(join(self.stderr, "\n")) +function GoCommandJob.update_quickfix() + if empty(self.stderr) + call setqflist([]) + call go#util#Cwindow() + else + " redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None + call go#tool#ShowErrors(join(self.stderr, "\n")) + endif endfunction function GoCommandJob.cmd(args) From df7076799787e32df83652f4171bdb22ebba9eb0 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Fri, 20 Nov 2015 13:54:32 +0200 Subject: [PATCH 05/36] jobcontrol: jump back again --- autoload/go/jobcontrol.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index d0b479aefc..882d5ba555 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -16,7 +16,11 @@ endfunction function GoCommandJob.show_quickfix() let errors = getqflist() call go#util#Cwindow(len(errors)) - " redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None + if !empty(errors) + cc 1 "jump to first error if there is any + else + redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None + endif endfunction function GoCommandJob.update_quickfix() @@ -24,7 +28,7 @@ function GoCommandJob.update_quickfix() call setqflist([]) call go#util#Cwindow() else - " redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None + redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None call go#tool#ShowErrors(join(self.stderr, "\n")) endif endfunction From 7f76a16836578802ed491180ea71c875070e32b8 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 00:12:04 +0200 Subject: [PATCH 06/36] cmd: change the way we start jobs --- autoload/go/cmd.vim | 2 +- autoload/go/jobcontrol.vim | 169 +++++++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 66 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index fb1cea8785..0e1fa2d08e 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -36,7 +36,7 @@ function! go#cmd#Build(bang, ...) elseif has('nvim') " autowrite is not enabled for jobs call go#cmd#autowrite() - let job1 = go#jobcontrol#Run(['build', '.', 'errors']) + let job = go#jobcontrol#Spawn(['build', '.', 'errors']) " rest is handled by go#jobcontrol#Run :) return diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 882d5ba555..6e619451a0 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -1,81 +1,120 @@ -let GoCommandJob = {} +let s:jobs = {} -function GoCommandJob.on_stdout(job_id, data) - call extend(self.stdout, a:data) +function! go#jobcontrol#Spawn(args) + let job = s:spawn(a:args[0], a:args) + return job.id endfunction -function GoCommandJob.on_stderr(job_id, data) - call extend(self.stderr, a:data) +function! s:spawn(name, args) + let job = { + \ 'name': a:name, + \ 'stderr' : [], + \ 'stdout' : [], + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } + + " abort previous running jobs + " call self.abort(a:name) + + " modify GOPATH if needed + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + " execute go build in the files directory + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + + " append the subcommand, such as 'build' + let argv = ['go'] + a:args + " call extend(argv, a:args) + + " run, forrest, run! + let id = jobstart(argv, job) + let job.id = id + let s:jobs[id] = job + finally + execute cd . fnameescape(dir) + endtry + + " restore back GOPATH + let $GOPATH = old_gopath + return job endfunction -function GoCommandJob.on_exit(job_id, data) - call self.update_quickfix() - call self.show_quickfix() +function! s:on_stdout(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + call extend(job.stdout, a:data) endfunction -function GoCommandJob.show_quickfix() - let errors = getqflist() - call go#util#Cwindow(len(errors)) - if !empty(errors) - cc 1 "jump to first error if there is any - else - redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None - endif +function! s:on_stderr(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + call extend(job.stderr, a:data) endfunction -function GoCommandJob.update_quickfix() - if empty(self.stderr) - call setqflist([]) - call go#util#Cwindow() - else - redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None - call go#tool#ShowErrors(join(self.stderr, "\n")) +function! s:on_exit(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + if empty(job.stderr) + call setqflist([]) + call go#util#Cwindow() + redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None + return + else + redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None + call go#tool#ShowErrors(join(job.stderr, "\n")) + let errors = getqflist() + call go#util#Cwindow(len(errors)) + if !empty(errors) + cc 1 "jump to first error if there is any endif -endfunction + endif -function GoCommandJob.cmd(args) - let argv = ['go'] - call extend(argv, a:args) " append the subcommand, such as 'build' - - " each Go instance has these local fields. - " name: defines the subcommand - " stderr: stores the stderr - " stdout: stores the stdout - let fields = { - \ 'name': a:args[0], - \ 'stderr':[], - \ 'stdout':[] - \ } - - " populate the instace with on_stdout, on_stderr ... functions. These are - " needed by jobstart. - let instance = extend(copy(g:GoCommandJob), fields) - - " modify GOPATH if needed - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() - - " execute go build in the files directory - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() - try - execute cd . fnameescape(expand("%:p:h")) - " run, forrest, run! - let instance.id = jobstart(argv, instance) - finally - execute cd . fnameescape(dir) - endtry - - " restore back GOPATH - let $GOPATH = old_gopath - - return instance + " do not keep anything when we are finished + unlet s:jobs[a:job_id] endfunction -function! go#jobcontrol#Run(args) - let job1 = g:GoCommandJob.cmd(a:args) - return job1.id +function! s:abort_all() + if empty(s:jobs) + return + endif + + for id in keys(s:jobs) + if id > 0 + silent! call jobstop(id) + endif + endfor + + let s:jobs = {} endfunction +function! s:abort(name) + if empty(s:jobs) + return + endif + + for job in values(s:jobs) + if job.name == name && job.id > 0 + silent! call jobstop(job.id) + unlet s:jobs['job.id'] + redraws! | echon "vim-go: " | echohl WarningMsg | echon printf("[%s] ABORTED", a:name) | echohl None + + endif + endfor +endfunction -" vim:ts=4:sw=4:et +" vim:ts=2:sw=2:et From 76968945a6b7af16d71ca8699547d4577d1723dd Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 00:35:02 +0200 Subject: [PATCH 07/36] job: add comments --- autoload/go/jobcontrol.vim | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 6e619451a0..6bdbc88dca 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -1,10 +1,18 @@ +" s:jobs is a global reference to all jobs started with Spawn() or with the +" internal function s:spawn let s:jobs = {} +" Spawn is a wrapper around s:spawn. It can be executed by other files and +" scripts if needed. function! go#jobcontrol#Spawn(args) let job = s:spawn(a:args[0], a:args) return job.id endfunction +" spawn spawns a go subcommand with the name and arguments with jobstart. Once +" a job is started a reference will be stored inside s:jobs. spawn changes the +" GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the +" current files folder. function! s:spawn(name, args) let job = { \ 'name': a:name, @@ -15,9 +23,6 @@ function! s:spawn(name, args) \ 'on_exit' : function('s:on_exit'), \ } - " abort previous running jobs - " call self.abort(a:name) - " modify GOPATH if needed let old_gopath = $GOPATH let $GOPATH = go#path#Detect() @@ -45,6 +50,8 @@ function! s:spawn(name, args) return job endfunction +" on_stdout is the stdout handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stdout list. function! s:on_stdout(job_id, data) if !has_key(s:jobs, a:job_id) return @@ -54,6 +61,8 @@ function! s:on_stdout(job_id, data) call extend(job.stdout, a:data) endfunction +" on_stderr is the stderr handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stderr list. function! s:on_stderr(job_id, data) if !has_key(s:jobs, a:job_id) return @@ -63,6 +72,9 @@ function! s:on_stderr(job_id, data) call extend(job.stderr, a:data) endfunction +" on_exit is the exit handler for jobstart(). It handles cleaning up the job +" references and also displaying errors in the quickfix window collected by +" on_stderr handler function! s:on_exit(job_id, data) if !has_key(s:jobs, a:job_id) return @@ -72,22 +84,26 @@ function! s:on_exit(job_id, data) if empty(job.stderr) call setqflist([]) call go#util#Cwindow() + redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None return else - redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None call go#tool#ShowErrors(join(job.stderr, "\n")) let errors = getqflist() call go#util#Cwindow(len(errors)) + if !empty(errors) cc 1 "jump to first error if there is any endif + + redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None endif " do not keep anything when we are finished unlet s:jobs[a:job_id] endfunction +" abort_all aborts all current jobs created with s:spawn() function! s:abort_all() if empty(s:jobs) return @@ -102,6 +118,8 @@ function! s:abort_all() let s:jobs = {} endfunction +" abort aborts the job with the given name, where name is the first argument +" passed to s:spawn() function! s:abort(name) if empty(s:jobs) return From 663048dc8867df1a35d3566bca5d8dbb1f7f4603 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 01:11:28 +0200 Subject: [PATCH 08/36] jobcontrol: refactor error messages --- autoload/go/cmd.vim | 9 ++++----- autoload/go/jobcontrol.vim | 14 ++++++++------ autoload/go/util.vim | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 0e1fa2d08e..3f411edcbf 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -30,17 +30,16 @@ function! go#cmd#Build(bang, ...) let &makeprg = "go build -o " . l:tmpname . ' ' . goargs . ' ' . gofiles endif - echon "vim-go: " | echohl Identifier | echon "building ..."| echohl None if g:go_dispatch_enabled && exists(':Make') == 2 + call go#util#EchoProgress("building dispatched ...") silent! exe 'Make' elseif has('nvim') - " autowrite is not enabled for jobs - call go#cmd#autowrite() - let job = go#jobcontrol#Spawn(['build', '.', 'errors']) - + let job_id = go#jobcontrol#Spawn(['build', '.', 'errors']) + call go#util#EchoProgress(printf("building with id [%d] ...", job_id)) " rest is handled by go#jobcontrol#Run :) return else + call go#util#EchoProgress("building ...") silent! exe 'make!' endif redraw! diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 6bdbc88dca..cbad324ff6 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -5,6 +5,9 @@ let s:jobs = {} " Spawn is a wrapper around s:spawn. It can be executed by other files and " scripts if needed. function! go#jobcontrol#Spawn(args) + " autowrite is not enabled for jobs + call go#cmd#autowrite() + let job = s:spawn(a:args[0], a:args) return job.id endfunction @@ -74,7 +77,8 @@ endfunction " on_exit is the exit handler for jobstart(). It handles cleaning up the job " references and also displaying errors in the quickfix window collected by -" on_stderr handler +" on_stderr handler. If there are no errors and a quickfix window is open, +" it'll be closed. function! s:on_exit(job_id, data) if !has_key(s:jobs, a:job_id) return @@ -84,8 +88,7 @@ function! s:on_exit(job_id, data) if empty(job.stderr) call setqflist([]) call go#util#Cwindow() - - redraws! | echon "vim-go: " | echohl Function | echon printf("[%s] SUCCESS", self.name) | echohl None + call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) return else call go#tool#ShowErrors(join(job.stderr, "\n")) @@ -96,7 +99,7 @@ function! s:on_exit(job_id, data) cc 1 "jump to first error if there is any endif - redraws! | echon "vim-go: " | echohl ErrorMsg | echon printf("[%s] FAILED", self.name)| echohl None + call go#util#EchoError(printf("[%s] FAILED", self.name)) endif " do not keep anything when we are finished @@ -129,8 +132,7 @@ function! s:abort(name) if job.name == name && job.id > 0 silent! call jobstop(job.id) unlet s:jobs['job.id'] - redraws! | echon "vim-go: " | echohl WarningMsg | echon printf("[%s] ABORTED", a:name) | echohl None - + call go#util#EchoWarning(printf("[%s] ABORTED", a:name)) endif endfor endfunction diff --git a/autoload/go/util.vim b/autoload/go/util.vim index 3997b422ae..4f19ace0a1 100644 --- a/autoload/go/util.vim +++ b/autoload/go/util.vim @@ -86,4 +86,24 @@ function! go#util#Cwindow(...) exe 'copen '. height endfunction +" TODO(arslan): I couldn't parameterize the highlight types. Check if we can +" simplify the following functions + +function! go#util#EchoSuccess(msg) + redraws! | echon "vim-go: " | echohl Function | echon a:msg | echohl None +endfunction + +function! go#util#EchoError(msg) + redraws! | echon "vim-go: " | echohl ErrorMsg | echon a:msg | echohl None +endfunction + +function! go#util#EchoWarning(msg) + redraws! | echon "vim-go: " | echohl WarningMsg | echon a:msg | echohl None +endfunction + +function! go#util#EchoProgress(msg) + redraws! | echon "vim-go: " | echohl Identifier | echon a:msg | echohl None +endfunction + + " vim:ts=4:sw=4:et From 733d55874804fc6694b159f6ac7f2cc1a922f35d Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 15:10:14 +0200 Subject: [PATCH 09/36] jobcontrol: add a statusline function to be used with statusline --- autoload/go/jobcontrol.vim | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index cbad324ff6..02efa2fb2e 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -12,6 +12,15 @@ function! go#jobcontrol#Spawn(args) return job.id endfunction +" Statusline returns the current status of the job +function! go#jobcontrol#Statusline(id) abort + if empty(s:jobs) + return '' + endif + "TODO(arslan): change this according to the type of job + return "building ..." +endfunction + " spawn spawns a go subcommand with the name and arguments with jobstart. Once " a job is started a reference will be stored inside s:jobs. spawn changes the " GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the @@ -89,7 +98,6 @@ function! s:on_exit(job_id, data) call setqflist([]) call go#util#Cwindow() call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) - return else call go#tool#ShowErrors(join(job.stderr, "\n")) let errors = getqflist() From 5573e9c5f0b75bdb2b914ad33e63e153a9b6a5ff Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 17:01:17 +0200 Subject: [PATCH 10/36] jobcontrol: show the job pased on the file name --- autoload/go/jobcontrol.vim | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 02efa2fb2e..9027366513 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -13,12 +13,18 @@ function! go#jobcontrol#Spawn(args) endfunction " Statusline returns the current status of the job -function! go#jobcontrol#Statusline(id) abort +function! go#jobcontrol#Statusline() abort if empty(s:jobs) return '' endif - "TODO(arslan): change this according to the type of job - return "building ..." + + for job in values(s:jobs) + if job.filename == fnameescape(expand("%:p")) + return printf("job [%d] running...", job.id) + endif + endfor + + return '' endfunction " spawn spawns a go subcommand with the name and arguments with jobstart. Once @@ -43,11 +49,12 @@ function! s:spawn(name, args) let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let dir = getcwd() try - execute cd . fnameescape(expand("%:p:h")) + let job.dir = fnameescape(expand("%:p:h")) + let job.filename = fnameescape(expand("%:p")) + execute cd . job.dir " append the subcommand, such as 'build' let argv = ['go'] + a:args - " call extend(argv, a:args) " run, forrest, run! let id = jobstart(argv, job) From 99a3a5fec048f0dc8ef65d292e905579b7bcbf73 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 19:44:30 +0200 Subject: [PATCH 11/36] build: cleanup build for multiple cases --- autoload/go/cmd.vim | 39 +++++++++++++++++++------------------- autoload/go/jobcontrol.vim | 12 +++++++----- autoload/go/util.vim | 13 +++++++++++-- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 3f411edcbf..1c47dc0e6a 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -14,30 +14,31 @@ endfunction " default it tries to call simply 'go build', but it first tries to get all " dependent files for the current folder and passes it to go build. function! go#cmd#Build(bang, ...) - let default_makeprg = &makeprg + " expand all wildcards(i.e: '%' to the current file name) + let goargs = map(copy(a:000), "expand(v:val)") - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() + " escape all shell arguments before we pass it to make + let goargs = go#util#Shelllist(goargs, 1) - let l:tmpname = tempname() + " create our command arguments. go build discards any results when it + " compiles multiple packages. So we pass the `errors` package just as an + " placeholder with the current folder (indicated with '.') + let args = ["build"] + goargs + [".", "errors"] - if v:shell_error - let &makeprg = "go build . errors" - else - " :make expands '%' and '#' wildcards, so they must also be escaped - let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) - let gofiles = go#util#Shelljoin(go#tool#Files(), 1) - let &makeprg = "go build -o " . l:tmpname . ' ' . goargs . ' ' . gofiles + " if we have nvim, call it asynchronously and return early ;) + if has('nvim') + call go#jobcontrol#Spawn("building ...", args) + return endif + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + let default_makeprg = &makeprg + let &makeprg = "go " . join(args, ' ') + if g:go_dispatch_enabled && exists(':Make') == 2 call go#util#EchoProgress("building dispatched ...") silent! exe 'Make' - elseif has('nvim') - let job_id = go#jobcontrol#Spawn(['build', '.', 'errors']) - call go#util#EchoProgress(printf("building with id [%d] ...", job_id)) - " rest is handled by go#jobcontrol#Run :) - return else call go#util#EchoProgress("building ...") silent! exe 'make!' @@ -47,16 +48,14 @@ function! go#cmd#Build(bang, ...) let errors = getqflist() call go#util#Cwindow(len(errors)) - if !empty(errors) + if !empty(errors) if !a:bang cc 1 "jump to first error if there is any endif else - redraws! | echon "vim-go: " | echohl Function | echon "[build] SUCCESS"| echohl None + call go#util#EchoSuccess("[build] SUCCESS") endif - - call delete(l:tmpname) let &makeprg = default_makeprg let $GOPATH = old_gopath endfunction diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 9027366513..a72ceb48fa 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -3,12 +3,13 @@ let s:jobs = {} " Spawn is a wrapper around s:spawn. It can be executed by other files and -" scripts if needed. -function! go#jobcontrol#Spawn(args) +" scripts if needed. Desc defines the description for printing the status +" during the job execution (useful for statusline integration). +function! go#jobcontrol#Spawn(desc, args) " autowrite is not enabled for jobs call go#cmd#autowrite() - let job = s:spawn(a:args[0], a:args) + let job = s:spawn(a:desc, a:args[0], a:args) return job.id endfunction @@ -20,7 +21,7 @@ function! go#jobcontrol#Statusline() abort for job in values(s:jobs) if job.filename == fnameescape(expand("%:p")) - return printf("job [%d] running...", job.id) + return job.desc endif endfor @@ -31,9 +32,10 @@ endfunction " a job is started a reference will be stored inside s:jobs. spawn changes the " GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the " current files folder. -function! s:spawn(name, args) +function! s:spawn(desc, name, args) let job = { \ 'name': a:name, + \ 'desc': a:desc, \ 'stderr' : [], \ 'stdout' : [], \ 'on_stdout': function('s:on_stdout'), diff --git a/autoload/go/util.vim b/autoload/go/util.vim index 4f19ace0a1..5ae7422d72 100644 --- a/autoload/go/util.vim +++ b/autoload/go/util.vim @@ -53,9 +53,18 @@ endfunction function! go#util#Shelljoin(arglist, ...) if a:0 return join(map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')'), ' ') - else - return join(map(copy(a:arglist), 'shellescape(v:val)'), ' ') endif + + return join(map(copy(a:arglist), 'shellescape(v:val)'), ' ') +endfunction + +" Shelljoin returns a shell-safe representation of the items in the given +" arglist. The {special} argument of shellescape() may optionally be passed. +function! go#util#Shelllist(arglist, ...) + if a:0 + return map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')') + endif + return map(copy(a:arglist), 'shellescape(v:val)') endfunction From 6c52e5c2c908eeeec4efd3b1b61e80ec92643e71 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 23:20:05 +0200 Subject: [PATCH 12/36] term: add initial code, not usable, WIP --- autoload/go/cmd.vim | 6 ++++++ autoload/go/term.vim | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 autoload/go/term.vim diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 1c47dc0e6a..9bb116b29e 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -65,6 +65,11 @@ endfunction " suitable for long running apps, because vim is blocking by default and " calling long running apps will block the whole UI. function! go#cmd#Run(bang, ...) + if has('nvim') + call go#term#new("go run ". go#util#Shelljoin(go#tool#Files())) + return + endif + let old_gopath = $GOPATH let $GOPATH = go#path#Detect() @@ -80,6 +85,7 @@ function! go#cmd#Run(bang, ...) return endif + " :make expands '%' and '#' wildcards, so they must also be escaped let default_makeprg = &makeprg if a:0 == 0 diff --git a/autoload/go/term.vim b/autoload/go/term.vim new file mode 100644 index 0000000000..4afe3d9a2f --- /dev/null +++ b/autoload/go/term.vim @@ -0,0 +1,23 @@ +function! go#term#new(cmd) + execute 'vertical new' + " setlocal filetype=vimgo + " setlocal bufhidden=delete + " setlocal buftype=nofile + " setlocal noswapfile + " setlocal nobuflisted + " setlocal winfixheight + setlocal cursorline " make it easy to distinguish + setlocal nomodifiable + nnoremap :close + + let id = termopen(a:cmd) +endfunction + +function! go#term#kill() +endfunction + +function! go#term#close() +endfunction + +function! go#term#clear() +endfunction From dcb970c00fe59c261527f1db3514cc78da4a27a9 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 22 Nov 2015 23:24:24 +0200 Subject: [PATCH 13/36] term: add vsplit(), start in insert mode --- autoload/go/cmd.vim | 4 +++- autoload/go/term.vim | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 9bb116b29e..a98cd129f5 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -66,7 +66,9 @@ endfunction " calling long running apps will block the whole UI. function! go#cmd#Run(bang, ...) if has('nvim') - call go#term#new("go run ". go#util#Shelljoin(go#tool#Files())) + call go#term#vsplit() + let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) + call go#term#new(cmd) return endif diff --git a/autoload/go/term.vim b/autoload/go/term.vim index 4afe3d9a2f..f2344f82c4 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -1,4 +1,4 @@ -function! go#term#new(cmd) +function! go#term#vsplit() execute 'vertical new' " setlocal filetype=vimgo " setlocal bufhidden=delete @@ -7,10 +7,13 @@ function! go#term#new(cmd) " setlocal nobuflisted " setlocal winfixheight setlocal cursorline " make it easy to distinguish - setlocal nomodifiable + " setlocal nomodifiable nnoremap :close +endfunction +function! go#term#new(cmd) let id = termopen(a:cmd) + startinsert endfunction function! go#term#kill() From 71234d0fa463de66b7b9e4be126dbfb686707fc5 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Mon, 23 Nov 2015 00:08:41 +0200 Subject: [PATCH 14/36] term: simplify term opening --- autoload/go/cmd.vim | 3 +-- autoload/go/term.vim | 49 +++++++++++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index a98cd129f5..1abaf89ba9 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -66,9 +66,8 @@ endfunction " calling long running apps will block the whole UI. function! go#cmd#Run(bang, ...) if has('nvim') - call go#term#vsplit() let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) - call go#term#new(cmd) + call go#term#new(cmd, "vsplit") return endif diff --git a/autoload/go/term.vim b/autoload/go/term.vim index f2344f82c4..9e8eb15b91 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -1,26 +1,41 @@ -function! go#term#vsplit() - execute 'vertical new' - " setlocal filetype=vimgo - " setlocal bufhidden=delete - " setlocal buftype=nofile - " setlocal noswapfile - " setlocal nobuflisted - " setlocal winfixheight - setlocal cursorline " make it easy to distinguish - " setlocal nomodifiable - nnoremap :close -endfunction +" s:jobs is a global reference to all jobs started with new() +let s:jobs = {} + +" new creates a new terminal with the given command +function! go#term#new(cmd, mode) + execute a:mode.' new' + call s:init_view() + + let job = { + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } -function! go#term#new(cmd) - let id = termopen(a:cmd) + let id = termopen(a:cmd, job) + let job.id = id + let s:jobs[id] = job startinsert + return id +endfunction + +"init_view initializes the view for a new terminal buffer +function! s:init_view() + sil file `="[term]"` + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal winfixheight + setlocal noswapfile + setlocal nobuflisted + setlocal cursorline " make it easy to distinguish endfunction -function! go#term#kill() +function! s:on_stdout(job_id, data) endfunction -function! go#term#close() +function! s:on_stderr(job_id, data) endfunction -function! go#term#clear() +function! s:on_exit(job_id, data) + unlet s:jobs[a:job_id] endfunction From 6850dc0ff1fa762e30dd0da994926e41eb7b4f3c Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Mon, 23 Nov 2015 00:17:30 +0200 Subject: [PATCH 15/36] term: add g:go_term_mode for term related options --- autoload/go/cmd.vim | 2 +- autoload/go/term.vim | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 1abaf89ba9..9edc949722 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -67,7 +67,7 @@ endfunction function! go#cmd#Run(bang, ...) if has('nvim') let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) - call go#term#new(cmd, "vsplit") + call go#term#new(cmd) return endif diff --git a/autoload/go/term.vim b/autoload/go/term.vim index 9e8eb15b91..9547dbf440 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -1,8 +1,18 @@ +if has('nvim') && !exists("g:go_term_mode") + let g:go_term_mode = 'vsplit' +endif + " s:jobs is a global reference to all jobs started with new() let s:jobs = {} -" new creates a new terminal with the given command -function! go#term#new(cmd, mode) +" new creates a new terminal with the given command. Mode is set based on the +" global variable g:go_term_mode, which is by default set to :vsplit +function! go#term#new(cmd) + call go#term#newmode(a:cmd, g:go_term_mode) +endfunction + +" new creates a new terminal with the given command and window mode. +function! go#term#newmode(cmd, mode) execute a:mode.' new' call s:init_view() From 0d37160ecf87b2ba8ffb0939111f91c3fe9f370b Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Mon, 23 Nov 2015 23:13:35 +0200 Subject: [PATCH 16/36] mappings: add new mappings for :GoRun --- autoload/go/cmd.vim | 22 ++++++++++++++++++++-- ftplugin/go/mappings.vim | 7 +++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 9edc949722..d77b6a4369 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -60,14 +60,32 @@ function! go#cmd#Build(bang, ...) let $GOPATH = old_gopath endfunction +" Run runs the current file (and their dependencies if any) in a new terminal. +function! go#cmd#RunTerm(mode) + " guard for other callers + if !has('nvim') + return + endif + + let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) + + " if empty call the default term + if empty(a:mode) + call go#term#new(cmd) + return + endif + + call go#term#newmode(cmd, a:mode) +endfunction + + " Run runs the current file (and their dependencies if any) and outputs it. " This is intented to test small programs and play with them. It's not " suitable for long running apps, because vim is blocking by default and " calling long running apps will block the whole UI. function! go#cmd#Run(bang, ...) if has('nvim') - let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) - call go#term#new(cmd) + call go#cmd#RunTerm('') return endif diff --git a/ftplugin/go/mappings.vim b/ftplugin/go/mappings.vim index eca0d0bbc5..b45e61e6c2 100644 --- a/ftplugin/go/mappings.vim +++ b/ftplugin/go/mappings.vim @@ -11,6 +11,13 @@ endif " Some handy plug mappings nnoremap (go-run) :call go#cmd#Run(!g:go_jump_to_error, '%') + +if has("nvim") + nnoremap (go-run-vertical) :call go#cmd#RunTerm('vsplit') + nnoremap (go-run-split) :call go#cmd#RunTerm('split') + nnoremap (go-run-tab) :call go#cmd#RunTerm('tab') +endif + nnoremap (go-build) :call go#cmd#Build(!g:go_jump_to_error) nnoremap (go-generate) :call go#cmd#Generate(!g:go_jump_to_error) nnoremap (go-install) :call go#cmd#Install(!g:go_jump_to_error) From f9e29b1bc3786d027579aeb6339266750e5297cd Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Mon, 23 Nov 2015 23:42:43 +0200 Subject: [PATCH 17/36] term: add go_term_height and go_term_width settings --- autoload/go/term.vim | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/autoload/go/term.vim b/autoload/go/term.vim index 9547dbf440..f27f5ac1ac 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -14,7 +14,15 @@ endfunction " new creates a new terminal with the given command and window mode. function! go#term#newmode(cmd, mode) execute a:mode.' new' - call s:init_view() + + + sil file `="[term]"` + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal winfixheight + setlocal noswapfile + setlocal nobuflisted + setlocal cursorline " make it easy to distinguish let job = { \ 'on_stdout': function('s:on_stdout'), @@ -24,20 +32,24 @@ function! go#term#newmode(cmd, mode) let id = termopen(a:cmd, job) let job.id = id - let s:jobs[id] = job startinsert - return id -endfunction -"init_view initializes the view for a new terminal buffer -function! s:init_view() - sil file `="[term]"` - setlocal bufhidden=delete - setlocal buftype=nofile - setlocal winfixheight - setlocal noswapfile - setlocal nobuflisted - setlocal cursorline " make it easy to distinguish + " resize new term if needed. + let height = get(g:, 'go_term_height', winheight(0)) + let width = get(g:, 'go_term_width', winwidth(0)) + + " we are careful how to resize. for example it's vertical we don't change + " the height + if a:mode == "vertical" + exe 'vertical resize ' . width + elseif a:mode == "split" + exe 'resize ' . height + endif + + call jobresize(id, width, height) + + let s:jobs[id] = job + return id endfunction function! s:on_stdout(job_id, data) From db3331c9c717207f9d8e43999762af4535a2fd14 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 24 Nov 2015 00:49:57 +0200 Subject: [PATCH 18/36] cmd: add term feature to :GoTest --- autoload/go/cmd.vim | 20 ++++++-------------- autoload/go/term.vim | 15 ++++++++++----- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index d77b6a4369..f1c1aede5a 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -60,25 +60,13 @@ function! go#cmd#Build(bang, ...) let $GOPATH = old_gopath endfunction + " Run runs the current file (and their dependencies if any) in a new terminal. function! go#cmd#RunTerm(mode) - " guard for other callers - if !has('nvim') - return - endif - let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) - - " if empty call the default term - if empty(a:mode) - call go#term#new(cmd) - return - endif - call go#term#newmode(cmd, a:mode) endfunction - " Run runs the current file (and their dependencies if any) and outputs it. " This is intented to test small programs and play with them. It's not " suitable for long running apps, because vim is blocking by default and @@ -104,7 +92,6 @@ function! go#cmd#Run(bang, ...) return endif - " :make expands '%' and '#' wildcards, so they must also be escaped let default_makeprg = &makeprg if a:0 == 0 @@ -201,6 +188,11 @@ function! go#cmd#Test(bang, compile, ...) echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None endif + if has('nvim') + call go#term#new(command) + return + endif + redraw let out = go#tool#ExecuteInDir(command) if v:shell_error diff --git a/autoload/go/term.vim b/autoload/go/term.vim index f27f5ac1ac..23b7a163b7 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -13,8 +13,12 @@ endfunction " new creates a new terminal with the given command and window mode. function! go#term#newmode(cmd, mode) - execute a:mode.' new' + let mode = a:mode + if empty(mode) + let mode = g:go_term_mode + endif + execute mode.' new' sil file `="[term]"` setlocal bufhidden=delete @@ -39,13 +43,14 @@ function! go#term#newmode(cmd, mode) let width = get(g:, 'go_term_width', winwidth(0)) " we are careful how to resize. for example it's vertical we don't change - " the height - if a:mode == "vertical" - exe 'vertical resize ' . width - elseif a:mode == "split" + " the height. The below command resizes the buffer + if a:mode == "split" exe 'resize ' . height + elseif a:mode == "vertical" + exe 'vertical resize ' . width endif + " we also need to resize the pty, so there you go... call jobresize(id, width, height) let s:jobs[id] = job From 261ffdcb4ac0a5124bbb33f46dee0b58d0d03875 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 24 Nov 2015 20:14:32 +0200 Subject: [PATCH 19/36] jobcontrol: fix jumping to errors --- autoload/go/jobcontrol.vim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index a72ceb48fa..7c9e64963b 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -51,9 +51,8 @@ function! s:spawn(desc, name, args) let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let dir = getcwd() try - let job.dir = fnameescape(expand("%:p:h")) let job.filename = fnameescape(expand("%:p")) - execute cd . job.dir + execute cd . fnameescape(expand("%:p:h")) " append the subcommand, such as 'build' let argv = ['go'] + a:args From b3a9f8b9ce5d2248f4c942eb1ee2c3e5ba5e4f1b Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 24 Nov 2015 21:01:13 +0200 Subject: [PATCH 20/36] term: parse errors and display them in qf window --- autoload/go/term.vim | 67 +++++++++++++++++++++++++++++++++++--------- autoload/go/tool.vim | 38 ++++++++++++++++--------- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/autoload/go/term.vim b/autoload/go/term.vim index 23b7a163b7..ef398ecbcb 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -1,5 +1,5 @@ if has('nvim') && !exists("g:go_term_mode") - let g:go_term_mode = 'vsplit' + let g:go_term_mode = 'vsplit' endif " s:jobs is a global reference to all jobs started with new() @@ -18,23 +18,24 @@ function! go#term#newmode(cmd, mode) let mode = g:go_term_mode endif - execute mode.' new' + execute mode.' __go_term__' - sil file `="[term]"` + setlocal filetype=goterm setlocal bufhidden=delete - setlocal buftype=nofile setlocal winfixheight setlocal noswapfile setlocal nobuflisted - setlocal cursorline " make it easy to distinguish + setlocal cursorline " makes it easy to distinguish - let job = { - \ 'on_stdout': function('s:on_stdout'), - \ 'on_stderr': function('s:on_stderr'), - \ 'on_exit' : function('s:on_exit'), - \ } + let job = { + \ 'stderr' : [], + \ 'stdout' : [], + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } - let id = termopen(a:cmd, job) + let id = termopen(a:cmd, job) let job.id = id startinsert @@ -53,16 +54,56 @@ function! go#term#newmode(cmd, mode) " we also need to resize the pty, so there you go... call jobresize(id, width, height) - let s:jobs[id] = job + let s:jobs[id] = job return id endfunction function! s:on_stdout(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + call extend(job.stdout, a:data) endfunction function! s:on_stderr(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + call extend(job.stderr, a:data) endfunction function! s:on_exit(job_id, data) - unlet s:jobs[a:job_id] + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + " usually there is always output so never branch into this clause + if empty(job.stdout) + call setqflist([]) + call go#util#Cwindow() + call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) + else + let errors = go#tool#ParseErrors(job.stdout) + if !empty(errors) + " close terminal we don't need it + close + + call setqflist(errors, 'r') + call go#util#Cwindow(len(errors)) + cc 1 "jump to first error if there is any + + call go#util#EchoError(printf("[%s] FAILED", self.name)) + else + call setqflist([]) + call go#util#Cwindow() + endif + + endif + + unlet s:jobs[a:job_id] endfunction diff --git a/autoload/go/tool.vim b/autoload/go/tool.vim index 64bc026744..4cb763e5fc 100644 --- a/autoload/go/tool.vim +++ b/autoload/go/tool.vim @@ -50,18 +50,39 @@ function! go#tool#ShowErrors(out) let current_dir = getcwd() execute cd . fnameescape(expand("%:p:h")) + let errors = go#tool#ParseErrors(split(a:out, '\n')) + + " return back to old dir once we are finished with populating the errors + execute cd . fnameescape(current_dir) + + if !empty(errors) + call setqflist(errors, 'r') + return + endif + + if empty(errors) + " Couldn't detect error format, output errors + echo a:out + endif +endfunction + +function! go#tool#ParseErrors(out) let errors = [] - for line in split(a:out, '\n') + for line in a:out let fatalerrors = matchlist(line, '^\(fatal error:.*\)$') let tokens = matchlist(line, '^\s*\(.\{-}\):\(\d\+\):\s*\(.*\)') if !empty(fatalerrors) call add(errors, {"text": fatalerrors[1]}) elseif !empty(tokens) + + " strip endlines of form ^M + let out=substitute(tokens[3], '\r$', '', '') + call add(errors, {"filename" : fnamemodify(tokens[1], ':p'), \"lnum": tokens[2], - \"text": tokens[3]}) + \"text": out}) elseif !empty(errors) " Preserve indented lines. " This comes up especially with multi-line test output. @@ -71,18 +92,7 @@ function! go#tool#ShowErrors(out) endif endfor - " return back to old dir once we are finished with populating the errors - execute cd . fnameescape(current_dir) - - if !empty(errors) - call setqflist(errors, 'r') - return - endif - - if empty(errors) - " Couldn't detect error format, output errors - echo a:out - endif + return errors endfunction function! go#tool#ExecuteInDir(cmd) abort From 4a009b4679e428e51158e27d01b8f39acf5eb9dd Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 24 Nov 2015 21:54:06 +0200 Subject: [PATCH 21/36] term: remove prints as the terminal can be anything --- autoload/go/term.vim | 2 -- 1 file changed, 2 deletions(-) diff --git a/autoload/go/term.vim b/autoload/go/term.vim index ef398ecbcb..ef5b638f5c 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -86,7 +86,6 @@ function! s:on_exit(job_id, data) if empty(job.stdout) call setqflist([]) call go#util#Cwindow() - call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) else let errors = go#tool#ParseErrors(job.stdout) if !empty(errors) @@ -97,7 +96,6 @@ function! s:on_exit(job_id, data) call go#util#Cwindow(len(errors)) cc 1 "jump to first error if there is any - call go#util#EchoError(printf("[%s] FAILED", self.name)) else call setqflist([]) call go#util#Cwindow() From 7002b07c9ce0554d0dcb65cb1b19b7dfe9dc92bc Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 24 Nov 2015 22:12:53 +0200 Subject: [PATCH 22/36] term: fix indentation of job var --- autoload/go/term.vim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/autoload/go/term.vim b/autoload/go/term.vim index ef5b638f5c..c4380e7d92 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -28,12 +28,12 @@ function! go#term#newmode(cmd, mode) setlocal cursorline " makes it easy to distinguish let job = { - \ 'stderr' : [], - \ 'stdout' : [], - \ 'on_stdout': function('s:on_stdout'), - \ 'on_stderr': function('s:on_stderr'), - \ 'on_exit' : function('s:on_exit'), - \ } + \ 'stderr' : [], + \ 'stdout' : [], + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } let id = termopen(a:cmd, job) let job.id = id @@ -105,3 +105,5 @@ function! s:on_exit(job_id, data) unlet s:jobs[a:job_id] endfunction + +" vim:ts=2:sw=2:et From 44ab21b7fc06cfed545e2669754268ef233143e4 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Wed, 25 Nov 2015 23:02:18 +0200 Subject: [PATCH 23/36] term: I don't like the cursorline on the bottom --- autoload/go/term.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/autoload/go/term.vim b/autoload/go/term.vim index c4380e7d92..8608f5da89 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -25,7 +25,6 @@ function! go#term#newmode(cmd, mode) setlocal winfixheight setlocal noswapfile setlocal nobuflisted - setlocal cursorline " makes it easy to distinguish let job = { \ 'stderr' : [], From f3b5ffef9e08c7283348c70978af5c651b7543f7 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Thu, 26 Nov 2015 23:46:25 +0200 Subject: [PATCH 24/36] jobcontrol: move to new location list --- autoload/go/jobcontrol.vim | 12 ++++++------ autoload/go/term.vim | 15 +++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 7c9e64963b..0fc57b75eb 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -103,16 +103,16 @@ function! s:on_exit(job_id, data) let job = s:jobs[a:job_id] if empty(job.stderr) - call setqflist([]) - call go#util#Cwindow() + call go#list#Clean() + call go#list#Window() call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) else - call go#tool#ShowErrors(join(job.stderr, "\n")) - let errors = getqflist() - call go#util#Cwindow(len(errors)) + let errors = go#tool#ParseErrors(job.stderr) + call go#list#Populate(errors) + call go#list#Window(len(errors)) if !empty(errors) - cc 1 "jump to first error if there is any + call go#list#JumpToFirst() endif call go#util#EchoError(printf("[%s] FAILED", self.name)) diff --git a/autoload/go/term.vim b/autoload/go/term.vim index 8608f5da89..3c7ac8fa1f 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -83,21 +83,20 @@ function! s:on_exit(job_id, data) " usually there is always output so never branch into this clause if empty(job.stdout) - call setqflist([]) - call go#util#Cwindow() + call go#list#Clean() + call go#list#Window() else let errors = go#tool#ParseErrors(job.stdout) if !empty(errors) " close terminal we don't need it close - call setqflist(errors, 'r') - call go#util#Cwindow(len(errors)) - cc 1 "jump to first error if there is any - + call go#list#Populate(errors) + call go#list#Window(len(errors)) + call go#list#JumpToFirst() else - call setqflist([]) - call go#util#Cwindow() + call go#list#Clean() + call go#list#Window() endif endif From 76195b9d77c7b28adce3054f2a1c58f2524c35ba Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Fri, 27 Nov 2015 17:31:05 +0200 Subject: [PATCH 25/36] jobcontrol: add async showing of location list --- autoload/go/jobcontrol.vim | 102 ++++++++++++++++++++----------------- autoload/go/list.vim | 4 ++ autoload/go/tool.vim | 9 ++-- plugin/go.vim | 3 ++ 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 0fc57b75eb..683623eea5 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -13,6 +13,23 @@ function! go#jobcontrol#Spawn(desc, args) return job.id endfunction +function! go#jobcontrol#DisplayLoclist() + if empty(s:jobs) + return '' + endif + + for job in values(s:jobs) + if job.winnr == winnr() && !empty(job.stderr) + let errors = go#tool#ParseErrors(job.stderr) + call go#list#PopulateWin(job.winnr, errors) + call go#list#Window(len(errors)) + + unlet s:jobs[job.id] + return + endif + endfor +endfunction + " Statusline returns the current status of the job function! go#jobcontrol#Statusline() abort if empty(s:jobs) @@ -36,6 +53,8 @@ function! s:spawn(desc, name, args) let job = { \ 'name': a:name, \ 'desc': a:desc, + \ 'id': '', + \ 'winnr': winnr(), \ 'stderr' : [], \ 'stdout' : [], \ 'on_stdout': function('s:on_stdout'), @@ -49,77 +68,66 @@ function! s:spawn(desc, name, args) " execute go build in the files directory let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let job.filename = fnameescape(expand("%:p")) let dir = getcwd() - try - let job.filename = fnameescape(expand("%:p")) - execute cd . fnameescape(expand("%:p:h")) - " append the subcommand, such as 'build' - let argv = ['go'] + a:args + execute cd . fnameescape(expand("%:p:h")) + + " append the subcommand, such as 'build' + let argv = ['go'] + a:args + + " run, forrest, run! + let id = jobstart(argv, job) + let job.id = id + let s:jobs[id] = job - " run, forrest, run! - let id = jobstart(argv, job) - let job.id = id - let s:jobs[id] = job - finally - execute cd . fnameescape(dir) - endtry + execute cd . fnameescape(dir) " restore back GOPATH let $GOPATH = old_gopath return job endfunction -" on_stdout is the stdout handler for jobstart(). It collects the output of -" stderr and stores them to the jobs internal stdout list. -function! s:on_stdout(job_id, data) - if !has_key(s:jobs, a:job_id) - return - endif - let job = s:jobs[a:job_id] - - call extend(job.stdout, a:data) -endfunction - -" on_stderr is the stderr handler for jobstart(). It collects the output of -" stderr and stores them to the jobs internal stderr list. -function! s:on_stderr(job_id, data) - if !has_key(s:jobs, a:job_id) - return - endif - let job = s:jobs[a:job_id] - - call extend(job.stderr, a:data) -endfunction - " on_exit is the exit handler for jobstart(). It handles cleaning up the job " references and also displaying errors in the quickfix window collected by " on_stderr handler. If there are no errors and a quickfix window is open, " it'll be closed. function! s:on_exit(job_id, data) - if !has_key(s:jobs, a:job_id) - return - endif - let job = s:jobs[a:job_id] - - if empty(job.stderr) + if empty(self.stderr) call go#list#Clean() call go#list#Window() call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) - else - let errors = go#tool#ParseErrors(job.stderr) - call go#list#Populate(errors) + + " do not keep anything when we are finished + unlet s:jobs[a:job_id] + return + endif + + + " if we are still in the same windows show the list + if self.winnr == winnr() + let errors = go#tool#ParseErrors(self.stderr) + call go#list#PopulateWin(self.winnr, errors) call go#list#Window(len(errors)) - if !empty(errors) - call go#list#JumpToFirst() + if has_key(s:jobs, a:job_id) + unlet s:jobs[a:job_id] endif call go#util#EchoError(printf("[%s] FAILED", self.name)) endif +endfunction - " do not keep anything when we are finished - unlet s:jobs[a:job_id] +" on_stdout is the stdout handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stdout list. +function! s:on_stdout(job_id, data) + call extend(self.stdout, a:data) +endfunction + +" on_stderr is the stderr handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stderr list. +function! s:on_stderr(job_id, data) + call extend(self.stderr, a:data) endfunction " abort_all aborts all current jobs created with s:spawn() diff --git a/autoload/go/list.vim b/autoload/go/list.vim index 366c558903..9582d788ce 100644 --- a/autoload/go/list.vim +++ b/autoload/go/list.vim @@ -36,6 +36,10 @@ function! go#list#Populate(items) call setloclist(0, a:items, 'r') endfunction +function! go#list#PopulateWin(winnr, items) + call setloclist(a:winnr, a:items, 'r') +endfunction + " Parse parses the given items based on the specified errorformat nad " populates the location list. function! go#list#ParseFormat(errformat, items) diff --git a/autoload/go/tool.vim b/autoload/go/tool.vim index 1ec3ad29c3..12206ee4b0 100644 --- a/autoload/go/tool.vim +++ b/autoload/go/tool.vim @@ -50,13 +50,14 @@ function! go#tool#ParseErrors(lines) if !empty(fatalerrors) call add(errors, {"text": fatalerrors[1]}) elseif !empty(tokens) - " strip endlines of form ^M let out=substitute(tokens[3], '\r$', '', '') - call add(errors, {"filename" : fnamemodify(tokens[1], ':p'), - \"lnum": tokens[2], - \"text": out}) + call add(errors, { + \ "filename" : fnamemodify(tokens[1], ':p'), + \ "lnum" : tokens[2], + \ "text" : out, + \ }) elseif !empty(errors) " Preserve indented lines. " This comes up especially with multi-line test output. diff --git a/plugin/go.vim b/plugin/go.vim index c3e1493a16..3e5250b012 100644 --- a/plugin/go.vim +++ b/plugin/go.vim @@ -133,6 +133,9 @@ augroup vim-go autocmd BufWritePre *.go call go#fmt#Format(-1) endif + if has('nvim') + autocmd WinEnter *.go call go#jobcontrol#DisplayLoclist() + endif augroup END From e64449510c6cc8d117f50cee7937f1c2da938972 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Sun, 29 Nov 2015 14:50:20 +0200 Subject: [PATCH 26/36] jobcontrol: more changes --- autoload/go/jobcontrol.vim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 683623eea5..ef9a6c937b 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -96,7 +96,6 @@ function! s:on_exit(job_id, data) if empty(self.stderr) call go#list#Clean() call go#list#Window() - call go#util#EchoSuccess(printf("[%s] SUCCESS", self.name)) " do not keep anything when we are finished unlet s:jobs[a:job_id] @@ -107,14 +106,13 @@ function! s:on_exit(job_id, data) " if we are still in the same windows show the list if self.winnr == winnr() let errors = go#tool#ParseErrors(self.stderr) - call go#list#PopulateWin(self.winnr, errors) + call go#list#Populate(errors) call go#list#Window(len(errors)) + " call go#list#JumpToFirst() if has_key(s:jobs, a:job_id) unlet s:jobs[a:job_id] endif - - call go#util#EchoError(printf("[%s] FAILED", self.name)) endif endfunction From 579dd89fea7d48827164145a3d0397f5c03cecf7 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 1 Dec 2015 00:26:34 +0200 Subject: [PATCH 27/36] cmd: add go_term_enabled for :GoTest .. commands This enables us to change the way how GoTest is executed --- autoload/go/cmd.vim | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 57b7e39083..44e93f67fb 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -165,35 +165,46 @@ endfunction " compile the tests instead of running them (useful to catch errors in the " test files). Any other argument is appendend to the final `go test` command function! go#cmd#Test(bang, compile, ...) - let command = "go test " + let args = ["test"] " don't run the test, only compile it. Useful to capture and fix errors or " to create a test binary. if a:compile - let command .= "-c " + call add(args, "-c") endif if a:0 - let command .= go#util#Shelljoin(map(copy(a:000), "expand(v:val)")) + " expand all wildcards(i.e: '%' to the current file name) + let goargs = map(copy(a:000), "expand(v:val)") + + " escape all shell arguments before we pass it to test + call extend(args, go#util#Shelllist(goargs, 1)) else " only add this if no custom flags are passed let timeout = get(g:, 'go_test_timeout', '10s') - let command .= "-timeout=" . timeout . " " + call add(args, printf("-timeout=%s", timeout)) + endif + + if has('nvim') + if get(g:, 'go_term_enabled', 0) + call go#term#new(["go"] + args) + else + call go#jobcontrol#Spawn("testing ...", args) + endif + return endif - call go#cmd#autowrite() if a:compile echon "vim-go: " | echohl Identifier | echon "compiling tests ..." | echohl None else echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None endif - if has('nvim') - call go#term#new(command) - return - endif - + call go#cmd#autowrite() redraw + + let command = "go " . join(args, ' ') + let out = go#tool#ExecuteInDir(command) if v:shell_error let errors = go#tool#ParseErrors(split(out, '\n')) From cacf197b69dc1f0e274e871800cadc0896bcf08b Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 1 Dec 2015 13:11:45 +0200 Subject: [PATCH 28/36] jobcontrol: improve statusline report --- autoload/go/cmd.vim | 4 ++-- autoload/go/jobcontrol.vim | 38 +++++++++++++------------------------- plugin/go.vim | 4 ---- 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index 44e93f67fb..fbb914064b 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -27,7 +27,7 @@ function! go#cmd#Build(bang, ...) " if we have nvim, call it asynchronously and return early ;) if has('nvim') - call go#jobcontrol#Spawn("building ...", args) + call go#jobcontrol#Spawn("build", args) return endif @@ -189,7 +189,7 @@ function! go#cmd#Test(bang, compile, ...) if get(g:, 'go_term_enabled', 0) call go#term#new(["go"] + args) else - call go#jobcontrol#Spawn("testing ...", args) + call go#jobcontrol#Spawn("test", args) endif return endif diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index ef9a6c937b..c41833cd44 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -13,23 +13,6 @@ function! go#jobcontrol#Spawn(desc, args) return job.id endfunction -function! go#jobcontrol#DisplayLoclist() - if empty(s:jobs) - return '' - endif - - for job in values(s:jobs) - if job.winnr == winnr() && !empty(job.stderr) - let errors = go#tool#ParseErrors(job.stderr) - call go#list#PopulateWin(job.winnr, errors) - call go#list#Window(len(errors)) - - unlet s:jobs[job.id] - return - endif - endfor -endfunction - " Statusline returns the current status of the job function! go#jobcontrol#Statusline() abort if empty(s:jobs) @@ -38,7 +21,7 @@ function! go#jobcontrol#Statusline() abort for job in values(s:jobs) if job.filename == fnameescape(expand("%:p")) - return job.desc + return printf("%s [%s]", job.desc, job.state) endif endfor @@ -55,6 +38,7 @@ function! s:spawn(desc, name, args) \ 'desc': a:desc, \ 'id': '', \ 'winnr': winnr(), + \ 'state': "RUNNING", \ 'stderr' : [], \ 'stdout' : [], \ 'on_stdout': function('s:on_stdout'), @@ -69,6 +53,14 @@ function! s:spawn(desc, name, args) " execute go build in the files directory let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let job.filename = fnameescape(expand("%:p")) + + " cleanup previous jobs for this file + for jb in values(s:jobs) + if jb.filename == job.filename + unlet s:jobs[jb.id] + endif + endfor + let dir = getcwd() execute cd . fnameescape(expand("%:p:h")) @@ -97,22 +89,18 @@ function! s:on_exit(job_id, data) call go#list#Clean() call go#list#Window() - " do not keep anything when we are finished - unlet s:jobs[a:job_id] + let self.state = "SUCCESS" return endif + let self.state = "FAILED" " if we are still in the same windows show the list if self.winnr == winnr() let errors = go#tool#ParseErrors(self.stderr) call go#list#Populate(errors) call go#list#Window(len(errors)) - " call go#list#JumpToFirst() - - if has_key(s:jobs, a:job_id) - unlet s:jobs[a:job_id] - endif + call go#list#JumpToFirst() endif endfunction diff --git a/plugin/go.vim b/plugin/go.vim index ddaa38c903..43295ad756 100644 --- a/plugin/go.vim +++ b/plugin/go.vim @@ -133,10 +133,6 @@ augroup vim-go autocmd BufWritePre *.go call go#fmt#Format(-1) endif - if has('nvim') - autocmd WinEnter *.go call go#jobcontrol#DisplayLoclist() - endif - " run gometalinter on save if get(g:, "go_metalinter_autosave", 0) autocmd BufWritePost *.go call go#lint#Gometa(1) From 32924a798af5876c2d94f8f5d1f9328ccd1bd981 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 1 Dec 2015 16:53:20 +0200 Subject: [PATCH 29/36] jobcontrol: don't anything for success case --- autoload/go/jobcontrol.vim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index c41833cd44..3540f25769 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -20,9 +20,15 @@ function! go#jobcontrol#Statusline() abort endif for job in values(s:jobs) - if job.filename == fnameescape(expand("%:p")) - return printf("%s [%s]", job.desc, job.state) + if job.filename != fnameescape(expand("%:p")) + continue endif + + if job.state == "SUCCESS" + return '' + endif + + return printf("%s [%s]", job.desc, job.state) endfor return '' From 8ef677a3625219758a63e1b6826c189a480b4edb Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 1 Dec 2015 22:37:42 +0200 Subject: [PATCH 30/36] fmt: do not close location list when it's not due fmt --- autoload/go/fmt.vim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/autoload/go/fmt.vim b/autoload/go/fmt.vim index ee9525a92a..9ef9a2ebe2 100644 --- a/autoload/go/fmt.vim +++ b/autoload/go/fmt.vim @@ -43,6 +43,8 @@ if !exists("g:go_fmt_experimental") let g:go_fmt_experimental = 0 endif +let s:got_fmt_error = 0 + " we have those problems : " http://stackoverflow.com/questions/12741977/prevent-vim-from-updating-its-undo-tree " http://stackoverflow.com/questions/18532692/golang-formatter-and-vim-how-to-destroy-history-record?rq=1 @@ -117,9 +119,12 @@ function! go#fmt#Format(withGoimport) let &fileformat = old_fileformat let &syntax = &syntax - " clean up previous location list - call go#list#Clean() - call go#list#Window() + " clean up previous location list, but only if it's due fmt + if s:got_fmt_error + let s:got_fmt_error = 0 + call go#list#Clean() + call go#list#Window() + endif elseif g:go_fmt_fail_silently == 0 let splitted = split(out, '\n') "otherwise get the errors and put them to location list @@ -141,6 +146,7 @@ function! go#fmt#Format(withGoimport) echohl Error | echomsg "Gofmt returned error" | echohl None endif + let s:got_fmt_error = 1 call go#list#Window(len(errors)) " We didn't use the temp file, so clean up From 7da1d604c63f67b73437df2232060c7bf384c7fd Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 1 Dec 2015 23:13:23 +0200 Subject: [PATCH 31/36] jobcontrol: status should be per package instead of per file --- autoload/go/jobcontrol.vim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index 3540f25769..f01c5eeb9b 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -19,8 +19,10 @@ function! go#jobcontrol#Statusline() abort return '' endif + let import_path = go#package#ImportPath(expand('%:p:h')) + for job in values(s:jobs) - if job.filename != fnameescape(expand("%:p")) + if job.importpath != import_path continue endif @@ -42,8 +44,8 @@ function! s:spawn(desc, name, args) let job = { \ 'name': a:name, \ 'desc': a:desc, - \ 'id': '', \ 'winnr': winnr(), + \ 'importpath': go#package#ImportPath(expand('%:p:h')), \ 'state': "RUNNING", \ 'stderr' : [], \ 'stdout' : [], @@ -58,11 +60,10 @@ function! s:spawn(desc, name, args) " execute go build in the files directory let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let job.filename = fnameescape(expand("%:p")) " cleanup previous jobs for this file for jb in values(s:jobs) - if jb.filename == job.filename + if jb.importpath == job.importpath unlet s:jobs[jb.id] endif endfor @@ -83,6 +84,7 @@ function! s:spawn(desc, name, args) " restore back GOPATH let $GOPATH = old_gopath + return job endfunction From bc2c0efa251fe0cc7f54bc9f245937b67457d8f7 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 1 Dec 2015 23:47:18 +0200 Subject: [PATCH 32/36] jobcontrol: remove nonused key --- autoload/go/jobcontrol.vim | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index f01c5eeb9b..b5881804a4 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -9,7 +9,7 @@ function! go#jobcontrol#Spawn(desc, args) " autowrite is not enabled for jobs call go#cmd#autowrite() - let job = s:spawn(a:desc, a:args[0], a:args) + let job = s:spawn(a:desc, a:args) return job.id endfunction @@ -40,9 +40,8 @@ endfunction " a job is started a reference will be stored inside s:jobs. spawn changes the " GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the " current files folder. -function! s:spawn(desc, name, args) +function! s:spawn(desc, args) let job = { - \ 'name': a:name, \ 'desc': a:desc, \ 'winnr': winnr(), \ 'importpath': go#package#ImportPath(expand('%:p:h')), @@ -141,16 +140,15 @@ endfunction " abort aborts the job with the given name, where name is the first argument " passed to s:spawn() -function! s:abort(name) +function! s:abort(path) if empty(s:jobs) return endif for job in values(s:jobs) - if job.name == name && job.id > 0 + if job.importpath == path && job.id > 0 silent! call jobstop(job.id) unlet s:jobs['job.id'] - call go#util#EchoWarning(printf("[%s] ABORTED", a:name)) endif endfor endfunction From 9715f287c87e4c7355a94f1a392b5c56c32cc9f2 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Wed, 2 Dec 2015 22:56:06 +0200 Subject: [PATCH 33/36] jobcontrol: fix test and build results --- autoload/go/jobcontrol.vim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index b5881804a4..a574c18aef 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -92,7 +92,8 @@ endfunction " on_stderr handler. If there are no errors and a quickfix window is open, " it'll be closed. function! s:on_exit(job_id, data) - if empty(self.stderr) + let std_combined = self.stderr + self.stdout + if empty(std_combined) call go#list#Clean() call go#list#Window() @@ -100,11 +101,18 @@ function! s:on_exit(job_id, data) return endif + + let errors = go#tool#ParseErrors(std_combined) + if !len(errors) + " no errors could be past, just return + let self.state = "SUCCESS" + return + endif + let self.state = "FAILED" " if we are still in the same windows show the list if self.winnr == winnr() - let errors = go#tool#ParseErrors(self.stderr) call go#list#Populate(errors) call go#list#Window(len(errors)) call go#list#JumpToFirst() From 61c9b49f73603f8a9099cc3655e9c218618bd498 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Wed, 2 Dec 2015 23:42:39 +0200 Subject: [PATCH 34/36] tool: filter valid filenames, fixes #618 --- autoload/go/cmd.vim | 23 +++-------------------- autoload/go/jobcontrol.vim | 3 ++- autoload/go/term.vim | 1 + autoload/go/tool.vim | 31 +++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim index fbb914064b..87312806d9 100644 --- a/autoload/go/cmd.vim +++ b/autoload/go/cmd.vim @@ -106,27 +106,8 @@ function! go#cmd#Run(bang, ...) exe 'lmake!' endif - " Remove any nonvalid filename from the location list to avoid opening an - " empty buffer. See https://github.com/fatih/vim-go/issues/287 for - " details. let items = go#list#Get() - let errors = [] - let is_readable = {} - - for item in items - let filename = bufname(item.bufnr) - if !has_key(is_readable, filename) - let is_readable[filename] = filereadable(filename) - endif - if is_readable[filename] - call add(errors, item) - endif - endfor - - for k in keys(filter(is_readable, '!v:val')) - echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"' - echohl Identifier | echon " from location list (nonvalid filename)" | echohl None - endfor + let errors = go#tool#FilterValids(items) call go#list#Populate(errors) call go#list#Window(len(errors)) @@ -208,6 +189,8 @@ function! go#cmd#Test(bang, compile, ...) let out = go#tool#ExecuteInDir(command) if v:shell_error let errors = go#tool#ParseErrors(split(out, '\n')) + let errors = go#tool#FilterValids(errors) + call go#list#Populate(errors) call go#list#Window(len(errors)) if !empty(errors) && !a:bang diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index a574c18aef..a52c5026fa 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -101,8 +101,9 @@ function! s:on_exit(job_id, data) return endif - let errors = go#tool#ParseErrors(std_combined) + let errors = go#tool#FilterValids(errors) + if !len(errors) " no errors could be past, just return let self.state = "SUCCESS" diff --git a/autoload/go/term.vim b/autoload/go/term.vim index 3c7ac8fa1f..9d0b13a936 100644 --- a/autoload/go/term.vim +++ b/autoload/go/term.vim @@ -87,6 +87,7 @@ function! s:on_exit(job_id, data) call go#list#Window() else let errors = go#tool#ParseErrors(job.stdout) + let errors = go#tool#FilterValids(errors) if !empty(errors) " close terminal we don't need it close diff --git a/autoload/go/tool.vim b/autoload/go/tool.vim index 12206ee4b0..7cfa6b0c60 100644 --- a/autoload/go/tool.vim +++ b/autoload/go/tool.vim @@ -70,6 +70,37 @@ function! go#tool#ParseErrors(lines) return errors endfunction +"FilterValids filters the given items with only items that have a valid +"filename. Any non valid filename is filtered out. +function! go#tool#FilterValids(items) + " Remove any nonvalid filename from the location list to avoid opening an + " empty buffer. See https://github.com/fatih/vim-go/issues/287 for + " details. + let filtered = [] + let is_readable = {} + + for item in a:items + let filename = item.filename + if has_key(item, 'bufnr') + let filename = bufname(item.bufnr) + endif + + if !has_key(is_readable, filename) + let is_readable[filename] = filereadable(filename) + endif + if is_readable[filename] + call add(filtered, item) + endif + endfor + + for k in keys(filter(is_readable, '!v:val')) + echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"' + echohl Identifier | echon " from location list (nonvalid filename)" | echohl None + endfor + + return filtered +endfunction + function! go#tool#ExecuteInDir(cmd) abort let old_gopath = $GOPATH let $GOPATH = go#path#Detect() From 1751ade74542336a52b036303dd59c0240e4f65b Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Thu, 3 Dec 2015 00:05:46 +0200 Subject: [PATCH 35/36] tool: it seems filename is absent too for some cases --- autoload/go/tool.vim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/autoload/go/tool.vim b/autoload/go/tool.vim index 7cfa6b0c60..2ad184c82e 100644 --- a/autoload/go/tool.vim +++ b/autoload/go/tool.vim @@ -80,9 +80,13 @@ function! go#tool#FilterValids(items) let is_readable = {} for item in a:items - let filename = item.filename if has_key(item, 'bufnr') let filename = bufname(item.bufnr) + elseif has_key(item, 'filename') + let filename = item.filename + else + echohl Identifier | echon "no filename available" | echohl None + continue endif if !has_key(is_readable, filename) From f772a4298b5466fd80869f149dc87d9c42fe8ebb Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Tue, 8 Dec 2015 11:30:38 +0200 Subject: [PATCH 36/36] jobcontrol: cleanup location list if it's passing --- autoload/go/jobcontrol.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim index a52c5026fa..f8576a2c45 100644 --- a/autoload/go/jobcontrol.vim +++ b/autoload/go/jobcontrol.vim @@ -30,7 +30,7 @@ function! go#jobcontrol#Statusline() abort return '' endif - return printf("%s [%s]", job.desc, job.state) + return printf("%s ... [%s]", job.desc, job.state) endfor return '' @@ -106,6 +106,9 @@ function! s:on_exit(job_id, data) if !len(errors) " no errors could be past, just return + call go#list#Clean() + call go#list#Window() + let self.state = "SUCCESS" return endif