vim-qf-diagnostics
vim-qf-diagnostics copied to clipboard
Duplicate text properties when opening a buffer in multiple windows
Hi,
A text properties is associated with a buffer while a location list is associated with a window.
I have a plugin that does linting when saving a buffer and shows the error on a location list.
If I open the buffer in two different tabs, each window will have duplicate virtual texts because the buffer already has text properties set from the location list in the first window.
For my use case, since the same buffer will always have the same location list content, I can fix this by commenting the top part of Group_id and just return 0 for all case.
This is as expected. When you open a buffer in a new window, all the current window's window-local options as well as the window's location list are cloned. Hence, you end up with the same location list. If you highlight the errors from both location lists (with :LDiagnosticsPlace), you end up with duplicate text-properties. The plugin doesn't know that they're duplicates. However, from now on when you change one of the location lists (for example after with running :lmake), the other location list won't be updated anymore. So you end up with two different location lists.
Why do you show the location list errors of each window in the first place? Looks like you're triggering :lmake every time you save a buffer (using an autocommand?).
For my use case, since the same buffer will always have the same location list content, I can fix this by commenting the top part of Group_id and just return 0 for all case.
Well, what you're doing with this is showing the errors of the quickfix ist. The internal group 0 is used for the quickfix list.
This is as expected.
I expected that messages from location list are only shown in the window it is associated with.
all the current window's window-local options as well as the window's location list are cloned
I do a tab split. Location list is not cloned.
Why do you show the location list errors of each window in the first place? Looks like you're triggering :lmake every time you save a buffer (using an autocommand?).
Exactly. I have a custom plugin that calls a python linter everytime a buffer is saved and shows the errors in a location list.
Well, what you're doing with this is showing the errors of the quickfix ist. The internal group 0 is used for the quickfix list.
I don't use quickfix list so it is not a problem for me.
I think duplicate text properties can be prevented by using a dict of bufnr .. lnum .. col .. messages.
The initial problem still stands: text properties are buffer specific while location list is window specific. It is possible to show the same buffer and the same location list content in multiple windows, thus causing duplicate text properties.
When a buffer has multiple location lists, those location lists should be merged together with duplicates removed before being added as text properties. Using a dict to prevent duplicates is basically the same thing.
I expected that messages from location list are only shown in the window it is associated with.
That's not how text-properties work. You already said it yourself, text-properties are associated with a buffer and displayed in every window the buffer is displayed in. There's no way to avoid this unless it's implemented in Vim.
I do a tab split. Location list is not cloned.
Yes it is. When you run :tab split, a new window is opened in a new tab page. Whenever a new window is created, the current window's options are used to initialize the new window, and so is the location-list. You can check this very quickly by running :lgrep and then :tab split. You will see that the new window has the same location list.
I think duplicate text properties can be prevented by using a dict of bufnr .. lnum .. col .. messages.
I don't like this tbh. Converting the items in the location-list to a string and putting them in a dictionary just to be able to throw away duplicates, that looks like a dirty hack to quickly solve your issue. What if one location-list contains errors of linter X and another location-list contains errors of linter Y, and it just happens that both display the same error at the same lnum/col? Very unlikely, but could happen. Would you want to remove the duplicates in that case as well?
And there's another problem: at the moment the plugin automatically removes signs and text-properties associated with a location-list once the location-list's parent window is closed. This is done because IMO when the location-list goes out of scope, the diagnostics should go away as well. (Note that when a window is closed, its location-list is automatically deleted by Vim). Now, once we start throwing away duplicates, for each sign/text-property we need to start keeping track of the the location-lists it belongs to (sort of like a reference counter). But instead of a counter we need to store a list of window IDs of the parent windows. Once a window is closed we need to remove the window ID from that list and only when that list is empty we can remove the signs and text-properties. Do you see how complicated this gets? This is doable but I don't know if that's the right way to solve your problem.
The issue is the following: you're trying to use the location-list which is a window-local list for a buffer-local task. I can understand what you're trying to do and I would really like to make it work but I don't think it's easily doable. You're also running into another issue: if you have a buffer displayed in window 1 and you run :lmake, then you open the buffer in a new window 2. Location-list 1 is automatically copied to window 2 by Vim. You make some changes to the buffer while in window 2 and then you run :lmake in that window. Location-list 2 will be updated but location-list 1 is still the old one because location lists are not synced between windows.
I need to think about this a little bit longer and whether it can be solved. Until then you can replace :lmake with :make and the errors will go to the global quickfix list. There's only one quickfix list per Vim session. The downside is that you cannot show linting errors for different buffers simultaneously.
When a buffer has multiple location lists, those location lists should be merged together with duplicates removed before being added as text properties. Using a dict to prevent duplicates is basically the same thing.
You're assuming that the location-lists are always identical. What if they're not? Could be from different linters, or the same linter but different buffers.
What you want is an error list that is associated with a buffer. That's not easily achievable with default :lmake. It will require some scripting.
What this plugin does is highlight all items in the quickfix list, or current location list. Whatever it is. Once a window is closed, Vim automatically deletes its location list. Therefore the plugin also automatically removes the diagnostics associated with that location list.
You're abusing the location list for something it wasn't meant to be used for. TBH personally, I still don't have a use case for the location list. The only use case I can think of is when you need to run two different commands in two different windows, for example, for comparison. Like, on the left side :lgrep foo, and on the right side :lgrep bar, or two different linters on the same buffer.
That's not how text-properties work. You already said it yourself, text-properties are associated with a buffer and displayed in every window the buffer is displayed in. There's no way to avoid this unless it's implemented in Vim.
You can, by preventing duplicates. There is no point showing the same virtual text on the same line number in the same column more that once. Which is why before calling prop_add, it should be checked that the property hasn't already exist.
Yes it is. When you run :tab split, a new window is opened in a new tab page.
It doesn't happen in my vim. I can confirm that the new window still references the previous location list by the output of getloclist() but the location list window itself is not shown in the new window. It does not matter in my case because the location list will be replaced when I save the file.
What if one location-list contains errors of linter X and another location-list contains errors of linter Y, and it just happens that both display the same error at the same lnum/col?
Then the text message should be different. If the message is the same and it is shown in the same line number and the same column, showing the same message twice is pointless. Even looking at the location list the user wouldn't be able to differentiate which linter by looking at the message.
And there's another problem: at the moment the plugin automatically removes signs and text-properties associated with a location-list once the location-list's parent window is closed.
Like you wrote, a data structure can be created to track what location list is associated with a text property. Two text property is equal if it is the same message in the same line and same column (and maybe same type if there are multiple types). If two text property is equal it should be only shown once, but a counter or a list can be added. The text property should only be removed if the counter is zero or the list is empty.
Like I wrote before, I have already fixed my problem by making some change in the plugin locally. It is up to you whether you want to change the plugin or not, but the main problem is this: it is possible to show the same virtual text in the same buffer and the same line/column more that once due to multiple location list, but showing the same virtual text more that once is useless if not annoying and thus should be prevented if possible.
It doesn't happen in my vim. I can confirm that the new window still references the previous location list by the output of getloclist() but the location list window itself is not shown in the new window.
The new window doesn't reference the previous location list, it has a local copy of it (which initially is identical to the location-list of the previous window). When you open a new split, the new window won't open its location-list window by default. Just open it with :lopen and you will see that they're identical. Once you have both location-list windows open, go to the previous window and run :lgrep (or something else) and see how the old location-list changes but the one in the new window remains the same. And from now on you have two diverging location-lists.
It does not matter in my case because the location list will be replaced when I save the file.
But only in the current window. If your buffer is displayed in several windows and you show the location-list errors from each location-list, you won't just have duplicated text-properties but possibly also outdated errors displayed in the buffer. Let's say you run :lmake in window 1 and it gives you 5 errors, you switch to window 2 where the same buffer is displayed and you fix those 5 errors and then run :lmake again in windows 2. The location-list of window 2 won't have any items but those 5 errors from the location-list of window 1 are still displayed. The won't go automatically away.
Like I wrote before, I have already fixed my problem by making some change in the plugin locally.
If you're referring to returning 0 from Group_id() you didn't really fix it, maybe your workflow. But you actually broke the functionality of the plugin.