<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <atom:link href="http://kitchingroup.cheme.cmu.edu/blog/feed/index.xml" rel="self" type="application/rss+xml" />
    <title>The Kitchin Research Group</title>
    <link>https://kitchingroup.cheme.cmu.edu/blog</link>
    <description>Chemical Engineering at Carnegie Mellon University</description>
    <pubDate>Sat, 01 Nov 2025 13:47:46 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    
    <item>
      <title>Object-oriented font-locking in emacs-lisp</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2017/04/07/Object-oriented-font-locking-in-emacs-lisp</link>
      <pubDate>Fri, 07 Apr 2017 15:00:54 EDT</pubDate>
      <category><![CDATA[emacs]]></category>
      <category><![CDATA[fontlock]]></category>
      <guid isPermaLink="false">1WygBs0ML7IO8fyyXao5G7m0YWQ=</guid>
      <description>Object-oriented font-locking in emacs-lisp</description>
      <content:encoded><![CDATA[


&lt;div id="table-of-contents"&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id="text-table-of-contents"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#orga752041"&gt;1. Font-locking the elements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#org4b6ed31"&gt;2. Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
I have been interested in functional text for a long time. With functional text you can read it, but also interact with it &lt;a class='org-ref-reference' href="#kitchin-2015-examp"&gt;kitchin-2015-examp&lt;/a&gt;. Lately I have been thinking about how to use some features of object-oriented programming to functional text. The premise is to use an object hierarchy to encapsulate some knowledge, and provide &lt;i&gt;functionality&lt;/i&gt; on the objects. We can use inheritance to customize some of this knowledge and functionality.
&lt;/p&gt;

&lt;p&gt;
The example I will work out here is to provide functional text for chemical elements. The goal is to define some objects that represent elements, and construct font-lock rules from the objects to make the text functional in Emacs. Functional here means it stands out so we know there is something special about it, it has a tooltip to get some information (like what type of element it is, and its atomic mass), and it is clickable to get more functionality.
&lt;/p&gt;

&lt;p&gt;
This post will make a lot more sense in this video: &lt;a href="https://www.youtube.com/watch?v=IWxCj5cr8rY"&gt;https://www.youtube.com/watch?v=IWxCj5cr8rY&lt;/a&gt;
&lt;/p&gt;

&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/IWxCj5cr8rY" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;
First, we create a base class of an Element. I use an instance tracker for this to make book keeping easy later. The base class will have a name, synonyms for the name, and a default face to color it. We define a few methods to get an atomic mass and search google for the element. Finally, we provide a function to generate a tooltip, and a font-lock rule.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #0000FF;"&gt;defvar&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;atomic-masses&lt;/span&gt; '((&lt;span style="color: #008000;"&gt;"Hydrogen"&lt;/span&gt; . 1.008)
                        (&lt;span style="color: #008000;"&gt;"Argon"&lt;/span&gt; . 39.948)
                        (&lt;span style="color: #008000;"&gt;"Sodium"&lt;/span&gt; . 22.989)
                        (&lt;span style="color: #008000;"&gt;"Palladium"&lt;/span&gt; . 106.42))
  &lt;span style="color: #036A07;"&gt;"a-list of atomic masses."&lt;/span&gt;)

(&lt;span style="color: #0000FF;"&gt;defvar&lt;/span&gt; &lt;span style="color: #BA36A5;"&gt;elements&lt;/span&gt; '() &lt;span style="color: #036A07;"&gt;"List of known elements"&lt;/span&gt;)
(&lt;span style="color: #0000FF;"&gt;setq&lt;/span&gt; elements '()) &lt;span style="color: #8D8D84;"&gt;;; &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;this is to start over&lt;/span&gt;

(&lt;span style="color: #0000FF;"&gt;defclass&lt;/span&gt; &lt;span style="color: #6434A3;"&gt;element&lt;/span&gt; (eieio-instance-tracker)
  ((tracking-symbol &lt;span style="color: #006FE0;"&gt;:initform&lt;/span&gt; elements
                    &lt;span style="color: #006FE0;"&gt;:documentation&lt;/span&gt; &lt;span style="color: #036A07;"&gt;"Variable that holds all class instances."&lt;/span&gt;)
   (name &lt;span style="color: #006FE0;"&gt;:initarg&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt;
         &lt;span style="color: #006FE0;"&gt;:documentation&lt;/span&gt; &lt;span style="color: #036A07;"&gt;"The name of the element"&lt;/span&gt;)
   (synonyms &lt;span style="color: #006FE0;"&gt;:initarg&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:synonyms&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:initform&lt;/span&gt; '()
             &lt;span style="color: #006FE0;"&gt;:documentation&lt;/span&gt; &lt;span style="color: #036A07;"&gt;"List of regular expressions that match the element."&lt;/span&gt;)
   (face &lt;span style="color: #006FE0;"&gt;:initarg&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:face&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:initform&lt;/span&gt; 'font-lock-type-face
         &lt;span style="color: #006FE0;"&gt;:documentation&lt;/span&gt; &lt;span style="color: #036A07;"&gt;"The face to use with font-lock."&lt;/span&gt;))
  &lt;span style="color: #036A07;"&gt;"Base class for a chemical element."&lt;/span&gt;)

(&lt;span style="color: #0000FF;"&gt;defmethod&lt;/span&gt; &lt;span style="color: #006699;"&gt;element-atomic-mass&lt;/span&gt; ((x element))
  &lt;span style="color: #036A07;"&gt;"Return atomic mass from `&lt;/span&gt;&lt;span style="color: #D0372D;"&gt;atomic-masses&lt;/span&gt;&lt;span style="color: #036A07;"&gt;'."&lt;/span&gt;
  (cdr (assoc (&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt;) atomic-masses)))

(&lt;span style="color: #0000FF;"&gt;defmethod&lt;/span&gt; &lt;span style="color: #006699;"&gt;element-help-echo&lt;/span&gt; ((x element))
  &lt;span style="color: #036A07;"&gt;"A tooltip for the element.&lt;/span&gt;
&lt;span style="color: #036A07;"&gt;It will look like class (inherited classes) mass=atomic-mass"&lt;/span&gt;
  (format &lt;span style="color: #008000;"&gt;"%s %s: mass=%s"&lt;/span&gt;
          (eieio-object-class x)
          (mapcar 'eieio-class-name (eieio-class-parents (eieio-object-class x)))
          (&lt;span style="color: #0000FF;"&gt;or&lt;/span&gt; (element-atomic-mass x) &lt;span style="color: #008000;"&gt;"unknown"&lt;/span&gt;)))

(&lt;span style="color: #0000FF;"&gt;defmethod&lt;/span&gt; &lt;span style="color: #006699;"&gt;element-search&lt;/span&gt; ((x element))
  &lt;span style="color: #036A07;"&gt;"Search google for the element"&lt;/span&gt;
  (google-this-string nil (&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt;) t))

(&lt;span style="color: #0000FF;"&gt;defmethod&lt;/span&gt; &lt;span style="color: #006699;"&gt;element-font-lock-rule&lt;/span&gt; ((x element))
  &lt;span style="color: #036A07;"&gt;"Return font-lock rule for the element."&lt;/span&gt;
  (&lt;span style="color: #0000FF;"&gt;let&lt;/span&gt; ((map (make-sparse-keymap)))
    (define-key map [mouse-1]
      (&lt;span style="color: #0000FF;"&gt;lambda&lt;/span&gt; ()
        &lt;span style="color: #036A07;"&gt;"Construct the object and run `&lt;/span&gt;&lt;span style="color: #D0372D;"&gt;element-search&lt;/span&gt;&lt;span style="color: #036A07;"&gt;' on it."&lt;/span&gt;
        (&lt;span style="color: #0000FF;"&gt;interactive&lt;/span&gt;)
        (element-search
         (eieio-instance-tracker-find
          (get-text-property (point) 'element-name)
          &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt; 'elements))))

    (list
     &lt;span style="color: #8D8D84;"&gt;;; &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;Construct the pattern to match&lt;/span&gt;
     (rx-to-string `(: bow
                       (&lt;span style="color: #0000FF;"&gt;or&lt;/span&gt;  ,(&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt;)
                            ,@(&lt;span style="color: #0000FF;"&gt;loop&lt;/span&gt; for sy in (&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:synonyms&lt;/span&gt;)
                                    collect `(regexp ,sy)))
                       eow))
     0  &lt;span style="color: #8D8D84;"&gt;;; &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;font-lock the whole match&lt;/span&gt;
     &lt;span style="color: #8D8D84;"&gt;;; &lt;/span&gt;&lt;span style="color: #8D8D84; font-style: italic;"&gt;These are the properties to put on the matches&lt;/span&gt;
     `(&lt;span style="color: #0000FF;"&gt;quote&lt;/span&gt; (face ,(&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:face&lt;/span&gt;)
                   element-name ,(&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt;)
                   local-map ,map
                   mouse-face 'highlight
                   help-echo ,(element-help-echo x))))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now, we can define some sub-classes. These are families of elements. For a metal, we change the face. For noble gases, we override the help-echo function, and for alkali metals we override the search function. The point is that we can customize the behavior for different classes.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #0000FF;"&gt;defclass&lt;/span&gt; &lt;span style="color: #6434A3;"&gt;metal&lt;/span&gt; (element)
  ((face &lt;span style="color: #006FE0;"&gt;:initform&lt;/span&gt; '(&lt;span style="color: #006FE0;"&gt;:foreground&lt;/span&gt; &lt;span style="color: #008000;"&gt;"orange"&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:underline&lt;/span&gt; t)))
  &lt;span style="color: #036A07;"&gt;"Metal"&lt;/span&gt;)

(&lt;span style="color: #0000FF;"&gt;defclass&lt;/span&gt; &lt;span style="color: #6434A3;"&gt;noble-gas&lt;/span&gt; (element)
  ()
  &lt;span style="color: #036A07;"&gt;"A noble gas"&lt;/span&gt;)

(&lt;span style="color: #0000FF;"&gt;defmethod&lt;/span&gt; &lt;span style="color: #006699;"&gt;element-help-echo&lt;/span&gt; ((x noble-gas))
  &lt;span style="color: #036A07;"&gt;"I am not a common element."&lt;/span&gt;)

(&lt;span style="color: #0000FF;"&gt;defclass&lt;/span&gt; &lt;span style="color: #6434A3;"&gt;alkali&lt;/span&gt; (element metal)
  ()
  &lt;span style="color: #036A07;"&gt;"Alkali metal"&lt;/span&gt;)

(&lt;span style="color: #0000FF;"&gt;defmethod&lt;/span&gt; &lt;span style="color: #006699;"&gt;element-search&lt;/span&gt; ((x alkali))
  (&lt;span style="color: #0000FF;"&gt;let&lt;/span&gt; ((visible-bell t))
    (beep)
    (message &lt;span style="color: #008000;"&gt;"You clicked on an alkali metal: %s."&lt;/span&gt; (&lt;span style="color: #0000FF;"&gt;oref&lt;/span&gt; x &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt;))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now we can define some elements. These are all instances of each class. For some, we define synonyms, and alternate appearances. Note the synonyms are regular expressions.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(element &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt; &lt;span style="color: #008000;"&gt;"Hydrogen"&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:synonyms&lt;/span&gt; '(&lt;span style="color: #008000;"&gt;"H"&lt;/span&gt; &lt;span style="color: #008000;"&gt;"[hH]ydrogen"&lt;/span&gt;))

(noble-gas &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt; &lt;span style="color: #008000;"&gt;"Argon"&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:synonyms&lt;/span&gt; '(&lt;span style="color: #008000;"&gt;"Ar"&lt;/span&gt;))

(alkali &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt; &lt;span style="color: #008000;"&gt;"Sodium"&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:synonyms&lt;/span&gt; '(&lt;span style="color: #008000;"&gt;"Na"&lt;/span&gt; &lt;span style="color: #008000;"&gt;"[nN]atrium"&lt;/span&gt;))
(alkali &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt; &lt;span style="color: #008000;"&gt;"Potassium"&lt;/span&gt; &lt;span style="color: #006FE0;"&gt;:synonyms&lt;/span&gt; '(&lt;span style="color: #008000;"&gt;"K"&lt;/span&gt;) &lt;span style="color: #006FE0;"&gt;:face&lt;/span&gt; '(&lt;span style="color: #006FE0;"&gt;:foreground&lt;/span&gt; &lt;span style="color: #008000;"&gt;"red"&lt;/span&gt;))

(metal &lt;span style="color: #006FE0;"&gt;:name&lt;/span&gt; &lt;span style="color: #008000;"&gt;"Palladium"&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The instance tracker shows us the defined objects.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;elements
&lt;/pre&gt;
&lt;/div&gt;

&lt;div id="outline-container-orga752041" class="outline-2"&gt;
&lt;h2 id="orga752041"&gt;&lt;span class="section-number-2"&gt;1&lt;/span&gt; Font-locking the elements&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
Here we generate font-lock rules from the set of objects. Each object will return its font-lock rule, so we just map over each object to get the list of rules.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-emacs-lisp"&gt;(font-lock-add-keywords
 nil
 (mapcar 'element-font-lock-rule elements))

(font-lock-fontify-buffer)
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;
Now any time we have Palladium or Hydrogen it will be highlighted.  And Sodium and Argon.
&lt;/p&gt;

&lt;p&gt;
Here are some synonyms: hydrogen H Natrium natrium.
&lt;/p&gt;

&lt;p&gt;
Potassium has a different color than Na.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-org4b6ed31" class="outline-2"&gt;
&lt;h2 id="org4b6ed31"&gt;&lt;span class="section-number-2"&gt;2&lt;/span&gt; Summary&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
This seems like a pretty useful way to encapsulate functionality for functional text. Clearly most of the work should go in the base class, and the inheritance model, so you do not have to repeat things unnecessarily. Some features are missing, like conveniently adding synonyms and regenerating the font-lock rules. It is also the case that we do not persist these objects. They could be written to disk so that they can be reloaded later.
&lt;/p&gt;

&lt;p&gt;
The actions you can use on a highlighted word are pretty limited in this implementation. It would be nice if you got a menu of options that was user extendable and dynamic. Either a popup menu, or a hydra would be fine.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Copyright (C) 2017 by John Kitchin. See the &lt;a href="/copying.html"&gt;License&lt;/a&gt; for information about copying.&lt;p&gt;
&lt;p&gt;&lt;a href="/org/2017/04/07/Object-oriented-font-locking-in-emacs-lisp.org"&gt;org-mode source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Org-mode version = 9.0.5&lt;/p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
