1

If I define an abbrev like wo -> without, and then type Wo, it's expanded to Without, starting with a capital letter. This is the behavior that I want.

However, I'd like to define the abbrev t -> the, but then an uppercase T is expanded to THE, all in uppercase. Is there a way to get The instead?

2 Answers 2

1

Bottom line: I don't see a way to have a lowercase, single-letter abbrev expand to a capitalized single word. I think your solution (answer) is maybe the best workaround.


The Emacs manual, node Expanding Abbrevs tells us how abbrev expansion handles text case:

Abbrev expansion preserves case: foo expands to find outer otter, and Foo to Find outer otter. FOO expands to Find Outer Otter by default, but if you change the variable abbrev-all-caps to a non-nil value, it expands to FIND OUTER OTTER.

That description doesn't really help when the abbrev has a single character. Should T expand to The or THE?

abbrev-all-caps is a variable defined in abbrev.el.

Its value is nil

Documentation:

Non-nil means expand multi-word abbrevs all caps if abbrev was so.

You can customize this variable.

That option doesn't help either. You're not dealing with a multi-word expansion and you don't want an uppercase expansion.

(That doc sentence is anyway not so good, IMO. I think by "multi-word abbrevs" it really means multi-word expansion. But in "abbrev was so" the word "abbrev" really means the abbrev, not its expansion, and "was so" means "was all caps".)

Yes, what you describe is what the code does. I think the doc is unclear/ambiguous about a single-word expansion from a single letter.

But the behavior corresponds to an associated comment in the code and this seems to have been the behavior "forever" (with the same comment), so I guess we have to consider that it's "by design".

This is the relevant part of the code (in function abbrev-insert, together with a comment that says that if the text to be expanded is all uppercase, then a multiple-word expansion is capitalized. It doesn't say anything explicit about a single-word expansion, but the code upcases it.

(if (not (string-match "[[:lower:]]" name))
    ;; Abbrev was all caps.  If expansion is multiple words,
    ;; normally capitalize each word.
    (if (and (not abbrev-all-caps)
             (save-excursion
               (> (progn (backward-word 1) (point))
                  (progn (goto-char wordstart)
                         (forward-word 1) (point)))))
        (upcase-initials-region wordstart (point))
      (upcase-region wordstart (point)))
  ;; ...
  )

The code uses this sexp to determine whether the expansion is multiple words (which it's not, in the case of the expansion the):

(save-excursion
  (> (progn (backward-word 1) (point)) ; Position at the start of "word" `T'.
     (progn (goto-char wordstart) ; Go to start of abbrev `T'.
            (forward-word 1) (point)))) ; Position at end of "word" `T'.

That backword-word moves back to the beginning of the inserted expansion the, which puts it at the start of the original t, which position is made the value of variable wordstart. And that position is < the position at the end of the first (and only) word, the, of the expansion inserted at wordstart.

That test doesn't return non-nil for an expansion (such as the) of one word. So the sexp (upcase-region wordstart (point)) is used.

1
  • emacs -Q shows the same behavior. abbrev-all-caps is nil.
    – NTC
    Sep 3, 2023 at 23:14
1

It's not the most elegant solution, but of course a workaround is to simply define uppercase and lowercase single-letter abbrevs.

Example:

(define-abbrev-table 'text-mode-abbrev-table
  '(("t" "the" nil :count 0)
    ("T" "The" nil :count 0)))

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.