4

I already use abbrev-mode to correct common typos, e.g. I have an abbrev setup for expanding functino to function, etc...

I'm currently writing a large document, and I keep stumbling over the decision whether to write "lifecycle" or "life-cycle", I decided to go with the former and add an abbreviation for "life-cycle" which would autocorrect it to "lifecycle". However, abbrev-mode is not able to expand over the - there and from what I understand there's no way to make abbrev-mode understand that.

Are there any alternatives out there that will make things like this work?

3
  • 1
    Why don't you use (query-replace-regexp "\\<life-cycle\\>" "lifecycle")? If you want to stick to abbrev-mode you could modify the syntax entry of - to word-constituent, i.e., "w", with the help of modify-syntax-entry.
    – Tobias
    Commented Nov 15, 2017 at 14:16
  • 1
    Well I don't want to replace all of the words after the fact, I want it to be immediate in order to prevent the problem altogether. I used to be able to do this in vim with abbreviations pretty easily. I'd rather not modify the syntax-entry for - because it would interfere with movement. Is there no alternative?
    – DeX3
    Commented Nov 15, 2017 at 14:34
  • You do not need to modify the syntax table permanently. Just modify it temporarily while abbreviation takes place. I've demonstrated this in my answer.
    – Tobias
    Commented Nov 15, 2017 at 16:23

1 Answer 1

4

You can continue to use abbrev-mode. The following elisp snippet allows you to specify additional characters as word-consistent when abbreviation takes place.

After running the snippet customize the option abbrev-additional-chars with an alist that maps major-mode symbols to the lists of characters that should be interpreted as word-consistent during abbreviation.

The special mode symbol t corresponds to additional characters active in all modes.

The default value of abbrev-additional-chars is '((t ?-)) which covers your use-case.

You can install the snippet in your init file if you like.

(defcustom abbrev-additional-chars
  '((t ?-))
  "Alist that maps major mode symbols to lists of characters that may appear in abbreviations.
The chars of the special major mode symbol `t' are active in all modes."
  :group 'abbrev
  :type '(repeat :tag "List of modes"
                 (cons :tag "Map major mode symbols to lists of additional chars in abbrevs"
                       (symbol :tag "Mode symbol (`t' stands for all modes)")
                       (repeat :tag "List of additional word-consistent characters" character))))

(defvar-local T-abbrev-syntax-table nil
  "List of additional characters in abbreviations.")

(defun T-abbrev-mode-hook-fun ()
  "Populate T-abbrev-syntax-table with the local syntax table modfied by
the characters in `abbrev-additional-chars'."
  (when abbrev-mode
    (let ((char-list (append (cdr (assoc major-mode abbrev-additional-chars))
                             (cdr (assoc 't abbrev-additional-chars)))))
      (setq T-abbrev-syntax-table (make-syntax-table (syntax-table)))
      (mapcar (lambda (char)
                (modify-syntax-entry char "w" T-abbrev-syntax-table))
              char-list))))

;; Wrapping functions of the `abbrev` package with the local syntax table.
;; I'm not sure I captured all fun's that need to run with the local syntax-table.
;; Adding further functions is easy.
;; Just add them to the list at the end of the next form.
(mapcar
 (lambda (fun)
   (let ((newfun (intern (concat "T-ad-" (symbol-name fun)))))
     (eval
      `(progn
         (defun ,newfun (oldfun &rest args)
           ,(concat "This function evaluates `" (symbol-name fun) "' with `T-abbrev-syntax-table' as active syntax table.
It is used for the advicing `" (symbol-name fun) "'.")
           (with-syntax-table T-abbrev-syntax-table
             (apply oldfun args)
             ))
         (advice-add (quote ,fun) :around (quote ,newfun))))))
 '(define-mode-abbrev abbrev--before-point))

(add-hook 'abbrev-mode-hook #'T-abbrev-mode-hook-fun)
7
  • Hm, this seems to be just what I was looking for, however it seems to break a couple of things: When I try to type non-word characters in the minibuffer, I now get an error: (wrong-type-argument syntax-table-p nil)
    – DeX3
    Commented Nov 16, 2017 at 8:29
  • I was able to make it work after some tinkering by wrapping the with-syntax-table call in an if to check whther or not T-abbrev-syntax-table is actually set.
    – DeX3
    Commented Nov 16, 2017 at 10:08
  • @DeX3 I cannot reproduce the problem with the undefined syntax table in emacs 25.1.50.2. Could you give a receipt for error reproduction? Thanks in advance.
    – Tobias
    Commented Nov 18, 2017 at 11:31
  • @DeX3 Now I had the same error in a buffer with a major-mode basing on c-mode. I had a look at c-mode. It is actually not my fault that things go wrong. The problem is that they directly use (setq abbrev-mode t) in c-mode. Even the doc string of variable abbrev-mode says Use the command ‘abbrev-mode’ to change this variable.! But, it is clear that we have to handle this problem on our side. I will think about it.
    – Tobias
    Commented Nov 19, 2017 at 1:10
  • @DeX3 Note, that I filed a bug-report for c-mode.
    – Tobias
    Commented Nov 19, 2017 at 1:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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