4

I'm writing small liniting plugin and I want to show errors in the quickfix as soon as errorfile changes.

fu! OpenErrors(job_id, data, event)
    let l:winid = win_getid()
    let l:output = split(a:data[0])
    echom l:output[0] . l:output[2]
    let l:view = winsaveview()
    exe 'silent! cfile! ' . escape(l:output[0] . l:output[2], '%#/')
    call winrestview(l:view)
    if len(getqflist()) > 0
        copen
    else
        cclose
    endif
    call win_gotoid(l:winid)
endfu

The problems with this code is that it the cursor jumps to quickfix window and it's annoying when errorfile changes fast.

user1685095
  • 1,141
  • 11
  • 24

4 Answers4

1

In Build-Tools-Wrapper, the callback receives the new line to display (as the job simply executes make (or equivalent) and doesn't play with any intermediary file), the line is processed with :caddexpr. If I'm supposed to be at the bottom I simply execute :cbottom -- moving the cursor (in the qf windows) changes that. These two commands don't change the current window I'm in.

Also when I start the compilation, I open the quickfix window without jumping to it. Actually, I jump to it in order to apply a few hooks (like syntax highlighting, concealment...) and eventually I jump back to the window I was in before starting the background compilation. Along the way I've an old trick able to detect if we need a qf window in other situations.

Luc Hermitte
  • 17,351
  • 1
  • 33
  • 49
  • I'm doing this to display quickfix only if the output of a linter changed. This way If I want to ignore errors I just close quickfix and until output is changed it wouldn't be opened. I guess I could clean quickfix maybe and add to it line by line from a file, but this sounds stupid. – user1685095 Aug 08 '17 at 12:00
  • I can close the qf window while I compile and open it back when the job is finished. It's not incompatible. – Luc Hermitte Aug 08 '17 at 12:38
  • I don't understand what you are talking about unfortunately. I want to open quickfix only if linter results changed. I also don't want my cursor to jump into quickfix window to show it. How can I achieve that? – user1685095 Aug 08 '17 at 13:45
  • When I compile something in the background. make is running and feeds its result back though job_start() callbacks. On each line fed back, I use caddexpr which doesn't trigger to qf-window to open. You can check it by yourself with :cclose + :caddexpr expand('%').":1: Error: test". This means that when I run :Make+:cclose, I can do my :COpen any time I want. – Luc Hermitte Aug 08 '17 at 14:06
  • In your case, you can simply close the qf-window whenever you want, and force it to pop-open when there is something new to feed with :caddexpr. You'll want to do it with :cwindow I guess, or some other trick (see my code) that keep the cursor where it was. – Luc Hermitte Aug 08 '17 at 14:06
  • where in your code is the trick to keep cursor where it was? I hope it's not just winsaveview and jumping back to previous window. – user1685095 Aug 08 '17 at 20:03
  • There are two things: gotoid() and it's reverse operation. Plus the :close+:cwin+ winnr()-test to know whether an error as been detected -- may be we could check filter(getqflist(), 'v:val.type=="E"') now (parts of the code are decade old)? I haven't check whether it would be faster/simpler/equivalent or not. – Luc Hermitte Aug 08 '17 at 20:12
1

Try this quickfix preview plugin: https://github.com/ronakg/quickr-preview.vim

enter image description here

tracyone
  • 345
  • 1
  • 6
1

Here is what I ended up using to prevent the cursor from moving after executing copen:

function! OpenErrors(job_id, data, event)
    " Store the original window number
    let l:winnr = winnr()

    " Open a window to show the current list of errors

    " If focus changed, jump to the last window
    if l:winnr !=# winnr()
        wincmd p
    endif
endfunction

Also, the vim-qf plugin makes it easier to work with the location/quickfix list.

LEI
  • 1,626
  • 9
  • 18
0

cwindow does your open/close behaviour but jumps to the quickfix if opening.

wincmd p jumps to the previous window (and should maintain the cursor position).

A simple method:

cclose | copen | wincmd p | cwindow

A modification of your code that probably works (untested):

fu! OpenErrors(job_id, data, event)
    let l:was_in_qf = &buftype == 'quickfix'
    let l:output = split(a:data[0])
    echom l:output[0] . l:output[2]
    exe 'silent! cgetfile! ' . escape(l:output[0] . l:output[2], '%#/')
    cwindow
    if !l:was_in_qf && &ft == 'qf'
        wincmd p
    endif
endfu
idbrii
  • 641
  • 4
  • 14
  • This introduces flickering, so the real answer is “You can’t” – user1685095 Jul 30 '21 at 05:49
  • Do you still see flickering with set lazyredraw? For something like this, I'd turn that on at the top of a function and restore its value at the end. – idbrii Jul 30 '21 at 18:37
  • That was a long time ago, but I think I’ve tried all the options and the flickering was still present, but it was bearable. What I was doing at the time is trying to updating quick fix windows in a background automatically after running linter. – user1685095 Jul 31 '21 at 08:39
  • ale and asyncrun both update the quickfix in the background, but don't open it automatically. Since you don't know what the user could be doing when the timer fires (in the middle of typing a multi-key mapping or a : command), maybe it'd be better to separate updating from opening and open the window from a CursorHold or SafeState autocmd. – idbrii Aug 02 '21 at 01:11