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)))
nil
(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
(save-excursion
(forward-word -1)
(save-match-data
(let* ((word-start (point))
(text-start
(progn
(while (or (minusp (skip-chars-backward "\""))
(minusp (skip-syntax-backward "\"(")))
t)
(point)))
lowercase-word)
(cond ((and auto-capitalize-words
(let ((case-fold-search nil))
(goto-char word-start)
(looking-at
(concat "\\("
(mapconcat 'downcase
auto-capitalize-words
"\\|")
"\\)\\>"))))
;; user-specified capitalization
(if (not (member (setq lowercase-word
(buffer-substring ; -no-properties?
(match-beginning 1)
(match-end 1)))
auto-capitalize-words))
;; not preserving lower case
(progn ; capitalize!
(undo-boundary)
(replace-match (find lowercase-word
auto-capitalize-words
: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)
(save-restriction
(narrow-to-region (point-min)
word-start)
(and (re-search-backward (auto-capitalize-sentence-end)
nil t)
(= (match-end 0) text-start)
;; verify: preceded by
;; whitespace?
(let ((previous-char
(char-after
(1- text-start))))
;; In some modes, newline
;; (^J, aka LFD) is comment-
;; end, not whitespace:
(or (equal previous-char
?\n)
(equal (char-syntax
previous-char)
? )))
;; verify: not preceded by
;; an abbreviation?
(let ((case-fold-search nil)
(abbrev-regexp
(if (featurep 'xemacs)
"\\<\\([A-Z�-��-�]?[a-z�-��-�]+\\.\\)+\\="
"\\<\\([[:upper:]]?[[:lower:]]+\\.\\)+\\=")))
(goto-char
(1+ (match-beginning 0)))
(or (not
(re-search-backward abbrev-regexp
nil t))
(not
(member
(buffer-substring ; -no-properties?
(match-beginning 0)
(match-end 0))
auto-capitalize-words))))
))))
;; inserting lowercase text?
(let ((case-fold-search nil))
(goto-char word-start)
(looking-at (if (featurep 'xemacs)
"[a-z�-��-�]+"
"[[:lower:]]+")))
(or (eq auto-capitalize t)
(prog1 (y-or-n-p
(format "Capitalize \"%s\"? "
(buffer-substring
(match-beginning 0)
(match-end 0))))
(message ""))))
;; capitalize!
(undo-boundary)
(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))))
(save-excursion
(goto-char beg)
(save-match-data
(while (re-search-forward "\\Sw" end t)
;; recursion!
(let* ((this-command 'self-insert-command)
(non-word-char (char-after (match-beginning 0)))
(last-command-event
(cond ((featurep 'xemacs) ; XEmacs
(character-to-event non-word-char))
(t non-word-char)))) ; GNU Emacs
(auto-capitalize (match-beginning 0)
(match-end 0)
0)))))))))