Describe the bug
Setting the wrap option and one or both of the breakindent and showbreak options causes the virtcol() function's behavior to differ from its documentation. The column number reported by virtcol() varies for a given position depending whether it appears after a soft line-wrap with breakindent or showbreak or both set.
From :h virtcol():
virtcol({expr})
The result is a Number, which is the screen column of the file
position given with {expr}. That is, the last screen position
occupied by the character at that position, when the screen
would be of unlimited width.
When virtcol() is called on a position past a soft line-wrap, its return is higher than if it were called on the same position while nowrap was set. The documentation makes it clear here, and in one other place (see "Expected behavior" below), that this is not correct. The returned value should be the same, just as if the "screen would be of unlimited width."
To Reproduce
vim --clean:set breakindent and :set wrapg$ to go to the final screen column.:echo virtcol('.') to see the positions number.:echo virtcol('.') to see the new positions number. Observe that it is higher than the previous position's number by more than one (it is higher by one + the width of the indent of the real line).:set nowrap and do :echo virtcol('.') again. Observe that virtcol() now reports a different number for the same position.showbreak set to a widthful value. Observe that the virtcol() function's response differs similarly.Expected behavior
The virtcol() function should report the same value consistently for any given position, regardless of the wrap, breakindent, and showindent options. The value reported will vary with window size even if these options do not change. Reporting values as it currently does has no use. Reporting values consistently has significant and important uses.
One of the best examples comes from vim's own help files, again from :h virtcol():
A more advanced example that echoes the maximum length of
all lines:
echo max(map(range(1, line('$')), "virtcol([v:val, '$'])"))
The above vimscript from the help file returns an incorrect value when it is run in a file where wrap and either breakindent or showbreak or both are set. This is obviously unexpected and unintentional behavior.
Screenshots
None unless requested.
Environment
Additional context
This function is an important aid when writing functions and plugins which concern view size and displaying and resizing windows. Its inconsistent behavior impedes one's ability to write vimscript effectively. This author encountered this issue while attempting to write functions to automatically resize popup windows based on the contents of their buffers. The only possible workaround for this issue is to set the wrap option or both the other options off for each buffer in which one wishes to use virtcol(). This is impossible in some cases and infeasible in many. strdisplaywidth() also varies based on option settings and line-wraps.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.![]()
I stumbled upon this behaviour in the past (and I believe it is not a bug). I think, this is expected, as the space occupied by the showbreak and breakindent options is counted as virtual spaces. So virtcol() shows the virtual column number for this position (including text added by breakindent and showbreak and I believe number, foldcolumn and signcolumn are relevant as well.)
I've just tested it right now, and it doesn't seem that number, signcolumn, or foldcolumn are affecting the return of virtcol() at all, wrapped or unwrapped. wincol() is affected by those, if that's the one you were thinking of.
At first I had some difficulty determining whether it was a bug or intended behavior, but I decided to call it a bug for two reasons:
:h virtcol(): " [...] which is the screen column of the file position given [...] screen positionwrap set. Why would this function return with their value included? virtcol() does not include the signcolumn, etc. so it is not intended to determine where exactly on the entire screen or even the entire window where the position appears; it is intended to determine where the position would appear in just the file part of the window if the window's width were unlimited.breakindent and showbreak values? I cannot think of a use case where including their values would be appropriate. If one wanted to see the column of a position relative to the window or screen, they would use wincol() or screencol() or something else. virtcol() in its present state would not be able to meet that use anyway.virtcol() not including the breakindent and showbreak values would be important, and one of those cases is what caused me to file this in the first place.All in all, I could be wrong. The documentaion could always be wrong or out of date or written poorly, and I will not pretend that my not having a use for this behavior implies none exists. However, I currently see neither any intention for virtcol() to include those values, nor any purpose for virtcol() to include those values. Lacking either intention or purpose, this must be considered a bug.
There is an item in the todo list which seems relevant:
Value returned by virtcol() changes depending on how lines wrap. This is
inconsistent with the documentation.
Would you believe I completely forgot we have a todo list?
The item v for 'rulerformat' and 'statusline' has the same problem:
cocopon/shadeline.vim#1
I'm not sure it can always be used as a workaround, but since the patch 8.2.2324, Vim provides the charcol() function which gives positions in terms of character index:
vim9script lefta :25vnew setl wrap lbr setline(1, 'the quick brown fox jumps over the lazy dog') norm! 19l echom virtcol('.') norm! 029l echom virtcol('.')
25
35
vim9script lefta :25vnew setl wrap lbr setline(1, 'the quick brown fox jumps over the lazy dog') norm! 19l echom charcol('.') norm! 029l echom charcol('.')
20
30
Notice that in the first snippet, the 5 virtual cells after the "x" in "fox" are counted when giving the column position of the cursor:
there is nothing there;
these are not spaces in the text;
and yet "virtcol()" count them
v---v
the quick brown fox |
jumps over the lazy dog |
But not in the second snippet.
I'm not sure it can always be used as a workaround
Because the | command also count those virtual cells. Same thing for the \%v atom. More generally, no matter how 'wrap', 'linebreak', 'breakindent', ... are set, |/\%v always seem to agree with virtcol(). IOW, if you intend to use the position in a 123| command or \%123v atom, you'll still want virtcol().
I wish I had seen #10477 and #10098 before they were closed so I could bring some attention back to this issue. As mentioned by @brammool here, 'breakindent' affects more than just virtcol() (including several other things already mentioned in this thread). charcol() counts a tab as a single column, so it couldn't be a replacement even disregarding the issues @lacygoill mentioned (although I appreciate the assistance).
As far as I can tell, there is no way to actually get what the virtcol() documentation claims it returns, specifically:
[...] a Number, which is the screen column of the file
position given with {expr}. That is, the last screen position
occupied by the character at that position, when the screen
would be of unlimited width.
My specific use case for this is to highlight the column number in my statusline if I type past whatever maximum width I think is appropriate for that filetype. When the file is wrapped, the highlight changes prematurely. However, other use cases are affected as well. For instance, this snippet (that I described earlier) from the documentation for virtcol() fails when wrapping is on:
A more advanced example that echoes the maximum length of
all lines:
echo max(map(range(1, line('$')), "virtcol([v:val, '$'])"))
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
This is kind of unfortunate, i am also facing the same issue, when using wrapped lines, the column number/cursor pos in the status line is incorrect, if i set nowrap then it works as expected, is there a way to get a consistent behaviour between both nowrap and wrap for the column number / cursor position.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
With the addition of virtual text this issue becomes more complicated. Two different example use cases:
virtcol() to compute the vertical distance between two characters in the same line (or the scrolling distance with 'smoothscroll'). This also includes computing the height of the line (i.e. the vertical distance between the start and the end). Including 'showbreak', 'breakindent' and 'linebreak' is preferable for this use case as then one can just divide the difference in the two characters' virtcol() by width - textoff from getwininfo(). Including virtual text is also preferable.virtcol() to compute the length of the text in a line or a part of it. Including 'showbreak', 'breakindent' and 'linebreak' is not preferable for this use case. It's unclear whether including virtual text is preferable, as that may depend on the purpose of the virtual text.A solution to allow both use cases is to add a flag to virtcol() specifying whether to exclude 'showbreak', 'breakindent' and 'linebreak'. It can be named text_only, but such a name may suggest that in excludes virtual text as well. Of course, if one always wants to excludes all of them at the same time, there will be no problem.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()