Updating a quickfix/location list asynchronously without interfering with another plugin

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Updating a quickfix/location list asynchronously without interfering with another plugin

Yegappan Lakshmanan
Hi all,

There was a recent thread in reddit/r/vim about two Vim plugins updating
the same quickfix list at the same time thereby interfering with each other:

https://www.reddit.com/r/vim/comments/7c5f1a/two_plugins_writing_to_quickfixloc_list_at_the/

As many other plugin authors may need to know how to use the new Vim quickfix
features to avoid this, I am sending this e-mail.

Vim has many plugins (vim-go, ale, etc.) that use the quickfix/location list
feature. Some of these plugins process the output of an external command and
update the quickfix list asynchronously as the output becomes available.

Updating the quickfix list asynchronously opens up a possibility that two or
more plugins may try to update the same quickfix list with different output.
Also when a plugin is updating the quickfix list in the background, the user
may issue a command that creates or updates a quickfix list. The plugin may
then incorrectly use this new list to add the entries.

The various commands that create or modify a quickfix list (cfile, cgetfile,
caddfile, cbuffer, cgetbuffer, caddbuffer, cexpr, cgetexpr, caddexpr, make,
grep, grepadd, vimgrep and vimgrepadd) operate only on the current quickfix
list. A plugin using these commands to update the quickfix list can interfere
with another plugin.

To avoid these issues, the Vim functions getqflist() and setqflist() can be
used to operate on any list in the quickfix stack. A list in the stack can be
specified using the quickfix identifier. So if a Vim plugin uses these
functions to operate on a specific quickfix list then it can avoid interfering
with the operation of another plugin operating on another quickfix list.

The identifier of a quickfix list can be obtained using:

  let qfid = getqflist({'id' : 0}).id

When adding new entries, the plugin can use setqflist() with this identifier:

  call setqflist([], 'a', {'id' : qfid, 'items' : newitems})

To parse the output of a command and add the quickfix entries, the plugin can
use the following:

  call setqflist([], 'a', {'id' : qfid, 'lines' : cmdoutput})

Note that in the previous command, the current 'errorformat' option setting is
used to parse the command output. This setting might have been changed either
by the user or by some other plugin to some other value. To parse the command
output using a specific 'errorformat' setting, the plugin can use:

  call setqflist([], 'a', {'id' : qfid, 'lines' : cmdoutput, 'efm' : myefm})

If a more than 10 quickfix lists  are added to the quickfix stack, then the
oldest quickfix list is removed. The plugin should check whether the quickfix
list it is using is still valid using the following:

  if has_key(getqflist({'id' : qfid}), 'id')
    " List is still valid
  endif

In summary, a plugin can use the following steps to asynchronously process a
command output and update a quickfix list:

1. Create an empty quickfix list:

     call setqflist([], ' ', {'title' : 'Output from command abc'})

2. Save the newly created quickfix list identifier:

     let qfid = getqflist({'id' : 0}).id

3. Start a command in the background using job_start()

4. In the job callback function, check if the quickfix list is still present:

     if has_key(getqflist({'id' : qfid}), 'id')
         " Still present
         " Update the list
     else
         " List is removed. Stop the background job.
         job_stop(....)
     endif

5. Process the command output and update the quickfix list using one
of the following
   calls:

     call setqflist([], 'a', {'id' : qfid, 'lines' : cmdoutput, 'efm' : myefm})

       or

     call setqflist([], 'a', {'id' : qfid, 'items' : newitems})


- Yegappan

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Updating a quickfix/location list asynchronously without interfering with another plugin

Marcin Szamotulski
On 06:44 Thu 16 Nov     , Yegappan Lakshmanan wrote:

> Hi all,
>
> There was a recent thread in reddit/r/vim about two Vim plugins updating
> the same quickfix list at the same time thereby interfering with each other:
>
> https://www.reddit.com/r/vim/comments/7c5f1a/two_plugins_writing_to_quickfixloc_list_at_the/
>
> As many other plugin authors may need to know how to use the new Vim quickfix
> features to avoid this, I am sending this e-mail.
>
> Vim has many plugins (vim-go, ale, etc.) that use the quickfix/location list
> feature. Some of these plugins process the output of an external command and
> update the quickfix list asynchronously as the output becomes available.
>
> Updating the quickfix list asynchronously opens up a possibility that two or
> more plugins may try to update the same quickfix list with different output.
> Also when a plugin is updating the quickfix list in the background, the user
> may issue a command that creates or updates a quickfix list. The plugin may
> then incorrectly use this new list to add the entries.
>
> The various commands that create or modify a quickfix list (cfile, cgetfile,
> caddfile, cbuffer, cgetbuffer, caddbuffer, cexpr, cgetexpr, caddexpr, make,
> grep, grepadd, vimgrep and vimgrepadd) operate only on the current quickfix
> list. A plugin using these commands to update the quickfix list can interfere
> with another plugin.
>
> To avoid these issues, the Vim functions getqflist() and setqflist() can be
> used to operate on any list in the quickfix stack. A list in the stack can be
> specified using the quickfix identifier. So if a Vim plugin uses these
> functions to operate on a specific quickfix list then it can avoid interfering
> with the operation of another plugin operating on another quickfix list.
>
> The identifier of a quickfix list can be obtained using:
>
>   let qfid = getqflist({'id' : 0}).id
>
> When adding new entries, the plugin can use setqflist() with this identifier:
>
>   call setqflist([], 'a', {'id' : qfid, 'items' : newitems})
>
> To parse the output of a command and add the quickfix entries, the plugin can
> use the following:
>
>   call setqflist([], 'a', {'id' : qfid, 'lines' : cmdoutput})
>
> Note that in the previous command, the current 'errorformat' option setting is
> used to parse the command output. This setting might have been changed either
> by the user or by some other plugin to some other value. To parse the command
> output using a specific 'errorformat' setting, the plugin can use:
>
>   call setqflist([], 'a', {'id' : qfid, 'lines' : cmdoutput, 'efm' : myefm})
>
> If a more than 10 quickfix lists  are added to the quickfix stack, then the
> oldest quickfix list is removed. The plugin should check whether the quickfix
> list it is using is still valid using the following:
>
>   if has_key(getqflist({'id' : qfid}), 'id')
>     " List is still valid
>   endif
>
> In summary, a plugin can use the following steps to asynchronously process a
> command output and update a quickfix list:
>
> 1. Create an empty quickfix list:
>
>      call setqflist([], ' ', {'title' : 'Output from command abc'})
>
> 2. Save the newly created quickfix list identifier:
>
>      let qfid = getqflist({'id' : 0}).id
>
> 3. Start a command in the background using job_start()
>
> 4. In the job callback function, check if the quickfix list is still present:
>
>      if has_key(getqflist({'id' : qfid}), 'id')
>          " Still present
>          " Update the list
>      else
>          " List is removed. Stop the background job.
>          job_stop(....)
>      endif
>
> 5. Process the command output and update the quickfix list using one
> of the following
>    calls:
>
>      call setqflist([], 'a', {'id' : qfid, 'lines' : cmdoutput, 'efm' : myefm})
>
>        or
>
>      call setqflist([], 'a', {'id' : qfid, 'items' : newitems})
>
>
> - Yegappan
Thanks for the detailed explanation!

Best regards,
Marcin

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

signature.asc (201 bytes) Download Attachment