How to set Emacs to wrap long line (using Visual Line Mode) at -, _, (, ) too, instead of just at spaces.

A sample line:

This is a long line with long words like long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-word, long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_long_word, (very)long(very)long(very)long(very)long(very)long(very)long(very)long(very)long(very)long(very)long(very)long-word.


Looking at the source code, it doesn't seem to be possible, as the definition of white-space relevant for visual-line-mode appears to be hard-coded in the C part of the source code.

I'm not sure about my conclusions, but here is the sequence of my source archaeology (I'll be linking to github for ease of reference, though when actually carrying out the investigation I had used C-h f, C-h v and finally a recursive grep of the C code.)

visual-line-mode enables word-wrap by setting the variable word-wrap to t. (You can test this by just setting the variable to true, without enabling visual-line-mode itself.)

The variable word-wrap is defined in buffer.c:

DEFVAR_PER_BUFFER ("word-wrap", &BVAR (current_buffer, word_wrap), Qnil,

Guessing (and my terminology will be off, because I'm a horrible person who hasn't yet read all of the texinfo manual), this means that the elisp-accessible variable "word-wrap", sets the C variable word_wrap.

Searching for where word_wrap is used, we find:

it->line_wrap = NILP (BVAR (current_buffer, word_wrap))

i.e. if word_wrap is set in the current buffer, the it->line_wrap is set to WORD_WRAP (A ? B : C is a ternary operator...).

Looking further on, we get, among other things, this condition:

  if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA)
          may_wrap = true;

I'm eliding quite a large amount of code, which I don't really, fully understand, but it seems that unless IT_DISPLAYING_WHITESPACE (it) then either may_wrap will be explicitly set to false, or we'll escape the block and do something interesting elsewhere.

IT_DISPLAYING_WHITESPACE in turn, is defined here:

#define IT_DISPLAYING_WHITESPACE(it)                    \
  ((it->what == IT_CHARACTER && (it->c == ' ' || it->c == '\t'))    \
   || ((STRINGP (it->string)                        \
    && (SREF (it->string, IT_STRING_BYTEPOS (*it)) == ' '       \
        || SREF (it->string, IT_STRING_BYTEPOS (*it)) == '\t')) \
       || (it->s                            \
       && (it->s[IT_BYTEPOS (*it)] == ' '               \
           || it->s[IT_BYTEPOS (*it)] == '\t'))         \
       || (IT_BYTEPOS (*it) < ZV_BYTE                   \
       && (*BYTE_POS_ADDR (IT_BYTEPOS (*it)) == ' '         \
|| *BYTE_POS_ADDR (IT_BYTEPOS (*it)) == '\t')))) \

suggesting that the only two recognised white-space characters are the space and the tab.

Hence, it appears that tabs and spaces are hard-coded as the only characters at which wrapping is allowed to occur, but as already written above, I might (hopefully) be wrong.

  • It seems that I was unfortunately correct: see this comment by Eli Zaretskii in a very interesting bug thread dedicated to trying to fix this. There seems to be an attempt at a fix here, but apparently it doesn't work and there hasn't been any recent progress. (I'm not sure if I'm up to dealing with something like this properly...) – aplaice Jun 16 '17 at 14:34

This is what you need to do (e.g., in the mode where you will be filling text):

(modify-category-entry ?- ?|)

That gives the hyphen character (-) the category of |, which means line breakable. Do the same for _, (, and ), if you want lines to be broken at each of those characters also. (But I suspect that you do not really want lines to be broken after ( -- just after ).)

You can use M-x describe-categories to see the available categories.

(I should say that I did not bother to try this with visual-line-mode (I don't use or like visual-line-mode), but only with ordinary text filling. If (modify-category-entry ?- ?|) is not sufficient for visual-line-mode then you might need to tinker a bit more with it or perhaps look at the visual-line-mode code to see how it does filling (if it is different from the usual filling).)

How I found this out:

  1. I looked at the code that does filling. I started with just command fill-paragraph (bound to M-q), and I eventually got to function fill-move-to-break-point.

  2. Function fill-move-to-break-point does this:

    (re-search-backward "[ \t]\\|\\c|.\\|.\\c|" linebeg 0)`
  1. Not being familiar with \c, I looked in the Elisp manual, under regexps, node Regexp Backslash, and saw that "\cC matches any character whose category is C.". I followed the link there to node Categories.

  2. I knew nothing about categories at this point, although I had some familiarity with character tables. So I read about categories.

  3. I found, from M-x describe-categories and from the fill-move-to-break-point code, that | is the category for a character to be line breakable. So I tried modifying the category entry for the character - (hyphen) to be line-breakable (|). It worked.

  • It doesn't work with either visual-line-mode on (still wrap at spaces) or off (still wrap at window edge). – aggu Dec 23 '15 at 9:57
  • @aggu Drew said in the mode where you will be filling text so you have to enable auto-fill-mode and/or use fill-paragraph (M-q). – JeanPierre Dec 23 '15 at 11:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.