Stop abbrev from expanding on newline
I have always I wanted to fix a particular behavior with abbrev expansion: by
default abbrev
will skip newlines when looking for a word to expand. Let me
illustrate. I had an abbrev, "args", that expanded into "arguments". If I
typed in the docstring below where the carrot is, the symbol "args" in the
previous line would expand into "arguments".
;; The cursor would be on the ^ symbol would cause args to expand to
;; `arguments'.
(defun foo (a b &rest args)
"^")
Curiously, this would even get around oo-use-text-abbrev-p which I use as the
:enable-function property because point is still in a string even though the
symbol args
is outside of it. In practice this means that there could be 10
newlines between your cursor and the previous abbrev and it would still be
expanded. This is not what I want. Such expansions cause me unexpected
and more often than not undesired abbrev expansions.
;; The cursor would be on the ^ symbol would cause args to expand to
;; `arguments'.
(defun foo (a b &rest args)
"^")
To find the root of the problem I looked inside expand-abbrev and then the
abbrev-expand-function, which was set to abbrev–default-expand, until finally I
found the culprit, abbrev–before-point. The body of abbrev--before-point
is
long but the only part that concerns this problem is the following chunk.
(let ((re (abbrev-table-get table :regexp)))
(if (null re)
;; We used to default `re' to "\\<\\(\\w+\\)\\W*"
;; but when words-include-escapes is set, that
;; is not right and fixing it is boring.
(let ((lim (point)))
(backward-word 1)
(setq start (point))
(forward-word 1)
(setq end (min (point) lim)))
(when (looking-back re (line-beginning-position))
(setq start (match-beginning 1))
(setq end (match-end 1)))))
If the abbrev table has no :regexp
property (the default) it uses backward-word
to find the previous expansion. Therein lies the problem. The function
backward-word
finds the last word regardless of how many lines it is behind
point. It has no boundaries. However, as is apparent from the call to
looking-back in (looking-back re (line-beginning-position))
, using any regexp
will automatically bound the search to the beginning of the current line1
In any case, this suggests that what I need to do is provide a regexp and it will automatically be bounded. Conveniently the comment in the code provides a good regexp to go by2.
And voilĂ .
(abbrev-table-put global-abbrev-table :regexp "\\<\\(\\sw+\\)\\Sw*")
Footnotes:
I do
not know if this was intentional. If you bound looking-back
why not bound
backward-word
?
Sure, I know it mentions words-include-escapes but to be
honest I do not really care about that. I have words-include-escapes
set to nil
have do not plan on enabling it any time soon.