Evil breaks visual-line-mode
In Evil, line operations always affect buffer lines instead of visual lines. This behavior is consistent with Vim. While I think that consistency is important, the reason for people to use Evil instead of Vim is that they would like to take advantage of features provided by Emacs. One such feature is visual-line-mode. The purpose of visual-line-mode is:
- automatic, visual word wrapping without changing the buffer content,
- making visual lines behave as if they were buffer lines.
In other words, the fact that visual lines are not buffer lines is supposed to be completely transparent to the user. Evil breaks visual-line-mode because it operates on buffer lines no matter whether visual-line-mode is switched on or not.
Visual-line-mode may not be important for people who mainly write code. In code, you want to manage line breaks yourself. However, for people who write a lot of text, visual-line-mode can be essential. One reason why I left Vim was that I was sick and tired of doing
gqap hundreds of times a day. Commands that I would like to see working with visual lines instead of with buffer lines are of course
yy, but also
What are possible solutions? I guess, one possibility would be to change all kinds of Evil commands to deal with visual lines instead buffer lines when visual-line-mode is switched on. This is what I currently implemented for some important commands. However, this leads to a lot of changes and messy code. Another possibility may be to let Evil always operate on visual lines irrespective of whether visual-line-mode is switched on or not. The obvious draw-back is that Evil's behavior would then be inconsistent with Vim's when visual-line-mode is turned off.
A more elegant and more correct solution might therefore be the following: The idea is to enclose line-operations in a function that does the following:
- Word wrap the current buffer line using
- Execute the line operation on the newly created buffer line (
- "Unwrap" the region corresponding to the original line by removing the line breaks introduced by
For this to work properly,
fill-column has to be set to the current width of the window (in characters). Filling should then not change the appearance of the text but simply replace visual line breaks by real line breaks (i.e. "\n" characters). So the user shouldn't notice what is going on. Here's some code to illustrate what I mean:
(defmacro fill-unfill (&rest body) `(lambda (&rest args) (interactive "p") (let ((fill-column-orig fill-column) (fill-column (- (window-width) 2)) (start (save-excursion (beginning-of-line) (point))) (end (save-excursion (end-of-line) (point))) (end-of-buffer (point-max))) (if visual-line-mode (save-excursion (fill-region start end nil 1))) (apply ,@body args) (if visual-line-mode (let ((fill-column (point-max)) ; Account for additional newline chars in the region: (new-end (+ end (- (point-max) end-of-buffer)))) (save-excursion (fill-region start end nil 1))))))) (define-key evil-normal-state-map "I" (fill-unfill 'evil-insert-line)) (define-key evil-normal-state-map "A" (fill-unfill 'evil-append-line))
This code may break some things like macros (
q and friends) but perhaps this can be dealt with. In case that this solution can be generalized to handle other operations like
dd as well, this may be a nice approach because it doesn't require changes in too many places. The whole issue is rather beautifully factored out from the actual line operations. The line operations themselves would be agnostic to the whole issue and would not have to deal with visual lines at all.
Frank considered a similar solution for VimMode. See https://bitbucket.org/lyro/vim-mode/issue/33/visual-line-mode
I think that having Evil play nicely with visual-line-mode would be a major improvement.