5

Is it possible to automatically capitalise i when writing in org-mode? I have to manually search all i and replaced it with I, before exporting to PDF. I want to do this only for the English word I, not occurrences of the letter i that may appear within words.

  • 1
    Do you really want to replace all occurrences of the letter i with I? Or do you only want to replace i with I if it represents the English word I (personal pronoun)? Please edit your question to clarify. – itsjeyd Jan 16 '15 at 9:45
  • Hi, only to replace the English word I. – yi.tang.uni Jan 16 '15 at 10:06
  • I thought so :) You can use the code in my answer to tell Emacs to do this for you. Just add it to your init-file. – itsjeyd Jan 16 '15 at 10:13
  • 3
    You could define an abbreviation: C-x a g when the point is after I. Answer the prompt with i. Enable abbrevs mode if not already enabled: M-x abbrevs-mode. Now every time you will type <not a word constituent>i<not a word constituent>, it will be replaced by I. – wvxvw Jan 16 '15 at 12:31
  • 1
    @wvxvw This will have better visibility as an answer. – T. Verron Jan 16 '15 at 13:52
8

Replacing the word i with the word I is a job for abbrevs. It activates after you press the space bar.

However...

Sometimes you do not want i replaced with I, such as when typing i = i + 1 in some programming language, but you do want it to expand when typing comments and strings.

In the case of org-mode you want to expand i in normal text, suppress the expansion in src blocks, except when in a comment or string within a src block.

Here's some code to do that:

(defun my-text-abbrev-expand-p ()
  "Return t if the abbrev is in a text context, which is: in
 comments and strings only when in a prog-mode derived-mode or
 src block in org-mode, and anywhere else."
  (if (or (derived-mode-p 'prog-mode)
          (and (eq major-mode 'org-mode)
               (org-in-src-block-p 'inside)))
      (nth 8 (syntax-ppss))
    t))

(define-abbrev-table 'my-text-abbrev-table ()
  "Abbrev table for text-only abbrevs. Expands only in comments and strings."
  :enable-function #'my-text-abbrev-expand-p)

(dolist (table (list text-mode-abbrev-table
                     prog-mode-abbrev-table))
  (abbrev-table-put table
                    :parents (list my-text-abbrev-table)))

;; Bug fix for python and lisp modes who's abbrev tables aren't set up correctly:

(eval-after-load "python"
  (abbrev-table-put python-mode-abbrev-table
                    :parents (list my-text-abbrev-table
                                   python-mode-skeleton-abbrev-table)))

(eval-after-load "lisp-mode"
  (abbrev-table-put lisp-mode-abbrev-table
                    :parents (list my-text-abbrev-table)))

;; Our only expansion, so far...

(define-abbrev my-text-abbrev-table "i" "I" nil :system t)

If you happen to manage your init.el as an org file you may be interested in the following snippet of additional code to manage your text expansions:

#+NAME: my-text-abbrevs
- i: I
- teh: the


#+begin_src emacs-lisp :var my-text-abbrevs=my-text-abbrevs

;; ...code from above goes here goes here...

(defun my-text-abbrev-table-init (abbrevs-org-list)
  "Parse 'name: expansion' pairs from an org list and insert into abbrev table."
  (message "Creating text-abbrev table...")
  (dolist (abbrev abbrevs-org-list)
    (let* ((pair (split-string abbrev ": +" :omit-nuls " "))
           (name (nth 0 pair))
           (expansion (nth 1 pair)))
      (define-abbrev my-text-abbrev-table name expansion nil :system t))))

(my-text-abbrev-table-init my-text-abbrevs)

#+end_src

Use your normal text editing and org skills to manage a list of expansion words. If it gets too long you can use TAB to collapse or hide it in a sub-heading.

FYI: If you wanted to do the opposite and expand text only in a program context and not in a text context then Emacs already has you covered with the expand package. For example, you could have if expand to if (...) {...} with a template which would otherwise be annoying when writing comments.

The expand package is not mentioned in the Emacs manual.

  • looks great but I can't test the functions. what is the definition of my-eval-after-load? – yi.tang.uni Jan 19 '15 at 9:14
  • :omit-nuls, do you mean :omit-nulls? – yi.tang.uni Jan 21 '15 at 8:39
3

Assuming that you want to capitalize i if it represents the English word "I" (personal pronoun), the following code will do what you want:

(defun capitalize-i ()
  (when (and (eq major-mode 'org-mode) (looking-back " i "))
    (capitalize-word -1)))

(add-hook 'post-self-insert-hook 'capitalize-i)

If you want to replace all occurrences of i with I, change (looking-back " i ") to (looking-back "i") in the code above.


If the solution above turns out to noticeably affect performance, you can instead tell Emacs to replace i with I every time you save your .org file (as suggested by @kaushalmodi in the comments):

(defun capitalize-i ()
  (when (eq major-mode 'org-mode)
    (replace-regexp "i" "I" t (point-min) (point-max))))

(add-hook 'before-save-hook 'capitalize-i)

Note that the third argument to replace-regexp (set to t) specifies that Emacs should

replace only matches surrounded by word boundaries.

So we don't have to specify a more complex regexp (such as \s-i\s-) here.

  • I am worried about the performance hit as emacs will be trying to check if the previous few characters were " i " while the user types each new character. I refrain from using post-self-insert-hook and especially when one usually types a lot in a mode like this. Suggestion: Would replacing i to I in a buffer wide search of ^i\s- or \s-i\s- be more efficient in a before-save-hook? – Kaushal Modi Jan 16 '15 at 12:26
  • @kaushalmodi Probably :) The code above was simply the first thing that popped into my head when I read the question. Would you mind if I added some code that implements your suggestion to my answer? – itsjeyd Jan 16 '15 at 12:56
  • Sure, not a problem. Go ahead :) – Kaushal Modi Jan 16 '15 at 13:49
  • @kaushalmodi, @itsjeyd, thank you very much for your answer and consideration. It works very well with the main body, but 1) it will change i in the src block, 2) will change the i in italic, bold or inline code style. 3) will change /i/ to /I/, resulting invalid links. – yi.tang.uni Jan 16 '15 at 14:32
3

Why not use auto-capitalize package (github page), which can do everything for you, not just i to I.

(require 'auto-capitalize)
(auto-capitalize-mode 1)

And give you a bonus: You can turn off auto-capitalize when cursor is at source block, and turn on it at other place using snippet below, which is from John Kitchin's blog.

Added: if want to escape /+something/, just need to add a another condition, like (looking-back "\\/[A-Za-z]+").

(defun exempt-auto-capitalize ()
  (if (or (looking-back "\\/[A-Za-z]+") (org-in-block-p '("src")))
      (when auto-capitalize
        (auto-capitalize-mode -1))
    (unless auto-capitalize
      (auto-capitalize-mode 1))))

(add-hook 'post-command-hook 'exempt-auto-capitalize)

Cheers! Please give your thanks to these two kind people.

  • great to have it skip the src blocks. how difficulty to have it skip the /i/, i and links? – yi.tang.uni Jan 16 '15 at 14:34
  • @yi.tang.uni Add a condition that tell auto-capitalize to stop when cursor is at word '\i', please confirm the answer. – Leu_Grady Jan 16 '15 at 14:44
  • @yi.tang.uni, sorry for code just now. Some bugs there, has been fixed. – Leu_Grady Jan 16 '15 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.