2

Looking at this page it seems title-capitals can get reasonably involved. Do packages exist that perform title-caps with support for some of these rules?

Drew
  • 75,699
  • 9
  • 109
  • 225
ideasman42
  • 8,375
  • 1
  • 28
  • 105

2 Answers2

3

This is a short function that does basic title-capitals:

  • Some words aren't capitalized (the, of, ... etc).
  • The 1st word is always capitalized.
  • Existing capitalized words are left unchanged (typically acronyms, e.g. CPU, API, FIFO .. etc).
  • Characters such as (\ - ' .) prevent the next letter from being capitalized.
  • Without a selection it operates on the whole line.
  • Colon character capitalizes the following word, even if it would normally be skipped: e.g.
    Example: The Test Title
    Question? The Answer

(defun my-upcase-initials-region ()
  (interactive)
  (let ((beg nil)
        (end nil)
        (prev-word-end nil)

        ;; Allow capitals for skip characters after this, so:
        ;;   Warning: An Example
        ;; Capitalizes the `An'.
        (chars-skip-reset '(?: ?! ??))
        ;; Don't capitalize characters directly after these. e.g.
        ;; "Foo-bar" or "Foo\bar" or "Foo's".
        (chars-separator '(?\\ ?- ?' ?.))

        (word-chars "[:alnum:]")
        (word-skip
          (list "a" "an" "and" "as" "at" "but" "by"
            "for" "if" "in" "is" "it" "nor" "of"
            "on" "or" "so" "the" "to" "up" "was" "yet"))
        (is-first t))
    (cond
      ((region-active-p)
        (setq beg (region-beginning))
        (setq end (region-end)))
      (t
        (setq beg (line-beginning-position))
        (setq end (line-end-position))))

    (save-excursion
      (goto-char beg)

      (while (< (point) end)
        (setq prev-word-end (point))
        (skip-chars-forward (concat "^" word-chars) end)
        (let ((word-end
                (save-excursion
                  (skip-chars-forward word-chars end)
                  (point))))

          (unless (memq (char-before (point)) chars-separator)
            (let* ((c-orig (char-to-string (char-after (point))))
                   (c-up (capitalize c-orig)))
              (unless (string-equal c-orig c-up)
                (let ((word (buffer-substring-no-properties (point) word-end)))
                  (when
                    (or
                      ;; Always allow capitalization.
                      is-first
                      ;; If it's not a skip word, allow.
                      (not (member word word-skip))
                      ;; Check the beginning of the
                      ;; previous word doesn't reset first.
                      (save-excursion
                        (and
                          (not (zerop (skip-chars-backward "[:blank:]" prev-word-end)))
                          (memq (char-before (point)) chars-skip-reset))))
                    (delete-region (point) (1+ (point)))
                    (insert c-up))))))
          (goto-char word-end)
          (setq is-first nil))))))
ideasman42
  • 8,375
  • 1
  • 28
  • 105
1

The titlecase package by Case Duckworth has a reasonable set of rules for various titlecase styles such as APA, MLA, AP, NYT, etc. The package is available on MELPA.

Omar
  • 4,732
  • 1
  • 17
  • 32