5

I'm new to Emacs and Org-mode, this is a very basic question. Unfortunately the keywords are too common for me to find the answer.

I would like to create a custom pattern to colour my writing input.

Something like

_theorem: (it would detect _ and a non-whitespace character for the start, and would trace it until the end, or colon)

_theorem 2: (it would be great if this could also include whitespaces)

_theorem on topic 1: (such as this).

The script would thus automatically color all of the above writing that is now in bold. References that would help me do this would also be helpful. Many thanks for helping a beginner!

Update

I've added a follow-up question in the comments on adding multiple such rules that ran into problems. Here's the full code for it. While the first answer to the first question is working perfectly now, the other two new rules seem to be mistakenly formulated or in conflict somehow. On the instances where either of them is found, it breaks the regular coloring of org-mode and does no coloring itself.

(add-hook 'org-mode-hook
          (lambda ()
                (font-lock-add-keywords nil
                                    '((
                                    "\\(^\\|\\s-+\\)\\(_\\([a-zA-Z0-9]+\\s-?\\)\\{0,4\\}:\\)" 2
                                    font-lock-type-face t)))
                (font-lock-add-keywords nil
                                    '((
                                    "\\(\\w+\\(\\s-&\\s-\\w+\\)?,\\s-[0-9]\\{4\\})" 2
                                    font-lock-type-face t)))
                (font-lock-add-keywords nil
                                    '((
                                    "^\\w+:" 2
                                       font-lock-type-face t)))
))
Dan
  • 32,584
  • 6
  • 98
  • 168
puslet88
  • 243
  • 2
  • 10

2 Answers2

10

You can use font-lock-add-keywords to add custom highlights.

In the below example, I am highlighting any string

  • that begins with _ preceded by a white-space character or beginning of the line
  • followed by a single alpha-numeric character
  • followed by anything else
  • ending with :

The highlight color chosen is font-lock-warning-face; you can choose any other face of your liking.

This highlighting will happen only in org-mode.

(add-hook 'org-mode-hook
          (lambda ()
            (font-lock-add-keywords nil
                                    '(("\\(^\\|\\s-+\\)\\(_[a-zA-Z0-9].*?:\\)" 2
                                       font-lock-warning-face t)))))

Update:

To match at most 4 words before the colon,

(add-hook 'org-mode-hook
          (lambda ()
            (font-lock-add-keywords nil
                                    '(("\\(^\\|\\s-+\\)\\(_\\w+\\(?:\\s-+\\w+\\)\\{0,3\\}\\s-*:\\)" 2
                                       font-lock-warning-face t)))))

Update 2:

Preview of the "max 4 words" regexp in re-builder:

enter image description here

Update 3:

Alright, I can't keep on updating this as your question changes. I would suggest that you spend adequate time understanding the elisp regular expression syntax and how the font-lock-add-keywords function works.

  • Elisp Regular Expression
  • font-lock-add-keywords - Do C-h f font-lock-add-keywords RET in emacs and then click on the font-lock.el hyperlink in the buffer that opens. That will take you to the function definition. Read the doc-string for that function.
  • In a similar fashion, you can learn more about any variable by doing C-h v VARIABLE-NAME RET and jumping through the hyperlinks.
  • Keep on experimenting with re-builder till you get a hang of the regexp syntax.

Below is the solution to your last modified request.

(add-hook 'org-mode-hook
          (lambda ()
            (font-lock-add-keywords
             nil
             '(
               ;; Highlight upto 4 words (including ,) between ( and )
               ("\\(([a-zA-Z0-9,]+\\(?:\\s-+[a-zA-Z0-9,]+\\)\\{0,3\\})\\)" 1 font-lock-type-face)
               ;; Highlight ^word:
               ("^\\([a-zA-Z0-9]+:\\)" 1 font-lock-type-face)
               ;; Highlight upto 4 words between _ and :
               ("\\(^\\|\\s-+\\)\\(_[a-zA-Z0-9]+\\(?:\\s-+[a-zA-Z0-9]+\\)\\{0,3\\}\\s-*:\\)" 2 font-lock-type-face)
               ))))

Alternative Solution, does the same thing as in Update 3

This approach updates font-lock-keywords-alist instead of polluting font-lock-keywords. There is no easy way to fix incorrect regexp added to font-lock-keywords other than restarting emacs.

But usually font-lock-keywords-alist is much smaller in size and can be tweaked manually or can be reset completely by doing

(setq font-lock-keywords-alist nil)

while you are testing out the regexp.

(font-lock-add-keywords
 'org-mode
 '(
   ;; Highlight upto 4 words (including ,) between ( and )
   ("\\(([a-zA-Z0-9,]+\\(?:\\s-+[a-zA-Z0-9,]+\\)\\{0,3\\})\\)" 1 font-lock-type-face)
   ;; Highlight ^word:
   ("^\\([a-zA-Z0-9]+:\\)" 1 font-lock-type-face)
   ;; Highlight upto 4 words between _ and :
   ("\\(^\\|\\s-+\\)\\(_[a-zA-Z0-9]+\\(?:\\s-+[a-zA-Z0-9]+\\)\\{0,3\\}\\s-*:\\)" 2 font-lock-type-face)
   )
 )
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
  • 1
    Brilliant! It does exactly the trick. Is there a good online regex checker for emacs? I tried to alter the expression to a new constraint so as to include maximum 4 words before the colon. However there seem to be some parts of the syntax that I couldn't figure out very quickly with the help of the manual. Thanks again for your help! – puslet88 Feb 13 '15 at 15:42
  • Check out http://emacs.stackexchange.com/q/869/115. Also search for terms like `regexp` and `visual` in this stackexchange for more examples. – Kaushal Modi Feb 13 '15 at 15:44
  • Perfect, exactly what I needed, thanks again! – puslet88 Feb 13 '15 at 15:47
  • You might want to replace the regexp between the double with this: `\(^\|\s-+\)\(_\w+\(?:\s-+\w+\)\{0,3\}\s-*:\)`. Make sure to replace the single back-slashes with double back-slashes when doing so. [[Regexp Backslash Reference](https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Backslash.html)] – Kaushal Modi Feb 13 '15 at 16:02
  • Thanks again! Strange, but I couldn't get the single backslashes to work with the built-in RE-builder, best similar case I could do was `"\\(^\\|\\s-+\\)\\(_\\w+\\(?:\\s-+\\w+\\)\\{0,3\\}\\s-*:\\)"` Not sure if this may have been due to inadequacies of the interface or me. But this feature is great! When I add more hooks of the same type "font-lock-add-keywords", should I create a new "add-hook" or should I add them under the same hook somehow. Any idea what happens if one of the partially includes the other. In case of conflict, perhaps either the first or last one is used? Many thanks again. – puslet88 Feb 13 '15 at 16:41
  • You use only single backslashes in the regexp by default. Only when you pass regexp expressions as string as in the `font-lock-add-keywords` use case, you need to escape those backslashes. So each backslash doubles. I don't use the RE-builder; I test out the regexps visually using the `visual-regexp` or `anzu` packages. – Kaushal Modi Feb 13 '15 at 16:44
  • Thanks again, this is most useful. If I add more than one coloring rule, should I create another hook or can I add them inside the same hook too? – puslet88 Feb 13 '15 at 16:48
  • I tried out `re-builder` with the string version of the regexp with double backslashes and that works. I have updated my answer to show the test text I used for this regexp. For adding second set of rules, you add that other `(font-lock-add-keywords ..)` statement withing the `(lambda () ..)`. – Kaushal Modi Feb 13 '15 at 16:52
  • Oh, sorry I misread your advice attached to the single back-slash version. Yes, the double backslash version works perfectly! Inside the 'lambda' then, ok thanks so much for the very thorough consultation! – puslet88 Feb 13 '15 at 16:52
  • I'd hate to keep this going, but perhaps you can troubleshoot me a bit. I created two more rules for "^\\w+:" (word+colon at the beginning of a line) and for "\\(^\\|\\s-+\\)\\(_\\([a-zA-Z0-9]+\\s-?\\)\\{0,4\\}:\\)" (something like "(Author, 2002)"). However the new ones only seem to block out the regular colour coding (headings become black from e.g. blue under the headings where the examples are), and does not colour any test examples. Perhaps I have not yet fully understood the backslashes, or maybe they are clashing with each other. Any ideas? Thx! I've added the full code in the question. – puslet88 Feb 13 '15 at 17:23
  • The updated regexp (in Update 3) does not mess up the default font lock on headers, etc. You will need to restart emacs as the `font-lock-keywords` alist would have got polluted. – Kaushal Modi Feb 13 '15 at 19:30
  • The [font-lock-studio](http://melpa.org/#/font-lock-studio) package can be fun/handy. – Greg Hendershott Feb 13 '15 at 21:46
  • Thanks @kaushalmodi again for the very thorough response. Very educational! This puts me very well on my way! Thanks to Greg this package looks most helpful too. – puslet88 Feb 15 '15 at 14:46
1

A while ago, I posted a similar question here: check How to highlight text permanently in org-mode

Not exactly what you are asking for, this gives you more, customize it as you like:

(setq org-emphasis-alist
      '(
        ("*" (:foreground "cyan" :weight bold))
        ("/" (:foreground "cyan" :slant italic))
        ("_" (:foreground "cyan" :underline t))
        ("=" (:foreground "cyan" :weight bold))
        ("~" (:foreground "cyan" :weight bold
                          :box (:line-width 3 :color "#545454" :style released-button)))
        ("+" (:foreground "cyan" :strike-through t))))
CodyChan
  • 2,599
  • 1
  • 19
  • 33