I want the auto-capitalize minor mode to work well with org-mode.

Currently, auto-capitalize does not capitalize the first word in headings or list items (i.e., the word after '*' or '-'). For example, the following transformations are not working:

* heading      -->   * Heading
** subheading  -->   ** Subheading
 - item        -->    - Item

Any ideas on how to fix auto-capitalize to do this?

I have tried changing the sentence-end regexp (which is used by auto-capitalize to decide what to capitalize), but it didn't work out. I added the following without success: \\(^\\*+ \\|^ +- \\)

Many thanks!

1 Answer 1


First of all, sentence-end should match everything up to the fist word of the next sentence.

The following sets a buffer-local binding of sentence-end in org-mode buffers, which seems to work for me.

(defun org-auto-capitalize-headings-and-lists ()
  "Create a buffer-local binding of sentence-end to auto-capitalize
section headings and list items."
  (make-local-variable 'sentence-end)
  (setq sentence-end (concat (rx (or
                                  ;; headings
                                  (seq line-start (1+ "*") (1+ space))
                                  ;; list and checklist items
                                  (seq line-start (0+ space) "-" (1+ space) (? (or "[ ]" "[X]") (1+ space)))))
                             "\\|" (sentence-end))))

(add-hook 'org-mode-hook #'org-auto-capitalize-headings-and-lists)

But this is not enough; auto-capitalize checks if this-command is "equivalent" to self-insert-command, or if it is newline or newline-and-indent. Org-mode rebinds RET to org-return, so auto-capitalize does not detect that it should try to capitalize after the user hits RET.

I think this is a bug in auto-capitalize; below is a fixed version of auto-capitalize which does work with org-mode.

(defun auto-capitalize (beg end length)
  "If `auto-capitalize' mode is on, then capitalize the previous word.
The previous word is capitalized (or upcased) if it is a member of the
`auto-capitalize-words' list; or if it begins a paragraph or sentence.

Capitalization occurs only if the current command was invoked via a
self-inserting non-word character (e.g. whitespace or punctuation)\; but
if the `auto-capitalize-yank' option is set, then the first word of
yanked sentences will be capitalized as well.

Capitalization can be disabled in specific contexts via the
`auto-capitalize-predicate' variable.

This should be installed as an `after-change-function'."
  (if (and auto-capitalize
           (or (null auto-capitalize-predicate)
               (funcall auto-capitalize-predicate)))
      (cond ((or (and (or (eq this-command 'self-insert-command)
                          ;; LaTeX mode binds "." to TeX-insert-punctuation,
                          ;; and "\"" to TeX-insert-quote:
                          (let ((key (this-command-keys)))
                            ;; XEmacs `lookup-key' signals "unable to bind
                            ;; this type of event" for commands invoked via
                            ;; the mouse:
                            (and (if (and (vectorp key)
                                          (> (length key) 0)
                                          (fboundp 'misc-user-event-p)
                                          (misc-user-event-p (aref key 0)))
                                   (memq (lookup-key global-map key t) ;new code
                                          '(self-insert-command newline newline-and-indent))) ;new code
                                 ;; single character insertion?
                                 (= length 0)
                                 (= (- end beg) 1))))
                      (let ((self-insert-char
                             (cond ((featurep 'xemacs) ; XEmacs
                                    (event-to-character last-command-event
                                                        nil nil t))
                                   (t last-command-event)))) ; GNU Emacs
                        (not (equal (char-syntax self-insert-char) ?w))))
                 (eq this-command 'newline)
                 (eq this-command 'newline-and-indent))
             ;; self-inserting, non-word character
             (if (and (> beg (point-min))
                      (equal (char-syntax (char-after (1- beg))) ?w))
                 ;; preceded by a word character
                   (forward-word -1)
                     (let* ((word-start (point))
                               (while (or (minusp (skip-chars-backward "\""))
                                          (minusp (skip-syntax-backward "\"(")))
                       (cond ((and auto-capitalize-words
                                   (let ((case-fold-search nil))
                                     (goto-char word-start)
                                      (concat "\\("
                                              (mapconcat 'downcase
                              ;; user-specified capitalization
                              (if (not (member (setq lowercase-word
                                                     (buffer-substring ; -no-properties?
                                                      (match-beginning 1)
                                                      (match-end 1)))
                                  ;; not preserving lower case
                                  (progn ; capitalize!
                                    (replace-match (find lowercase-word
                                                         :key 'downcase
                                                         :test 'string-equal)
                                                   t t))))
                             ((and (or (equal text-start (point-min)) ; (bobp)
                                       (progn ; beginning of paragraph?
                                         (goto-char text-start)
                                         (and (= (current-column) left-margin)
                                              (zerop (forward-line -1))
                                              (looking-at paragraph-separate)))
                                       (progn ; beginning of paragraph?
                                         (goto-char text-start)
                                         (and (= (current-column) left-margin)
                                              (re-search-backward paragraph-start
                                                                  nil t)
                                              (= (match-end 0) text-start)
                                              (= (current-column) left-margin)))
                                       (progn ; beginning of sentence?
                                         (goto-char text-start)
                                           (narrow-to-region (point-min)
                                           (and (re-search-backward (auto-capitalize-sentence-end)
                                                                    nil t)
                                                (= (match-end 0) text-start)
                                                ;; verify: preceded by
                                                ;; whitespace?
                                                (let ((previous-char
                                                        (1- text-start))))
                                                  ;; In some modes, newline
                                                  ;; (^J, aka LFD) is comment-
                                                  ;; end, not whitespace:
                                                  (or (equal previous-char
                                                      (equal (char-syntax
                                                             ? )))
                                                ;; verify: not preceded by
                                                ;; an abbreviation?
                                                (let ((case-fold-search nil)
                                                       (if (featurep 'xemacs)
                                                   (1+ (match-beginning 0)))
                                                  (or (not
                                                       (re-search-backward abbrev-regexp
                                                                           nil t))
                                                        (buffer-substring ; -no-properties?
                                                         (match-beginning 0)
                                                         (match-end 0))
                                   ;; inserting lowercase text?
                                   (let ((case-fold-search nil))
                                     (goto-char word-start)
                                     (looking-at (if (featurep 'xemacs)
                                   (or (eq auto-capitalize t)
                                       (prog1 (y-or-n-p
                                               (format "Capitalize \"%s\"? "
                                                        (match-beginning 0)
                                                        (match-end 0))))
                                         (message ""))))
                              ;; capitalize!
                              (goto-char word-start)
                              (capitalize-word 1))))))))
            ((and auto-capitalize-yank
                  ;; `yank' sets `this-command' to t, and the
                  ;; after-change-functions are run before it has been
                  ;; reset:
                  (or (eq this-command 'yank)
                      (and (= length 0) ; insertion?
                           (eq this-command 't))))
               (goto-char beg)
                 (while (re-search-forward "\\Sw" end t)
                   ;; recursion!
                   (let* ((this-command 'self-insert-command)
                          (non-word-char (char-after (match-beginning 0)))
                           (cond ((featurep 'xemacs) ; XEmacs
                                  (character-to-event non-word-char))
                                 (t non-word-char)))) ; GNU Emacs
                     (auto-capitalize (match-beginning 0)
                                      (match-end 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.