Vim rendering bug
Posted by Bluebird | Filed under , vi clones
While working on Vy’s rendering, I checked different rendering examples with Vim. And I found a bug. Incredible. It is the first time of my life that I find a bug in vim, which is deemed super super stable.
The bug happens on gvim on windows, I haven’t tested on other versions of vim. To reproduce :
- Open an empty file
- Fill the file with more lines than vim can display (force it to scroll a bit)
- Scroll up, there should be some non visible lines below your current vim window
- Go to the last line of your screen
- Fill the line with the exact number of character that fit on screen
- Return to normal mode, stay on that last line
- Press A : your cursor is no longer visible.
What happens is that the cursor is no the next line, but vim forgot to scroll the view so that you see your cursor. Interestingly, when you filled the line, the cursor was visible all the time and vim scrolled the window correctly. This means that what happens when you press A is different from what happens when you are in the middle of the insert mode.
Because I am working on rendering, I have a good idea of why there is bug there. Vim editing mode has the particularity that the cursor is standing on a non-existing character, which is past the last character of the line. When editing in the middle of a window line, this is not a problem. When you are at the end of a line, this becomes interesting. At one point, the last character of the line is at the last character of the window line; so the cursor is displayed on the first character of the next line. That line does not exist in the buffer, and that character does not exist in the buffer.
That creates a big rendering question : should we create an empty rendering line on screen, to display the cursor on a line that does not exist ? That means that the rendering code must do more than take existing lines and split them into window lines: it must take existing lines, check for the cursor location, if the cursor is past the last character of the window line, extend the length of the line to render to create a new empty rendering line.
This also means that the rendering must be updated each time the cursor moves, instead of just when a character is added or modified.
This is a bit cumbersome and more importantly, it would trigger a rendering check each time the cursor moves. That’s a bit too often and is likely to cause flickers. Instead, the simple solution is not to update the rendering each time the cursor moves and handle the very specific case of when the cursor is sitting on a line that is not part of the buffer, just put the cursor on the next displayed line, which is actually the next buffer line. The simplification is reasonable. It also avoids a rendering in the case we fill up a window line with character. With correct rendering, there would be an scroll-down to display the cursor on the next empty window line, and then a scroll-up if the user presses ESC. With this simplified approach, no rendering is necessary in this case. The only drawback is that in some situations, the cursor disappears from screen during the typing of one character.