aboutsummaryrefslogtreecommitdiff
path: root/modules/custom-case.el
diff options
context:
space:
mode:
Diffstat (limited to 'modules/custom-case.el')
-rw-r--r--modules/custom-case.el120
1 files changed, 59 insertions, 61 deletions
diff --git a/modules/custom-case.el b/modules/custom-case.el
index d30ebf942..876226958 100644
--- a/modules/custom-case.el
+++ b/modules/custom-case.el
@@ -49,6 +49,18 @@
(downcase-region (car bounds) (cdr bounds))
(user-error "No symbol at point")))))
+(defun cj/--title-case-capitalize-word-p (word is-first prev-word-end word-skip chars-skip-reset)
+ "Return non-nil when WORD at point should be capitalized in title case.
+Point is at WORD's first character. WORD is capitalized when it is the first
+word (IS-FIRST), is not a minor skip word (in WORD-SKIP), or immediately follows
+a skip-reset character (one of CHARS-SKIP-RESET: : ! ?), reached by skipping
+blanks back to PREV-WORD-END."
+ (or is-first
+ (not (member word word-skip))
+ (save-excursion
+ (and (not (zerop (skip-chars-backward "[:blank:]" prev-word-end)))
+ (memq (char-before (point)) chars-skip-reset)))))
+
(defun cj/title-case-region ()
"Capitalize the region in title case format.
Title case is a capitalization convention where major words are capitalized,
@@ -58,67 +70,53 @@ considered major words. Short (i.e., three letters or fewer) conjunctions,
short prepositions, and all articles are considered minor words."
(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" "nor" "of"
- "on" "or" "so" "the" "to" "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
- ;; work on uppercased text (e.g., headlines) by downcasing first
- (downcase-region beg end)
- (goto-char beg)
-
- (while (< (point) end)
- (setq prev-word-end (point))
- (skip-chars-forward (concat "^" word-chars) end)
- (when (>= (point) end) ;; no word chars remaining
- (goto-char end))
- (let ((word-end
- (save-excursion
- (skip-chars-forward word-chars end)
- (point))))
-
- (unless (or (>= (point) end)
- (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))))))
+ (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" "nor" "of"
+ "on" "or" "so" "the" "to" "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
+ ;; work on uppercased text (e.g., headlines) by downcasing first
+ (downcase-region beg end)
+ (goto-char beg)
+ (while (< (point) end)
+ (setq prev-word-end (point))
+ (skip-chars-forward (concat "^" word-chars) end)
+ (when (>= (point) end) ;; no word chars remaining
+ (goto-char end))
+ (let ((word-end
+ (save-excursion
+ (skip-chars-forward word-chars end)
+ (point))))
+ (unless (or (>= (point) end)
+ (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 (cj/--title-case-capitalize-word-p
+ word is-first prev-word-end word-skip chars-skip-reset)
+ (delete-region (point) (1+ (point)))
+ (insert c-up))))))
+ (goto-char word-end)
+ (setq is-first nil))))))
;; replace the capitalize-region keybinding to call title-case
(keymap-global-set "<remap> <capitalize-region>" #'cj/title-case-region)