Irreal: Describe Symbol Redux

I just came across this post from Bozhidar Batsov about describe-symbol. It’s a help command, bound to Ctrl+h o, that combines the results of describe-function and describe-variable. The putative advantage of this (newish) command is that a single key shortcut serves to get information on both functions and variables.

My first thought, when reading Batsov’s post was that the command was a real win but then I started wondering whether muscle memory would keep me using the bindings, Ctrl+h f and Ctrl+h v for describe-function and describe-variable instead. It turns out that we have an answer to that question.

As I was preparing this post, I discovered that I’d written about the describe-symbol command back in 2022. Back then, I also thought it was a good replacement but I don’t believe I’ve ever used it since. Indeed, I’d forgotten all about it. Still, if you’re new to Emacs and haven’t developed muscle memory for the old commands, it could be a win. If you’re a new user or not as set in your ways as I am, this single command is only one thing to remember instead of two.

-1:-- Describe Symbol Redux (Post jcs)--L0--C0--February 26, 2024 05:04 PM

Sacha Chua: 2024-02-26 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, lobste.rs, kbin, programming.dev, lemmy, communick.news, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View org source for this post
-1:-- 2024-02-26 Emacs news (Post Sacha Chua)--L0--C0--February 26, 2024 01:20 PM

Bozhidar Batsov: Emacs: Dead and Loving It

What is dead cannot die.

– A Song of Ice and Fire (a.k.a. Game of Thrones)

Emacs was originally created in 1976. I was born in 1984. I’ve been using Emacs as my primary editor since 2005. Back then GNU Emacs was still reeling from the schism with XEmacs1 and a lot of people felt that the project might be near the end of its long and storied history. I recall the Emacs development wasn’t moving particularly fast at the time, modern text editors and IDEs (e.g. TextMate and Eclipse) were on the rise, and there were quite a few articles proclaiming the death of Emacs. Yet Emacs is still here 19 years later and some of the editors and IDEs that were popular in 2005 are not. This year Emacs will turn 48 years, which is an amazing achievement for any piece of software!

Last week another Reddit thread on Emacs’s death created some ripples in the Emacs community. I didn’t even read it, because it’s always more or less the same:

  • Emacs is too complicated (different) for people used to “modern” editors
  • Emacs is not as popular as X, Y and Z
  • Emacs is not as fast as X, Y and Z in some context
  • Emacs is old, so it’s probably (technologically) outdated
  • Lisp sucks, let’s just use JavaScript everywhere

Highly subjective personal preferences aside, people often conflate how popular something is with how good or lively it actually is. If you look at the market share of Emacs, things definitely don’t look great:2

editor_usage.jpg

On the other hand, both the upstream Emacs development and the ecosystem of third-party packages feel to me more active than ever. At a time when some people are ready to write off Emacs as dead yet again, I’d be more inclined to pronounce a “Golden Age of Emacs”.3

Why so you might ask. Here are a couple of reasons that immediately come to mind:

  • There are around 6,000 Emacs packages available (on the MELPA repository alone)
  • Emacs 29 has built-in support for modern technologies like LSP and TreeSitter
  • Tools like magit and org-mode are widely admired even outside Emacs, but haven’t been completely emulated by any other editor
  • The core Emacs Lisp APIs have been extended and refined a lot in recent years (think seq.el, map.el, subr-x.el, project.el, xref.el, etc)
  • Emacs distros like Spacemacs and Doom Emacs have made it easier for newcomers to experience the full power of Emacs (and have enticed quite a few people from the world of vim)
  • The popularity of Clojure drove a lot of curious developers to Emacs, as it was the first editor to provide solid support for Clojure
  • Emacs 28 brought massive performance gains with native compilation.
  • Emacs 29 features a pure GTK front-end (a.k.a pgtk). This means that Emacs 29 works natively on Wayland (and Windows 11 + WSL by association).
  • Last, but not least - Emacs 29 has proper support for emojis!

Much of this happened in the last 5 years, which is pretty amazing for a dying editors that has been rendered irrelevant but its vastly superior competitors.

It amuses me how many people keep chasing after shiny and new things, even if they don’t really need them. Emacs has covered all of my use-cases for so long that I don’t even bother to upgrade to the new releases right way. I’m writing this article in Emacs 28, as I didn’t really have much of an incentive to upgrade to Emacs 29, despite it packing a lot of improvements. Even if Emacs didn’t add any few features for the next decade I’d be a pretty happy camper.4 Perhaps that’s a reflection of my appreciation for minimalism, perhaps I’ve grown wiser with age, but I’m no longer equating “progress” with “more”.

I’ve written at length about the merits of Emacs in the past, so I’m not going to repeat myself here. I’ll just remind you about the old saying “Don’t judge a book by its cover”. Or by it’s popularity or marketing campaigns for that matter. Essence is what matters, not superficial appearances.

Whatever doesn’t kill you simply makes you stranger.

– The Joker (The Dark Knight)

Emacs is certainly a very strange editor by modern standards. I’m guessing that’s the main reason why so many people quickly dismiss it. But its remarkable resilience is an indication that it’s probably made of stronger stuff than most editors. If you want to invest into a tool that has stood the test of time and will probably continue to be useful 50 years down the road - your options besides Emacs are quite limited…

It’s true that Emacs was at one point one of the two most popular text editors in the world, but those days are long gone and there’s little point in dwelling on them. Still, today it probably has a lot more users in absolute terms than in the 80s and early 90s. World domination is not a big priority for Emacs, but it may still be in the cards, given the average lifespan of many of its competitors.

Emacs is no more dead than usual and it likes it that way. M-x forever!

  1. See https://www.emacswiki.org/emacs/EmacsSchism 

  2. See https://survey.stackoverflow.co/2023/#section-most-popular-technologies-integrated-development-environment 

  3. And I’m not the only one who thinks this way

  4. I also need some time to catch up with everything new from the past few years. Emacs is so dead that its users can’t catch up with all the progress it makes from the grave! 

-1:-- Emacs: Dead and Loving It (Post Bozhidar Batsov (bozhidar@batsov.net))--L0--C0--February 26, 2024 09:47 AM

Mario Jason Braganza: Org Mode Narrowing and Widening Buffers

tldr: C-x n s to narrow, C-x n w to widen


Now that life’s getting busier and busier, with lots more things on my plate, ye olde system of writing the day’s tasks down on paper does not work any more. Specially since, I’ve begun logging clock times1. Writing them down by hand is a giant pain.
This is what the plan’s begun to look like.


a plan of tasks for a mediumish range of time

An unwieldy plan.
Click this image or any below, for larger versions


I wanted to see, if Org would let me replicate what I did my hand, natively.
Which in a nutshell was to look at the day sheet (on paper) for the nitty gritty and my organiser (currently my tasks.org file) for the big picture view.
I bounce between the two often. And discrete focus is something I wanted to keep.

The focus from wide …


wide shot of Himalayan range. a valley with a tree line with the mountains in the back and the wide blue sky overheard

to narrow. And back …

a yellow Moss Rose

A bit of searching and this is the result! Woohoo!

the plan is now scoped to just today

Like I mentioned above, I hit C-x n s next to my day’s headline to narrow the scope to just today. And a C-x n w gets me back to the thirty thousand foot view.
I think of them as, s for slim and w for wide, to keep them straight in my head.
I’m intuiting this is Org’s Sparse Tree Magic at play here. But learning that wizardry is a thing for another day.

The important bit for me, is that I can focus when I working, without getting distracted.
And then when I need to, zoom out to review what progress looks like and make changes, if I need to.

I feel a bit sad though. I’ve given up book notes on paper. I’m giving up planning my day on paper. It feels like I’m losing something tangible. I miss paper.


Feedback on this post? Mail me at feedback at this domain

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.


  1. I hope to use these, to objectively measure where my time is really going ↩︎

-1:-- Org Mode Narrowing and Widening Buffers (Post)--L0--C0--February 26, 2024 04:10 AM

Jeremy Friesen: Emacs Function to Assign Org-Mode Property to Matching Criteria

, I wrote my Update on the Campaign Status Document. I had manually set the alignment of several Non-Player Characters (NPCs 📖) ; but I thought “Maybe I should instead clear those out and randomize?”

And I started thinking about how I might automatically update (or add) new properties.

What I wanted was to select a top-level headline, select a property name, and pick a random table. Sidenote As per my random-tables Emacs package 📖 . With those chosen, I then populate all immediate sub-headings with the property and a randomized roll on the table. Sidenote A “table” can be a dice expression.

Below is that implementation.

(defun jf/campaign/populate-property-draw-value (&optional headline property table)
  "Populate HEADLINE's direct children's empty PROPERTY with roll on given TABLE.

When given no HEADLINE, prompt for ones from the document.

When given no PROPERTY, prompt for a property.

When given no TABLE, prompt for an expression to evaluate via
`random-table/roll'."
  (interactive)
  (let* ((level 1)
          (headline (or headline
                      (completing-read "Headline: "
                        (jf/org-get-headlines level) nil t)))
          (property (or property
                      (org-read-property-name)))
          (table (or table
                   (completing-read "Table: "
                     random-table/storage/tables)))
          (random-table/reporter
            ;; Bind a function that will only output the results of the
            ;; table, excluding the expression that generated the
            ;; results.
            (lambda (expression results) (format "%s" results))))
    (unless (org--valid-property-p property)
      (user-error "Invalid property name: \"%s\"" property))
    (save-excursion
      ;; Now that we have the information, let’s start rolling on some
      ;; tables.
      (dolist
        ;; Because we will not be recalculating heading positions, we
        ;; need to reverse the list, as updates to the last element will
        ;; not alter the position of the next to last element.
        (subheading (reverse
                      (org-element-map
                        (org-element-parse-buffer)
                        'headline
                        (lambda (el)
                          (and
                            (= ;; We want the children of the given
			       ;; level
                             (+ 1 level)
			     (org-element-property :level el))
                            (string=
                              ;; Match the text
                              (org-element-property
                                :raw-value
                                (car (org-element-lineage el)))
                              headline)
                            ;; Don't clobber already set values
                            (not (org-element-property property el))
                            ;; We found a match now return it
                            el)))))
        ;; We have finally found the element, now roll on the table and
        ;; populate.
        (org-entry-put
          (goto-char (org-element-property :begin subheading))
          property (random-table/roll table))))))

Conclusion

On the surface, Org-Mode 📖 syntax appears to be a variant of Markdown. But the true beauty shines when you consider the additional structure and functionality. The Org Element API provides useful guidance. But even more valuable is being able to see the source code.

I was able to look at the org-set-property function to find a few bits of useful logic that would help me avoid repetition. And from there I have a reasonably robust function.

One refinement obvious refinement is that the table parameter could instead be a function, which is then called for each element.

update

The previous code worked for a small test case, but once I test against a more complicated situation, it failed.

I intuited there would be a problem, as the previous comments indicated; but in my haste I didn’t check and verify.

I have updated the code and references to reflect those changes.

-1:-- Emacs Function to Assign Org-Mode Property to Matching Criteria (Post Jeremy Friesen (jeremy@takeonrules.com))--L0--C0--February 24, 2024 10:15 PM

Jeremy Friesen: Quick and Dirty Function to Sort My Feed

I use the elfeed 📖 for reading RSS 📖 feeds. And for managing the feeds, I use elfeed-org 📖 package to organize and manage those feeds.

Recently, I started publishing my blogroll this is done by exporting an Outline Processor Markup Language (OPML 📖) document from elfeed .

I was a little bothered that I didn’t export those items in what I consider a well-sorted order.

So, I set about exploring how to fix that. I’ve been using the org-sort function for other organizational tasks. Sidenote See Structure Editing (The Org Manual) for documentation on org-sort.

When calling org-sort, I can provide a sorting function.

And this is what I have hacked together.

First, here’s a utility function to pull out the domain and top-level domain. The function will convert https://www.somewhere.com/blogroll to somewhere.com.

(defun jf/utility/maybe-url-domain-from-string (string)
  "Return domain from STRING when URL scheme present.

Else fallback to provided STRING"
  (require 's)
  (if (s-contains? "://" string)
      (s-join "."
              (cl-subseq
               (s-split "\\." (nth 2 (s-split "/" string))) -2))
    string))

Next was to figure out how to invoke this function. With a bit of searching, I stumbled upon Org mode how to sort entries - Emacs Stack Exchange.

With a bit of experimenting, I settled on the following function.

  (org-sort-entries
   nil
   ?f
   (lambda ()
     (let ((heading (nth 4 (org-heading-components))))
       (jf/utility/maybe-url-domain-from-string heading))))

Given that the function was relative to the page, I chose to add an elisp type link to the elfeed-org document, just below the heading that contains the sub-headings and all the feeds. Sidenote See External Links (The Org Manual) for documentation about the elisp format.

It looks like this:

[[elisp:(org-sort-entries nil ?f (lambda () (let ((heading (nth 4 (org-heading-components)))) (jf/utility/maybe-url-domain-from-string heading))))][Sort Headings!]]

Now my feed document has a link called Sort Headings!; and when I click it I’m prompted to confirm running the function, which then updates the sort order according to my whim.

-1:-- Quick and Dirty Function to Sort My Feed (Post Jeremy Friesen (jeremy@takeonrules.com))--L0--C0--February 24, 2024 06:07 PM

Irreal: The init-dir Option

Bozhidar Batsov over at Emacs Redux has a short post on the new(ish) Emacs command line option, init-dir. It does just what it sounds like: it allows you to specify where to find the init.el file and other configuration information. I remember seeing it in the release notes for Emacs 29 but didn’t think a whole lot about it.

Batsov, of course, zeroes in on an important use case: enabling you to try (or use) different configurations while keeping your main init.el intact. He uses the example of having one init file for the Prelude distribution and another for Spacemacs.

Using the option for trying different distributions makes a lot of sense because the init files and required packages differ substantially. Even those of us who happily use vanilla Emacs will find it useful for trying out experimental configurations or for refactoring our configurations.

As Bastov remarks, you can do the same thing with symlinks and, indeed, that’s just what he and I used to do but using init-dir makes things simpler and easier and is another way of sanding down your workflow. Where before I may have hesitated to go through all the steps to set up an alternative configuration, now it’s simply a matter of copying my init.el, making the necessary edits, plopping it in some directory, and pointing Emacs at it with init-dir.

It’s nice to see Batsov blogging more again. I almost always learn something new when he posts.

-1:-- The init-dir Option (Post jcs)--L0--C0--February 24, 2024 04:50 PM

Jeremy Friesen: Update on the Campaign Status Document

We’ve completed six sessions of In the Shadows of Mont Brun . We’re using Errant 📖 for our campaign in the Savoy region of Europe.

In Test Driving a Campaign Status Document I wrote about my campaign note-taking. Since then, I’ve significantly refined the structure of the document.

I’ve settled on the following top-level headings:

  • Player Characters
  • Non-Player Characters
  • Sessions
  • Factions
  • Locations
  • Bestiary
  • Procedures
  • Rumours
  • Questions

Each heading then has topical sub-headings. Sidenote Nothing unique or overly interesting. And those sub-headings have further notes.

Benefits of the Lowly Outline

This document serves a multi-modal purpose; I use it when preparing my session, while running the session, and then later synthesizing the session.

And because of the outline form being written in Org-Mode 📖 , I have several views into the document. Headings support status custom states, like TODO or WAITING. I can also tag those sections and filter on the tags. Again, basic Org-Mode functionality.

Outline View

With a quick key stroke, I can open a side window to see the top two heading levels of the status document.

I use this functionality while I’m running a session. Taking a quick peek at names, as a reminder of who I might reintroduce.

I can also use the outline for navigation. Sidenote Though it is not the only means as I’ll soon explain.

Image

A screenshot of the base line status document.

Technical Implementation

My default outline depth in Org-Mode is 4. Sidenote (setq org-imenu-depth 4) But for the Status Document, I’ve set the outline depth to 2. Sidenote See 49.2.4 Local Variables in Files of the Emacs 📖 documentation.

I’ve bound the command imenu-list-smart-toggle to Cmd + 4. Sidenote s-4 in Emacs syntax. And I’ve added the document local variable org-imenu-depth: 2.

Heading Navigation

The heading navigation functionality relates to the Outline View functionality, but instead of seeing the full outline, I have a completion prompt.

I start typing and completion mechanism narrows those candidates. I can type Return to jump to that candidate.

I find that while I’m running the game and referencing the document, this quick movement can help me lookup something.

Image

A screenshot of the outline menu.

Technical Implementation

This navigation leverages the org-imenu-depth to limit the candidates to the top two heading levels.

I’ve bound the consult-org-heading function to Option + g then o. Sidenote M-g o in Emacs syntax. Mentally mapping to the mnemonic of “Go to Outline.” In non Org-Mode documents, I bind consult-outline the same key combination.

From Consult’s documentation:

consult-org-heading: Variant of consult-imenu or consult-outline for Org buffers. The headline and its ancestors headlines are separated by slashes. Supports narrowing by heading level, priority and TODO keyword, as well as live preview and recursive editing.

I like the ability to see the greater context of a particular heading, which allows for different means of filtering.

I consider the Consult package to be a high-utility enhancement of Emacs default completing read functionality. Mixin the Orderless package, and I have a wonderful fuzzy narrowing capabilities.

The Lowly “Session Notes”

When I’m preparing for the next session, I add a sub-heading under “Sessions”. There I write my notes, create links to resources.

And while I’m running the session, it is here where I write down all of those notes.

While running I might use my random-tables Emacs package 📖 to generate a random encounter, a dice roll, a weather event, or much of anything. And all of those rolls are inserted into the document. In other words, my notes are also a custom application.

Image

Random table prompt with partial completion using Vertico and Orderless.

List of random tables I’ve transcribed into Emacs
  • Ask the Stars
  • Black Sword Hack > Attributes
  • Black Sword Hack > Demon Name
  • Black Sword Hack > How to find the demon you seek
  • Black Sword Hack > Nickname
  • Black Sword Hack > Oracle Event
  • Black Sword Hack > Oracle Question
  • Black Sword Hack > Travel Event
  • Character Attitudes
  • Corporation, Sci-Fi
  • Deity
  • Descriptive Words to Pair with Concrete Element
  • Errant > Archetype > Equipment
  • Errant > Character
  • Errant > Chase Developments
  • Errant > Conspicuous Consumption Failed Save
  • Errant > Death & Dying
  • Errant > Death & Dying > Burning
  • Errant > Death & Dying > Physical
  • Errant > Death & Dying > Shocking
  • Errant > Death & Dying > Toxic
  • Errant > Debt Holder
  • Errant > Downtime Event
  • Errant > Failed Professions
  • Errant > General Downtime Turn Action
  • Errant > General Event
  • Errant > Grimoire
  • Errant > Henchman
  • Errant > Keepsakes
  • Errant > Lock
  • Errant > Reaction Roll
  • Errant > Weather
  • Escape the Dungeon
  • Everything Is Not as Expected
  • HRE > Contact
  • HRE > Random Encounter > Urban
  • HRE > Random Encounter > Wetlands or Coastal
  • HRE > Random Encounter > Wilderness
  • Herbalist’s Primer > Plant
  • Heresy
  • How Far Away is a Thing > Distant Thing
  • How Far Away is a Thing > Same Place
  • In the Shadows of Mont Brun > In the stagecoach
  • In the Shadows of Mont Brun > Names
  • Inn Name
  • Intervention Type
  • Ironsworn > Action Oracle
  • Ironsworn > Theme Oracle
  • Knave > Divine Domain
  • Knave > Divine Symbol
  • Knave > Potion Effect
  • Knave > Wilderness Region
  • Knave > Wilderness Structure
  • Knave > Wilderness Theme
  • Knave > Wilderness Trait
  • Laws of the Land
  • Location Adjectives of Connection
  • Location Adjectives of Disconnection
  • Lore24 Insipiration
  • Mortal Site
  • Mythical Castle Region Encounter
  • Mythical Castle Region Fantastical Encounter
  • Name
  • Noble House
  • Noble House > Atriarch Age
  • OSE > Attributes > 3d6 Down the Line
  • OSE > Monster Motivation
  • OSE > Random Dungeon Content
  • OSE > Reaction Roll
  • OSE > Secondary Skill
  • Oblique Strategy
  • Oracle
  • Person
  • Plot Twist
  • Random NPC Quirks
  • Savoy Region > Random Encounter
  • Scarlet Heroes > NPC
  • Scarlet Heroes > Reaction Roll
  • The Anarchical Grimoire of Propylonic Discharges
  • The One Ring > Lore
  • The One Ring > Lore > Action
  • The One Ring > Lore > Aspect
  • The One Ring > Lore > Focus
  • What is the monster doing?”

Tabular Views

The above Outline View and Heading Navigation functions are things I’ve been using for quite some time. But the Tabular Views are something I stumbled upon recently and have since further explored.

With each top-level heading I declare a list of keywords that I want to present in a table. Then for each sub-heading I fill out those keywords.

For my Bestiary, the keywords are: Name, Threat (abbreviated as T) Hit Point (HP 📖) , Attacks, Movement Dice (MD 📖) , Morale Level (ML 📖) , and Alignment (abbreviated to AL).

I can then toggle to see the tabular view of those keywords for the sub-heading.

Meaning, I have a quick overview of my the Bestiary stat block.

Image

Viewing Properties as Tabular Data

Technical Implementation

For the Bestiary, I’ve declared the following in the Property Drawer:

:COLUMNS: %ITEM(NAME) %THREAT(T) %HP %ATTACKS %MD %ML %ALIGNMENT(AL)

The %ITEM column populates the table column with the sub-heading’s text. The %ALIGNMENT(AL) specifies the ALIGNMENT property but uses AL as the column heading. Sidenote Instead of using the NAME property, I could use %ITEM; this would use the heading’s text.

And then for each sub-heading, I add properties for each of those named elements.

Here is one for a human soldier:

** Soldier
:PROPERTIES:
:ID:       EBC67225-8194-470D-ABC2-29745390F7C0
:HP: 7
:ATTACKS: 1d8 weapon
:ML: 7
:ALIGNMENT: L
:END:

Further, while in the column view, I can edit the properties.

For the Sessions section, I track the Experience Points (XP 📖) earned. I store that as a property on each session, and specify that I want to see a sum of those values. A nice and lazy XP tracker.

Dynamic Table Blocks

Building on the tabular view, I’ve added three dynamic tables via the columnview declaration. Sidenote See Capturing column view (The Org Manual) documentation.

  • Player Characters
  • Sessions
  • Bestiary

These tables reuse the declared columns and show them inline. They are also part of the export, which I’ll get to later.

I use these “always visible” tables to provide quick summaries to help answer in the moment questions:

  • Where were they on a given date?
  • What are the Player Characters (PCs 📖) names?
  • What’s the monster stack block?
Image

Dynamic table view with properties

Technical Implementation

This is fairly straight forward, I add the following just below the property drawer:

#+BEGIN: columnview :hlines 1 :id local :skip-empty-rows t
#+END:

Then I can call org-dblock-update to populate the table. I also set an save hook to auto-update all tables in the file. Sidenote Here’s a link to Github for section of code for conditional table recalculation.

Code for Conditional Recalculation
;;; From https://emacs.stackexchange.com/questions/22210/auto-update-org-tables-before-each-export
;; Recalculate all org tables in the buffer when saving.
(defvar-local jf/org-enable-buffer-wide-recalculation t
  "When non-nil, recalculate all dynamic regions when saving the file.

This variable is buffer local.")
;; Mark `jf/org-enable-buffer-wide-recalculation' as a safe local
;; variable as long as its value is t or nil. That way you are not
;; prompted to add that to `safe-local-variable-values' in custom.el.
(put 'jf/org-enable-buffer-wide-recalculation
     'safe-local-variable #'Boolean)

(defun jf/org-recalculate-buffer-tables (&rest args)
  "Wrapper function for `org-table-recalculate-buffer-tables' and
`org-dblock-update' that runs that function only if
`jf/org-enable-buffer-wide-recalculation' is non-nil.

Also, this function has optional ARGS that is needed for any
function that is added to
`org-export-before-processing-hook'. This would be useful if this
function is ever added to that hook."
  (when jf/org-enable-buffer-wide-recalculation
    (progn
      (org-table-recalculate-buffer-tables)
      (org-dblock-update '(4)))))

(defun jf/org-recalculate-before-save ()
  "Recalculate dynamic buffer regions before saving."
  (add-hook 'before-save-hook
            #'jf/org-recalculate-buffer-tables nil :local))
(add-hook 'org-mode-hook #'jf/org-recalculate-before-save)

Then at the bottom of my status document I added the following to enable the recalculate on save:

# Local Variables:
# jf/org--enable-buffer-wide-recalculation: t
# END:

For awhile, I had been declaring each sub-heading as radio targets. Sidenote See Radio Targets (The Org Manual) for more documentation and details. This would automatically convert all textual references of the heading into links to that heading.

To declare a radio target in Org-Mode , I would prepend the target declaration with \<\<\< and append \>\>\>. I could then refresh the buffer and all instances of that word (except the target) would become links to that target.

What I discovered, however, is that when sub-heading’s text is a radio target, it would produce an empty %ITEM cell for that sub-heading. Before I knew of the %ITEM column name, I had set a :NAME: property.

I did spend an hour trying to specify the org-radio-target-regexp variable, to customize what would be considered a radio target. However, I wasn’t able to make the Regular Expression (RegEx 📖) work.

Capture Templates

When I’m running a session, I often create Non-Player Characters (NPCs 📖) on the fly. I grab a name, a quirk, and a motivation. And from there run with it.

Given that I use the status document while I’m running a session, I wanted a way to randomly generate an NPC .

I wrote an Org-Mode capture template to perform this task. Sidenote See Capture (The Org Manual) for documentation. I’ve set it up, so that I type a few keys and presto I buffer that shows me this new NPC . I can, in the moment, take notes or refine the NPC .

When complete, I save the buffer and it is inserted into my status document as a sub-heading of the Non-Player Character heading.

As an added bonus, I don’t need to be editing my status document to create an NPC . When the mood strikes, I can start a capture by typing Control+c then c to bring up the capture menu.

Technical Implementation

First, let’s look to the capture template definition. Sidenote See my Org Mode configuration on Github.

("n" "NPC"
 entry (file+headline jf/campaign/file-name "Non-Player Characters")
 "* %(jf/campaign/random-npc-as-entry)\n%?"
 :empty-lines-after 1
 :jump-to-captured t)
  • n is the template key.
  • “NPC” is the label of the template.
  • The entry declaration means to write this as a heading in Org-Mode .
  • (file+headline jf/campaign/file-name "Non-Player Characters") means to file the new entry in the current campaign file-name and as a sub-head of the “Non-Player Characters” heading.
  • "* %(jf/campaign/random-npc-as-entry)\n%?" is the template, in this case a headline with most of the body created by my jf/campaign/random-npc-as-entry function. More on that in a bit.

The remaining keywords add an empty line afterwards and once I’m done “capturing” this information, Emacs will jump to that file and sub-heading.

The jf/campaign/random-npc-as-entry function.
(defun jf/campaign/random-npc-as-entry ()
  "Create an NPC entry."
  (let* ((random-table/reporter
          ;; Bind a function that will only output the results of the
          ;; table, excluding the expression that generated the results.
          (lambda (expression results) (format "%s" results)))
         (name
          (random-table/roll "In the Shadows of Mont Brun > Names"))
         (quirk
          (random-table/roll "Random NPC Quirks"))
         (alignment
          (random-table/roll "Noble House > Alignment"))
         (lore-table
          (random-table/roll "The One Ring > Lore"))
         (locations
          (s-join ", "
                  (completing-read-multiple
                   "Location(s): "
                   (jf/campaign/named-element :property "NAME"
                                              :tag "locations"))))
         (factions
          (s-join ", "
                  (completing-read-multiple
                   "Faction(s): "
                   (jf/campaign/named-element :property "NAME"
                                              :tag "factions")))))
    (format (concat
             "<<<%s>>>\n:PROPERTIES:\n:NAME:  %s\n:BACKGROUND:\n"
             ":LOCATIONS:  %s\n:DEMEANOR:\n:ALIGNMENT:  %s\n"
             ":QUIRKS:  %s\n:FACTIONS:  %s\n:END:\n\n%s"))
    name name locations alignment quirk factions lore-table))

Using my random-tables Emacs package I randomly create a name, quirks, alignment, and an oracle of action, aspect, and focus. I then use the campaign document to conditionally prompt for relevant locations and factions for the character.

There are more details but those are left as an exercise for the reader.

Exporting

As everything is in an Org-Mode document, I can export to multiple formats; in particular PDF 📖 via LaTeX 📖 .

The export recalculates the dynamic tables. It also exports, as definitions, the drawer properties. Meaning these bits of metadata appear as definition lists of term and value.

With Org-Mode , I can also tag headings as ones to skip for export. There are some secrets within my document that are not part of the export.

Here’s a link to my somewhat redacted session notes.

Technical Implementation

To achieve the layout, I use the following LaTeX headers and declarations. Sidenote See my Org-Mode include file for this LaTeX configuration. I then include the file in my document. Sidenote This LaTeX configuration will automatically start and stop the two-column functions.

LaTeX class and header declarations.
#+LATEX_CLASS: jf/article
#+LATEX_CLASS_OPTIONS: [11pt]
#+LATEX_HEADER: \usepackage[linktocpage=true]{hyperref}
#+LATEX_HEADER: \usepackage[french,english]{babel}
#+LATEX_HEADER: \usepackage[a4paper,top=3cm,bottom=3cm]{geometry}
#+LATEX_HEADER: \usepackage{minimalist}
#+LATEX_HEADER: \usepackage{fontspec}
#+LATEX_HEADER: \usepackage{caption} \captionsetup{labelfont=bf,font={sf,small}}
#+LATEX_HEADER: \setmainfont{TeX Gyre Pagella}
#+LATEX_HEADER: \usepackage{enumitem} \setlist{nosep}
#+LATEX_HEADER: \usepackage{longtable}
#+LATEX_HEADER: \usepackage{microtype}
#+LATEX_HEADER: \AtBeginEnvironment{longtable}{\footnotesize}
#+LATEX_HEADER: \usepackage[marginal,hang]{footmisc}
#+LATEX_HEADER: \usepackage{relsize,etoolbox}
#+LATEX_HEADER: \AtBeginEnvironment{quote}{\smaller}
#+LATEX_HEADER: \AtBeginEnvironment{tabular}{\smaller}
#+LATEX_HEADER: \usepackage[printonlyused,nohyperlinks]{acronym}
#+LATEX_HEADER: \usepackage[marginal,hang]{footmisc}
#+LATEX_HEADER: \usepackage{multicol}
#+LATEX_HEADER: \setlength\columnsep{20pt}
#+LATEX_HEADER: \hypersetup{colorlinks=true, linkcolor=blue, filecolor=magenta, urlcolor=cyan}
#+LATEX_HEADER: \tolerance=1000
#+LATEX_HEADER: \usepackage{float}
#+LATEX_HEADER: \usepackage{rotating}
#+LATEX_HEADER: \usepackage{sectsty}
#+LATEX_HEADER: \usepackage{titlesec}
#+LATEX_HEADER: \titleformat{\section}{\normalfont\fontsize{12}{18}\bfseries}{\thesection}{1em}{}
#+LATEX_HEADER: \setcounter{secnumdepth}{1}

# If this is a header; it will affect the table of contents.  But as a LATEX
# command, it happens in the processing order of the org file.
#+LATEX: \let\oldsection\section
#+LATEX: \renewcommand{\section}[1]{\newpage\end{multicols}\oldsection{#1}\begin{multicols}{2}}

For each of the dynamic tables, I end the multi-column layout before rendering the table and then resume the multi-column layout after the table; by adding the line #+LATEX: \end{multicols} before the table and adding #+LATEX: \begin{multicols}{2} after the table.

I had also been encountering issues where the generator would not render a table of contents. But based on some online guidance, I circumvented the problem by processing the .tex file twice.

Here’s the configuration for that:

(setq org-latex-pdf-process
        '("lualatex -shell-escape -interaction nonstopmode -output-directory %o %f"
          "lualatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

I have specified that I want to export the following drawer properties:

And when I export drawer properties, I want them to be exported as description lists. I also only want the text of the headline exported, not it’s tags nor status.

Code for refining export.
;; Convert the data ":PROPERTY: VALUE" into a latex item or markdown
;; definition term and detail.
(setq org-export-filter-node-property-functions
  '(jf/ox/transform-node-property-to-item))

(defun jf/ox/transform-node-property-to-item (data back-end channel)
  "Convert DATA to appropriate markup for given BACK-END.

CHANNEL is ignored."
  (let* ((field-value (s-split ":" data))
          (term (s-titleize (s-replace "_" " " (car field-value))))
          (value (s-trim (cadr field-value))))
    (if (s-blank? value)
      ""
      (cond
        ((eq back-end 'latex)
          (format "\\item[{%s:}] %s\n" term value))
        ((eq back-end 'md)
          (format "%s\n: %s\n" term value))
        (t data)))))

(defun jf/org-latex-property-drawer (_property-drawer contents _info)
  "Transcode a PROPERTY-DRAWER element from Org to LaTeX.
CONTENTS holds the contents of the drawer.  INFO is a plist
holding contextual information."
  (and (org-string-nw-p contents)
       (format
        "\\begin{description}\n%s\\end{description}\n\\vspace{5mm}"
        contents)))

(advice-add #'org-latex-property-drawer
            :override #'jf/org-latex-property-drawer)

(defun jf/org-latex-format-basic-headline-function
  (_todo _todo-type _priority text _tags _info)
  "Only render the TEXT of the headline.
See `org-latex-format-headline-function' for details."
  text)

Here’s a copy of my Status Document exported to a PDF.

Conclusion

The structure of Org-Mode paired with various Emacs functions creates a source of record that flexes with different uses for the same document. Now my prep, session notes, and “world-building” all exist within a single document.

-1:-- Update on the Campaign Status Document (Post Jeremy Friesen (jeremy@takeonrules.com))--L0--C0--February 24, 2024 01:43 AM

Arialdo Martini: Emacs: how to activate the functionality X for all files of type Y?

  • When a file of type Y is opened, Emacs sets a specific major mode.
  • Each major mode is equipped with a hook, a variable holding a list of functions.
  • After that major mode is activated, all the functions in its hook are run.
  • If you add a function to the mode’s hook it will be run for that kind of file.
  • You can use `add-hook` for that.

Therefore, just use:

(add-hook '<major-mode>-hook #'<function-you-wish-to-trigger>)

For example, to have line numbers in your Python files, use:

(add-hook 'python-mode-hook #'display-line-numbers-mode)

Table of Contents

Note

This post is based on the content of the lessons I took from Protesilaos Stavrou and it includes his comments.

A sample use case

There are 3 functionalities that I like to have enabled for specific kinds of files:

Minor mode Feature Where it makes sense to me
olivetti-mode Enhances the appearance of prose documents Markdown, Org
toggle-truncate-lines When it is on, long lines are not wrapped at the window’s edge All files
aggressive-indent-mode Format Lisp code in real time as you type Emacs Lisp files

Your mileage may be different.

In this post we will find out how to istruct Emacs to enable a specific feature when specific kinds of files are opened.

Solution

(add-hook 'org-mode-hook #'olivetti-mode)

(add-hook 'markdown-mode-hook #'olivetti-mode)

(defun turn-on-toggle-truncate-line ()
  "Turns truncating on, without printing messages"
  (let ((inhibit-message t))
    (toggle-truncate-lines 1)))
(add-hook 'log4j-mode-hooks #'turn-on-toggle-truncate-line)

(add-hook 'emacs-lisp-mode-hooks #'aggressive-indent-mode)

Major modes

When files are opened, Emacs enables a specific major mode.

A major mode is a function that configures Emacs to provide specific functionalities. It sets up keybindings, indentation rules, syntax highlighting and other features tailored to that specific file type. Typically, different kinds of files require different sets of features.

You can verify that a major mode is indeed a function by running:

M-x describe-function lisp-mode

Type s to inspect the source code, or jump to the Source Code section if you use the helpful package.

Major modes have a notion of inheritance. This means that when a major mode is activated, it is run together with all its the parent mode functions.

Some modes such as fundamental-mode and text-mode are top-level parents - and therefore the least specialized ones. fundamental-mode is defined with defun:

  (defun fundamental-mode ()
  "Major mode not specialized for anything in particular.
Other major modes are defined by comparison with this one."
  (interactive)
  (kill-all-local-variables)
  (run-mode-hooks))

Modes that inherit from other modes, such as emacs-lisp-mode, are defined with the define-derived-mode macro:

(define-derived-mode emacs-lisp-mode lisp-data-mode
  ...

text-mode, that is a top-level mode, is also declared with define-derived-mode, indicating nil as its parent;

(define-derived-mode text-mode nil "Text"
"Major mode for editing text written for humans to read.

Indeed, most of the top-level major modes are created with define-derived-mode by passing a nil PARENT argument. The fact that fundamental-mode is declared instead as a defun, is likely a legacy rather than the current convention.

The advantage of define-derived-mode over defun is that it automatically declares the corresponding hook, the key map, the abbrev table etc. Perhaps, then the macro define-derived-mode is a misnomer. It should be defmode or something.

It’s easy to follow the inheritance line up to the first parent with xref-find-definitions (M-.). For example, if you display the source code of emacs-lisp-mode with describe-function:

C-h f emacs-lisp-mode RET

and then you display its source code, you can see it derives from lisp-data-mode:

(define-derived-mode emacs-lisp-mode lisp-data-mode
...

Move the point over lisp-data-mode and hit M-. to jump to its definition:

(define-derived-mode lisp-data-mode prog-mode "Lisp-Data"
...

So, lisp-data-mode inherits from prog-mode. Do the same for prog-mode:

(define-derived-mode prog-mode fundamental-mode "Prog"
...

and finally for fundamental-mode:

(defun fundamental-mode ()
  "Major mode not specialized for anything in particular.
Other major modes are defined by comparison with this one."
  (interactive)
  (kill-all-local-variables)
  (run-mode-hooks))

We reached the top-most mode.
The inheritance line we just found out is:

    fundamental-mode 
          |
      prog-mode
          |
    lisp-data-mode
          |
    emacs-lisp-mode

The source code of fundamental-mode is very interesting: the last command is (run-mode-hooks), which leads us to the to the concept of hooks.

Hooks

Each major mode is equipped with a hook, a variable containing a list of functions.

All major mode hooks are parameterless, and they are called “normal”. There are also “abnormal” hooks (conventially named with a -functions suffix) such as enable-theme-functions and after-load-functions, which take parameters.

As you can foresee from the source code of fundamental-mode, after the major mode has been activated, the functions in the hook are triggered.

The same happens for modes inheriting from a parent major mode. We saw before that those modes are defined via the macro define-derived-mode. The source of define-derived-mode may not be straightforward to undestand, but it is easy to see that its last operation is to run the hook functions:

;; Run the hooks (and delayed-after-hook-functions), if any.
(run-mode-hooks ',hook)))))

By convention, each major mode defines a hook whose name is the mode name followed by -hook. For example, the hook for emacs-lisp-mode is emacs-lisp-mode-hook.

You can display the value of a hook with M-x describe-variable RET <hook name>.

For example, in my case prog-mode-hook contains:

prog-mode-hook

Note the the hook contains rainbow-mode: this makes sense, because we know that modes are just functions. This is in fact the idiomatic way to associate minor modes to a major mode.

Adding a function in a hook

So, let’s go back to my original goal of adding the beautiful aggressive-indent-mode to emacs-lisp-mode:

The current value of:

M-x describe-variable RET emacs-lisp-mode-hook RET

returns:

(ert--activate-font-lock-keywords erefactor-lazy-highlight-turn-on)

While it is possible to add aggressive-indent-mode with:

(setq emacs-lisp-modehook '(ert--activate-font-lock-keywords aggressive-indent-mode))

this is not very convenient, as it forces to copy the already existing values.

One could think of using:

(push #'aggressive-indent-mode emacs-lisp-mode-hook)

but this is also suboptimal, because push is not idempotent, so running the code above twice would insert aggressive-indent-mode multiple times.

The idiomatic way to customize a hook is via add-hook.

(add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)

add-hook is an amazingly complex function, but what matters here is that it makes sure that it adds a function to the hook variable ensuring that it is not added again if already present.

Of course, you can also add custom functions to a mode. For example:

(defun my-say-hello ()
  (interactive)
  (message "Enjoy your programming session!"))
    
(add-hook 'prog-mode-hook #'my-say-hello)

Open any .el file and verify that the greeting is displayed in the echo area.

Removing functions from hooks

As you could imagine, the inverse of add-hook is remove-hook:

(remove-hook 'prog-mode-hook #'my-say-hello)

More on modes

Some major modes are mostly used as parent text-mode prog-mode special-mode, they can also be used directly prog-mode by its own does nothing.

It should not come as a surprise that minor modes too have hooks. When a file is opened, its major mode is run, together with all its parent major modes. Minor modes too are run, but typically they don’t inherit from eachother.

This leads us to a question: what does happen if a function is defined in multiple hooks? Let’s find this out.

Conflicts

We know that emacs-lisp-mode inherits from prog-mode. So, let’s add my-say-hello to both their hooks:

(add-hook 'emacs-lisp-mode-hook #'my-say-hello)
(add-hook 'prog-mode-hook #'my-say-hello)

Let’s display the view echo ares messages buffer with:

M-x view-echo-area-messages RET

and finally let’s open a random .el file.

Oh, no! There is a disappointing:

Enjoy your programming session! [2 times]

This gets us to a general recommendation:

  • either make sure that the inheritance graph of modes avoids duplications.
  • or make sure that hooks invoke idempotent functions. Luckily, this is the default for the vast majority of minor modes.

In the case of aggressive-indent-mode, its author has been so diligent to make it idempotent. Indeed, its documentation (C-h f aggressive-indent-mode RET) states:

If called from Lisp, toggle the mode if ARG is toggle.  Enable
the mode if ARG is nil, omitted, or is a positive number.

You can easily verify that toggle-truncate-lines behaves differently. If you set it up twice:

(add-hook 'emacs-lisp-mode-hook #'toggle-truncate-lines)
(add-hook 'prog-mode-hook #'toggle-truncate-lines)

opening a file will result in having it off.

It’s better to have the following:

(remove-hook 'emacs-lisp-mode-hook #'toggle-truncate-lines)
(remove-hook 'prog-mode-hook #'toggle-truncate-lines)


(defun turn-on-toggle-truncate-line ()
  "Turns truncating on, without printing messages"
  (let ((inhibit-message t))
    (toggle-truncate-lines 1)))

(add-hook 'emacs-lisp-mode-hook #turn-on-toggle-truncate-line)
(add-hook 'prog-mode-hook #turn-on-toggle-truncate-line)

Of course, it is never a good idea to intentionally add the same function to multiple hooks in the same hierarchy. In this case, I prefer to have:

(add-hook 'fundamental-mode-hook #turn-on-toggle-truncate-line)

Anonymous functions

It is also possible to use anonymous functions:

(add-hook 'mark-down-mode-hook (lambda () (auto-fill-mode -1)))

Anonymous functions are fine, but you have to remove them as a whole if you want to modify them, whereas adding a symbol (a function’s name) gives you an indirection: you can change the function without updating the hook.

Associating major modes to file types

This leads us to a final question: what determines the major mode for a file or a functionality like magit or dired?

For files, it all revolves aroung the variable auto-mode-alist.

If you inspect its value (C-h v auto-mode-alist RET) you will find a very large value, containing items such as:

: (
...
(\.fs[iylx]?\' . fsharp-mode)
(\.hsc\' . haskell-mode)
(\.cabal\'\|/cabal\.project\|/\.cabal/config\' . haskell-cabal-mode)
(\.py[iw]?\' . python-mode)
(\.cs\' . csharp-mode)
(\.java\' . java-mode)
...
(\.org\' . org-mode)
(\.tgz\' . tar-mode))

So, it is an alist of file name patterns vs corresponding major mode functions. Visiting a file whose name matches the regular expression will run the corresponding mode function.

Modes are also run when a buffer is not visiting a file. That’s the case, for example, of magit and dired. You will usually find this as just a function call with the name of the major mode.

For those cases, mode functions are explicitly run from the code. This is for example a snippet from dired-internal-noselect:

(if mode (funcall mode)
  (dired-mode dir-or-list switches))

References

-1:-- Emacs: how to activate the functionality X for all files of type Y? (Post)--L0--C0--February 24, 2024 12:00 AM

Irreal: 🥩 Red Meat Friday: Is Emacs Dying?

Is Emacs dying? Lordy, I get so tired of that trope. Don’t like Emacs? I’m fine with that. Sorry you’re missing out, but fine. What I’m not fine with is people who don’t like Emacs and probably have never used it seriously declaring it a spent force. To be fair, redditisinmyheart doesn’t appear to fall into that category and seems to be asking an honest question rather than making an ill informed statement but most people declaring Emacs dead don’t know what they’re talking about.

Part of the confusion for some people is the conflation of two separate issues:

  • Has development and improvement of Emacs stopped or slowed?
  • Is the user base as large as it once was. Is it growing or shrinking?

The answer to the first question is an unequivocal “No”. Org Mode, Magit, Native Compilation, Treesitter, LSP, and tons of lesser improvements are happening all the time. Lately, the Emacs developers are making major releases about once a year and smaller releases regularly. Meanwhile, all those “vibrant editors” are still trying to clone Org and Magit

I don’t have figures at hand but it’s my impression that Emacs development and the introduction of major new features has accelerated lately. Regardless, it’s clearly not the case that development has slowed or stalled.

The answer to the second question is more difficult and part of what makes this a Red Meat Friday post. Again, I don’t have figures but it’s my impression that Emacs usage as a percentage of the possible user base has shrunk. Partly, that’s because there are many more choices now, but also because the nature of the user base has changed.

It used to be—before computers were cool—that being a programmer was a calling and those who answered the call were willing and even eager to expend major effort in honing their craft. That included spending the time to master “difficult” editors like Emacs and Vi. There are still plenty of people like that, of course, but there are also a lot of people to whom it’s only a job to be gotten through with the least effort possible. Of course users in the latter group aren’t going to spend the effort to master Emacs when they can simply use VS Code instead. No steep learning curves; Just buttons to push and a lot of bling to make it look really cool.

Years before Red Meat Friday became an occasional offering here, I wrote a post that could have qualified. It posited that serious programmers use Emacs, Vim, or (on the Mac) TextMate. It got a bit of pushback but I think its point remains valid and probably speaks to why the percentage of Emacs users has shrunk.

In any event, I’m not the least worried about the imminent demise of Emacs.

-1:-- 🥩 Red Meat Friday: Is Emacs Dying? (Post jcs)--L0--C0--February 23, 2024 05:03 PM

Michal Sapka: Emacs: watching YouTube with Yeetube and mpv

I may hate YouTube as a service, but I even I can’t deny the quality of vlogs there. Even though I would strongly prefer to follow folks on PeerTube or Odysee, this will not happen anytime soon for most of the channels. Luckily, with the popularity of YouTube comes quite a few ways to use it in a better way Yeetube Yeetube1 is an Emacs wrapper around searching and viewing videos on Youtube.
-1:-- Emacs: watching YouTube with Yeetube and mpv (Post)--L0--C0--February 23, 2024 03:16 PM

Emacs Redux: Changing The Emacs Configuration Directory

I’ve noticed recently that I’ve missed one small, but very handy addition to Emacs 29 - the --init-dir command-line options. According the release notes:

Emacs now supports setting ‘user-emacs-directory’ via ‘–init-directory’. Use the ‘–init-directory’ command-line option to set ‘user-emacs-directory’.

Basically, this allows you to instruct Emacs where to read its configuration from. By default that’s .emacs.d, but with this option you can easily override the default. That’s extremely handy when you want to try out different Emacs setups. Here’s some example:

# Use Emacs Prelude
$ emacs --init-dir ~/emacs-distros/prelude

# Use Spacemacs
$ emacs --init-dir ~/emacs-distros/spacemacs

I’m guessing this command-line options will be enough for most people who casually switch between different configurations and will reduce the need for them to rely on more sophisticated tools like chemacs or with-emacs.

Admittedly, I never used any such tools and I’d just symlink different dirs to .emacs.d, so --init-dir will definitely improve my workflow a bit.

That’s all I have for you today! Keep hacking!

-1:-- Changing The Emacs Configuration Directory (Post Bozhidar Batsov)--L0--C0--February 23, 2024 02:36 PM

Meta Redux: Weird Ruby: Fun with String#split

String#split is a pretty-well known and commonly used method. Still, its behaviour in some cases might surprise you:

irb(main):001:0> 'foo.bar'.split('.')
=> ["foo", "bar"]
irb(main):002:0> '...'.split('.')
=> []
irb(main):003:0> 'foo...'.split('.')
=> ["foo"]
irb(main):004:0> 'foo'.split('.')
=> ["foo"]
irb(main):005:0> ''.split('.')
=> []

No comment needed. Keep Ruby weird!

-1:-- Weird Ruby: Fun with String#split (Post Bozhidar Batsov)--L0--C0--February 23, 2024 01:05 PM

Meta Redux: Weird Ruby: Nil Conversions

Most Rubyists probably know that nil in Ruby is an instance of the singleton class NilClass. Because nil is a object like any other, we can actually invoke methods on it, and it turns out that NilClass provides a few - mostly conversion methods to other Ruby types. Let’s see some of those in action:

irb(main):001:0> nil.to_h
=> {}
irb(main):002:0> nil.to_a
=> []
irb(main):003:0> nil.to_s
=> ""
irb(main):004:0> nil.to_f
=> 0.0
irb(main):005:0> nil.to_i
=> 0
irb(main):006:0> nil.to_c
=> (0+0i)

With those methods you can literally create something (e.g. an empty hash) out of nothing! Whether you should actually be doing this is a totally different matter, though. According to the class docs:

The converter methods are carrying the concept of nullity to other classes.

I’m not sure how this “concept of nullity” is defined, but I’ve always found it weird that you can equate the absence of value with some anchor value (e.g. an empty collection or 0). I know there are contexts in which this may be useful, but I’m fairly certain you’d be able to get similar results without resorting to such conversions.

Keep in mind that you can do similar conversions in a different way:

irb(main):001:0> Array(nil)
=> []
irb(main):002:0> Hash(nil)
=> {}
irb(main):003:0> Integer(nil)
# `Integer': can't convert nil into Integer (TypeError)
irb(main):004:0> Float(nil)
# `Float': can't convert nil into Float (TypeError)

Notice how the “safe” number conversion methods Integer and Float operate differently and raise an exception when passed nil. That’s part of the reason why it’s often suggested to avoid the use of to_i and to_f to avoid converting unexpected values.

The Zen of Python famously says:

Explicit is better than implicit.

Errors should never pass silently.

In the face of ambiguity, refuse the temptation to guess.

But in the land of Ruby it seems we like to live dangerously!

That’s all I have for you today. Keep Ruby weird!

-1:-- Weird Ruby: Nil Conversions (Post Bozhidar Batsov)--L0--C0--February 23, 2024 08:15 AM

Troy Hinckley: Cycles all way down

A while ago while working on Rust-based Emacs, I was loading a new elisp file and hit a stack overflow. Digging deeper I found the issue in trying to print a cyclic list (where the tail of the list points back to previous element). I knew this was a possibility, and that at some point I would have to handle cycles. So I quickly implemented a version of Floyd’s cycle detection algorithm (visualized here).
-1:-- Cycles all way down (Post)--L0--C0--February 23, 2024 12:00 AM

Irreal: OSX Dictionary Interface

Here’s something that may be of interest to those of you falling in the intersection of Emacs users and Mac heads. It’s an Emacs interface to the macOS dictionary app. What it does is give you access to the macOS dictionary from within Emacs. There are two commands:

  1. osx-dictionary-search-word-at-point
  2. osx-dictionary-search-input

that look up the word at point or any word that you input. In both cases, the results are displayed in an Emacs buffer (rather than popping up a separate Mac window with the definition). Take a look at the screen shot at the link to see what it looks like.

This almost seems like a no-brainer but my dictionary needs are more than adequately met and I don’t feel the need to install this. As I’ve written many times, I have access to the famed 1913 Webster dictionary (my latest method for accessing it is here) and for quick lookups I use abo-abo’s excellent define-word. Still, if you’re using a Mac and don’t otherwise have an acceptable way of accessing a dictionary from within Emacs, this seems like an excellent solution.

Emacs really does offer us an embarrassment of riches. If you need a dictionary, there’s not just one or two solutions but many.

-1:-- OSX Dictionary Interface (Post jcs)--L0--C0--February 22, 2024 05:03 PM

Lars Ingebrigtsen: Can you spot the date I said “I’m done”?

Anyhoo… the reason I checked was because I was cleaning up my closet, which I’m told you have to do five times a century, whether it needs it or not.

I got about er 11? of these refuse sacs (a lot of yellowed white t-shirts, and why did I have 20 bed sets? dating back to the 80s, and some bleached-out black t-shirts, and some disgusting old pillows).

Look! Now I can squeeze in more stuff. But the reason I was reminded of the Emacs thing was that I happened upon these shirts:

I’d forgotten that I had some overflow No Gnus t-shirts — and as you can see, some of them are kinda dusty and/or sun bleached (there should be no sun in closets, but there is in mine, and these have been lying around for more than a decade). (Never worn! How dare you even ask!)

So, I’ve got four XL and four L shirts (in various colours)…

… and one Twenty Years of September t-shirt (I was supposed to do a Thirty Years last autumn, but I er didn’t; perhaps I’ll do them this spring).

So if you want one (primarily if you’re a Gnus user, I guess), send me an email (at larsi@gnus.org) with your snail-mail address, and I’ll do a lottery in about a week, and then send you a shirt if you win. (Free postage, too.) Remember to state your size, and whether you have any colour preference.

-1:-- Can you spot the date I said “I’m done”? (Post larsmagne23)--L0--C0--February 22, 2024 02:45 PM

Protesilaos Stavrou: Emacs: spontaneous live stream today (2024-02-22) at 14:30 Europe/Athens time

Raw link: https://www.youtube.com/watch?v=4Gg9PThHYZI

I will work on some of my packages for Emacs. The idea is to spend ~2 hours maintaining fontaine, spacious-padding, denote, ef-themes, and maybe others.

This being a live stream, I will accept any questions in the comments section and may thus talk about issues not related to the topic of maintaining the aforementioned packages.

The video will be recorded and can be watched later.

All my Emacs-related work: https://protesilaos.com/emacs.

-1:-- Emacs: spontaneous live stream today (2024-02-22) at 14:30 Europe/Athens time (Post)--L0--C0--February 22, 2024 12:00 AM

Irreal: Replacing Dash

It seems like only yesterday that Magnar Sveen introduced his Dash library for working with lists. It provided macros and functions for dealing with lists that weren’t covered by the standard Elisp library. Dash gained immediate traction and became a mainstay for Elisp developers. It’s one of those things that you use without even realizing it. So many packages install it that it’s present in almost every user’s Emacs so it’s easy to use its functions without realizing they’re from Dash.

Dash was so successful that its functions or their clones have made their way into the standard Elisp libraries. Now, Bozhidar Batsov writes that Dash has been essentially replaced by seq.el and subr-x.el and is no longer necessary.

No longer necessary except for the huge number of applications that use it. Batsov recently took over maintaining Flycheck and decided to eliminate the Dash dependencies. That turned out to be easy—almost trivial—to do. It was pretty much a one-for-one replacement of the function name.

It’s a tribute to Sveen’s vision and work that his library was so successful that the Emacs maintainers felt obligated to import its functionality into Emacs core. As a practical matter, Dash will live on if only because many of the applications that use it won’t have a Batsov to refactor them. It’s a nice library and I wouldn’t feel bad about using one of its functions today.

-1:-- Replacing Dash (Post jcs)--L0--C0--February 21, 2024 05:44 PM

Discovering Emacs podcast: Using Whitespace Mode in Emacs - EP4

-1:-- Using Whitespace Mode in Emacs - EP4 (Post Discovering Emacs)--L0--C0--February 21, 2024 03:43 PM

Meta Redux: nREPL 1.1.1: Improved Completion with compliment-lite

Today I’ve released nREPL 1.1.1 with a couple of small bug-fixes and one more notable, if mostly invisible change.

Historically nREPL’s completions op (introduced in nREPL 0.8) used internally a modified version of clojure-complete, that I eventually released as a library named incomplete. clojure-complete was pretty much abadonware at this point and I wanted a simple drop-in replacement that we could use in tools like REPLy (and by association - Leiningen).1

Using the much better compliment library wasn’t an option, as we needed something that was quite literally a single file. (so it’d be easy to inline in nREPL and load in REPLy) Recently, however, compliment’s author Oleksandr Yakushev released exactly what we needed - a single-file, stripped-down version of compliment called compliment-lite. Here’s how it differs from the full-fledged compliment:

  • Context. Completion features that require context (e.g., filtering methods by the class of the receiver object) don’t work.
  • Local bindings and resources. Those two sources of completions completely rely on context, so they are disabled in Compliment-lite.
  • Documentation. The documentation function is absent from Compliment-lite.

None of those were things supported by incomplete, so nothing lost. Naturally, it made sense for nREPL to adopt it and offer improved out-of-the-box completion experience to Clojure programmers. As this doesn’t affect the public API of nREPL2 I’ve opted to ship this change in a “bug-fix” release. A version like 1.1.1 is just too cool to pass up!

In light of the creation of compliment-lite I now consider incomplete to be obsolete and I’d encourage everyone looking for a simple, but capable code-completion library to go for compliment-lite instead.

That’s all I have for you today. Keep hacking!

-1:-- nREPL 1.1.1: Improved Completion with compliment-lite (Post Bozhidar Batsov)--L0--C0--February 20, 2024 09:18 AM

Sacha Chua: 2024-02-19 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, lobste.rs, kbin, programming.dev, lemmy, communick.news, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View org source for this post
-1:-- 2024-02-19 Emacs news (Post Sacha Chua)--L0--C0--February 19, 2024 01:41 PM

Meta Redux: Need for Speed: Using RuboCop with Prism

By now probably most Rubists have heard of Prism (formerly know as YARP), the modern and (very) fast Ruby parser that’s aiming to replace all the existing Ruby parsers out there. Including RuboCop’s “own” parser (a.k.a whitequark/parser), which aimed to replace ripper and ruby_parser, back when it was created.

I’ve been keeping an eye on Prism for while, as RuboCop has long been criticized for its choice of parser (and more specifically - for that parser being somewhat slow). That being said - parser migrations are always a pain, especially in a project as big as RuboCop. Early on I had to essentially rewrite RuboCop when I switched the codebase from ripper to parser, and back then RuboCop was a lot smaller. The good thing is that such rewrites can be done incrementally (I was migrating cops in batches), but it was still a lot of (boring, repetitive) work.

That’s why I was super happy when I recently discovered parser-prism - a gem that provides a new backend for the parser gem’s syntax tree that uses the Prism parser. To make things better - it seemed that this library actually worked with RuboCop already. Yeah, the users still had to do some manual work, but it turned out that the migration could be a lot simpler than what I had expected. Did I also mention some mighty impressive benchmarks?


As a whole, this parser should be significantly faster than the parser gem. The bin/bench script in this repository compares the performance of Parser::CurrentRuby and Parser::Prism. Running against a large file like lib/parser/prism/compiler.rb yields:

Warming up --------------------------------------
 Parser::CurrentRuby     1.000  i/100ms
       Parser::Prism     6.000  i/100ms
Calculating -------------------------------------
 Parser::CurrentRuby     16.642  (± 0.0%) i/s -     84.000  in   5.052021s
       Parser::Prism     64.951  (± 3.1%) i/s -    330.000  in   5.088147s

Comparison:
       Parser::Prism:       65.0 i/s
 Parser::CurrentRuby:       16.6 i/s - 3.90x  slower

When running with --yjit, the comparison is even more stark:

Warming up --------------------------------------
 Parser::CurrentRuby     1.000  i/100ms
       Parser::Prism     9.000  i/100ms
Calculating -------------------------------------
 Parser::CurrentRuby     20.062  (± 0.0%) i/s -    101.000  in   5.034389s
       Parser::Prism    112.823  (± 9.7%) i/s -    558.000  in   5.009460s

Comparison:
       Parser::Prism:      112.8 i/s
 Parser::CurrentRuby:       20.1 i/s - 5.62x  slower

These benchmarks were run on a single laptop without a lot of control for other processes, so take them with a grain of salt.


Note: The results above were taken straight from parser-prism’s README. In a nutshell we’re looking into something like 4-6 times speedup!!! Now I was VERY excited!

Immediately I created an issue to improve the support for Prism in RuboCop and we’ve started to collaborate with Prism’s author Kevin Newton, who has been very supportive and accommodating. I suggest to everyone interested in the topic to peruse the discussion in this issue (and issues references from it), as we’ve already managed to simplify the usage of RuboCop with Prism quite a bit. And we’re very close to addressing the main outstanding item - multi-versioning for the parser. Basically, the parser gem allows you to select the version of Ruby to parse code as (e.g. 3.1), independently from the version of Ruby runtime. This powers the TargetRubyVersion configuration option in RuboCop and was one of the many reasons for picking parser over ripper back in the day. In practical terms - this allowed us to keep supporting parsing Ruby 2.x code, long after one couldn’t run RuboCop on Ruby 2.x. To put it in different terms - the versions of Ruby that RuboCop understands (can parse) are completely orthogonal to the versions of Ruby on which RuboCop can be run.

This and other items we need to address, are nicely summarized here. None of them seems like a particularly hard obstacle, so I’m quite optimistic about the future. A future in which one day you’ll be able to have this in your RuboCop config:

ParserEngine: prism

When is this going to happen exactly? No promises yet, but at the current pace it will likely happen sooner rather than later. I’d encourage adventurous people to play with RuboCop on Prism and to contribute to getting all required pieces in place faster.

I recall that RuboCop’s adoption of whitequar/parser was instrumental in uncovering (many) weird bugs in it, and I have a feeling that things might be the same for parser-prism as well. (I assume a lot less projects use parser-prism compared to Prism) Exciting times ahead!

That’s all I have for you today! Keep hacking!

-1:-- Need for Speed: Using RuboCop with Prism (Post Bozhidar Batsov)--L0--C0--February 19, 2024 01:12 PM

Meta Redux: Configuring fixed/tonsky indentation in clojure-mode

A few years ago Nikita Tonsky made popular a certain style of formating Clojure code, that became known as “fixed” or “tonsky” indentation in the community.

clojure-mode has long had some support for this via clojure-indent-style:

(setq clojure-indent-style 'always-indent)

However, it was kind of hard to get exactly the same indentation from Nikita’s article, as there was no way to suppress the usage of indent specs and forms starting with a keyword were indented by separate rules from what was set by clojure-indent-style. A recent upstream change made the indentation configuration more granular and now you can get fixed indentation with the following snippet:

(setq clojure-indent-style 'always-indent
      clojure-indent-keyword-style 'always-indent
      clojure-enable-indent-specs nil)

clojure-indent-keyword-style and clojure-enable-indent-specs are the new additions, that made this possible. Here’s how clojure-indent-keyword-style can be used:

  • always-align (default) - All args are vertically aligned with the first arg in case (A), and vertically aligned with the function name in case (B).

     (:require [foo.bar]
               [bar.baz])
     (:require
      [foo.bar]
      [bar.baz])
    
  • always-indent - All args are indented like a macro body.

      (:require [foo.bar]
         [bar.baz])
      (:x
         location
         0)
    
  • align-arguments - Case (A) is indented like always-align, and case (B) is indented like a macro body.

      (:require [foo.bar]
                [bar.baz])
      (:x
         location
         0)
    

By the way, clojure-ts-mode also supports the fixed indentation style:

(setq clojure-ts-indent-style 'fixed)

The configuration here is a lot simpler, as this functionality existed since day 1.

For the record, I still don’t endorse/like fixed indentation1 (and funny enough - neither does Arne, who implemented those changes). Still, I acknowledge that this style is somewhat popular in the Clojure community and I’m all for making it easier for people who like/need it to be able to use it.

That’s all I have for you today. Keep hacking!

-1:-- Configuring fixed/tonsky indentation in clojure-mode (Post Bozhidar Batsov)--L0--C0--February 19, 2024 09:45 AM

Meta Redux: Flycheck Updates: A new Stable Release and Way More

I’ve been pretty busy with Flycheck ever since I became its maintainer recently. Today I wanted to share with you a few of the recent highlights.

Flycheck 34

I’ve cut Flycheck 34, which features quite a few new checkers and removes a bunch of legacy checkers. As I’m not familiar with all the lint tools that Flycheck supports1, I’d appreciate your input on whether some checkers need to be updated/replaced/removed down the road.

Random trivia: I noticed that Chef’s foodcritic has been replaced by cookstyle, a project built on top of my very own RuboCop project (a linter & formatter for Ruby) and I wrote the checker for it myself.

flycheck.org Ownership

I’ve managed to obtain the ownership of the flycheck.org domain. Big thanks to Matthias Güdemann for paying for the domain after the departure of Sebastian from Flycheck.

flycheck-eglot

I quickly realized that Eglot support was one of the main reasons why people switched from Flycheck to Flymake, so when I noticed that Sergey Firsov had created Flycheck backend for Eglot I immediately invited him to move his project to the official Flycheck organization on GitHub. You can find flycheck-eglot here.

Documentation Updates

I’ve been updating the Flycheck docs here and there. Most notably, I’ve updated the comparison with Flymake, as a few people complained it was out-of-date.

I haven’t used Flymake in ages, so I’m definitely not an expert there, but I did my best to make sure the comparsion is reasonably accurate. If someone notices any issues there - please, submit improvements!

Open Collective

I’ve created an Open Collective for Flycheck, so people and organizations can support the project financially via donations. I hope that in the long-run it can make the maintenance of the project more sustainable. In the short run - I’ll use some of the donations to cover my expenses for flycheck.org.

NonGNU ELPA

I’ve filed a request to submit Flycheck to NonGNU ELPA, one of Emacs’s official package repositories. Let’s see how this will go. I’m hoping to have there Flycheck and many of its extensions in the long run.

Update: You can track the emacs-devel discussion on the topic here.

Help Welcome

If you want to hack on Flycheck or any of its extensions - now would be a great time! I love working with other contributors and Flycheck could definitely use all the help it could get. The main organization features some 20 projects and not all of them are actively maintained today.

Epilogue

And that’s a wrap! It has been a busy few weeks working on Flycheck, but now I’ve done pretty much everything that I wanted to tackle originally. Moving forward I’ll be working slowly on the backlog of issues and pull requests and pondering whether more ambitious changes will be needed down the road.

Thanks to everyone for their support for the project! Keep hacking!

  1. See https://www.flycheck.org/en/latest/languages.html for the list of built-in checkers. 

-1:-- Flycheck Updates: A new Stable Release and Way More (Post Bozhidar Batsov)--L0--C0--February 19, 2024 08:30 AM

Meta Redux: CIDER: Preliminary Support for clojure-ts-mode

I’m glad to report that yesterday the long-awaited preliminary support for clojure-ts-mode in CIDER has landed!1

This pull request (or rather the original PR on which it was based) was in the works for a very long time and it feels good to see it finally merged. What does this mean in practice? Well, CIDER will now properly recognize clojure-ts-mode and modes derived from it, meaning most of the functionality in CIDER will work reasonably well with them. There are a few caveats to keep in mind, though:

  • CIDER still has a hard dependency on clojure-mode, as it relies on some APIs from it that have yet to be ported to clojure-ts-mode
  • Some functionality like dynamic indentation and dynamic font-locking (syntax highlighting) is still not support by clojure-ts-mode
  • You need to use the latest version of clojure-ts-mode, as it features some related changes
  • The new code hasn’t been test much, so probably we’ll encounter some bugs along the way
  • You’ll need to use a snapshot release of CIDER (e.g. one installed from MELPA), as there’s no stable CIDER release with this functionality yet (CIDER 1.14 will be the first one)
  • You need to be on Emacs 29 to be able to use clojure-ts-mode in the first place

At any rate - we’ve made one big step towards decoupling CIDER from clojure-mode and gradually we’ll get there. Thanks to everyone who was involved in making this happen! Keep hacking!

  1. I’d suggest checking out https://metaredux.com/posts/2023/03/12/clojure-mode-meets-tree-sitter.html if you’re not familiar with clojure-ts-mode

-1:-- CIDER: Preliminary Support for clojure-ts-mode (Post Bozhidar Batsov)--L0--C0--February 19, 2024 07:57 AM

Emacs Redux: Replacing dash.el with Built-in Emacs APIs

dash.el has long been a staple in the Emacs community and I give it a lot of credit for driving some upstream API progress. By showing how many people wanted to use dash.el for various reasons (e.g. macros like -if-let and -when-let and numerous functions that transform sequences), a strong case could be made with Emacs’s maintainers that core Emacs Lisp APIs needed to be extended. Eventually that lead to the creation of subr-x.el1 (bundled with Emacs since 24.3) and seq.el (bundled with Emacs since 26.1). The two packages mostly obsolete dash.el and I guess this contributed to it being used less and less these days.

I recently took over the maintenance of the popular Flycheck package and I’ve noticed that it was still using dash.el, despite targeting Emacs 26.1. As I plan to submit Flycheck to the official NonGNU ELPA package repository, it couldn’t have dependencies that are not present on GNU ELPA and NonGNU ELPA and I needed to remove dash.el. Doing so turned out to be trivial, as there were obvious built-in alternatives for everything Flycheck was using:

  • I replaced -if-let(*) and -when-let(*) with if-let(*) and when-let(*) from subr-x.el.
  • I replaced -any? and -first with seq-find, -all? with seq-every-p and -take with seq-take.

And that was it! The whole process literally took me 10 minutes, most of them spent checking whether seq-find is the right replacement for -any? and -first.

One small caveat to keep in mind, when depending on the built-in Emacs packages like seq.el, is that you should typically stick to whatever version of the package was bundled with the version of Emacs that you target (in my case Emacs 26.1 bundles seq.el 2.20), otherwise you’ll need to declare an explicit package dependency on the newer version. Keep in mind, however, this might cause some issues in packages that are depending on your package, but not declaring the same dependencies.2 Transitive dependencies in Emacs are a messy topic…

So, all it all it was a very quick and easy process and I can totally recommend to other packages that are still relying only on basic dash.el functionality to replace it as well. Now Flycheck is totally dependency-free and it’s ready for submission to NonGNU ELPA!

  1. Originally authored by yours truly. 

  2. See https://github.com/flycheck/flycheck/issues/2054

-1:-- Replacing dash.el with Built-in Emacs APIs (Post Bozhidar Batsov)--L0--C0--February 19, 2024 06:59 AM

Charles Choi: cc-isearch-menu now on MELPA

In my post “Improving Emacs isearch Usability with Transient”, I described a way to use a Transient menu to get at the richer features of isearch. I’m happy to announce that this menu is now available as the package named cc-isearch-menu on MELPA. More info at this link:

https://melpa.org/#/cc-isearch-menu

-1:-- cc-isearch-menu now on MELPA (Post Charles Choi)--L0--C0--February 19, 2024 12:34 AM

Andrea: A quick-fix for golden-ratio and lsp-mode

A quick-fix for golden-ratio and lsp-mode

I have been back into Scala development lately and I have been enjoying using lsp-metals.el.

Coding is a lot about trial and error and my compiler errors are inflating: they became long enough to make my windows jumpy (as you can see in the video below)!

/assets/blog/2024/02/19/a-quick-fix-for-golden-ratio-and-lsp-mode/goldenratioslp.mp4

I use golden-ratio to resize my windows automatically in a way pleasant to the eye. Unluckily this mode seems to have issues with the way lsp-mode logs errors.

I couldn't dig into this as much as I wanted (very short on time these days! Actually a year and a half so far of a really demanding joy). But! I hacked my config enough to make it work:

(use-package golden-ratio
  :diminish golden-ratio-mode
  :defer 1
  :config
  (golden-ratio-mode 1)
  ;; this fixes issue with lsp-mode minibuffer logs (which made my windows jump like crazy)
  (remove-hook 'window-configuration-change-hook 'golden-ratio)
  (add-to-list 'golden-ratio-extra-commands 'avy-goto-char-timer))

The idea is that the setting of window-configuration-change-hook is covering what lsp-mode does when it refreshes the minibuffer, so we disable that hook. That has side effects (for example I use avy-goto-char-timer a lot and without that hook the window is not resized). As you can see above, golden-ratio is configurable, so I added the command explicitly.

Hope that helps somebody!

Happy error-handling,

Andrea

-1:-- A quick-fix for golden-ratio and lsp-mode (Post)--L0--C0--February 19, 2024 12:00 AM

Irreal: Opening Dired On External Drives

Marcin Borkowski (mbork) has a nice post on opening a Dired buffer for an external drive. Plugging the drive into a USB port causes it to automount but then you have to call Dired on the path to the drive, which can be long and complicated. Mbork’s solution to this is to automate the process.

If there is more than one drive mounted, his function will give you a completing read list to choose the one you want the Dired buffer for. It’s a small thing but like many things we do with Emacs, it helps smooth our workflow.

His code is both short and obvious, and nicely demonstrates how simple it is to do this sort of thing. This is an example of how easy it is to make Emacs into a secret weapon that can automate away many routine but tedious tasks. As Mbork says, it took him about 10 minutes to write the code but since he’s always mounting external drives, it won’t be long before he’s amortized that effort.

-1:-- Opening Dired On External Drives (Post jcs)--L0--C0--February 18, 2024 05:53 PM

Emacs Redux: Lookup the Documentation of Functions, Variables and Faces

Looking up the documentation for some command/function or configuration option/variable is something all Emacs users have to do quite often. Two of the Emacs help commands that I use most often are describe-function (C-h f) and describe-variable (C-h v). Basically they display the documentation for some function or variable. E.g. if you press C-h f and type afterwards describe-function we’d get the following:

describe-function is an autoloaded interactive compiled Lisp function in
‘help-fns.el’.

It is bound to C-h f, <f1> f, <help> f, <menu-bar> <help-menu> <describe>
<describe-function>.

(describe-function FUNCTION)

Display the full documentation of FUNCTION (a symbol).
When called from Lisp, FUNCTION may also be a function object.

See the ‘help-enable-symbol-autoload’ variable for special
handling of autoloaded functions.

Probably introduced at or before Emacs version 22.1.

describe-variable is also useful to check the current value of some variable. Here’s an example:

clojure-indent-keyword-style is a variable defined in ‘clojure-mode.el’.

Its value is ‘always-align’

I’m guessing most Emacs users are quite familiar with both commands. What I didn’t know until recently, though, is that Emacs 25.1 introduced the related command describe-symbol (C-h o), which works for both functions and variables. I’m guessing most people would benefit from using it over the more specific commands, as it reduces some mental overhead. (the fewer keybindings we have to remember, the better)

Bonus points - describe-symbol also works with faces, so it’s a good replacement for describe-face (which doesn’t have a default keybinding). One command to rule them all!

That’s all I have for you today. Keep hacking!

-1:-- Lookup the Documentation of Functions, Variables and Faces (Post Bozhidar Batsov)--L0--C0--February 18, 2024 10:35 AM

Irreal: OPML and RSS

Knut Magnus Aasrud has an interesting post on OPML. If you haven’t heard of OPML before (I hadn’t) it stands for Outline Processor Markup Language and is a file format for outliner applications. It’s fairly general but has found particular use as a file format for exchanging subscription lists between feed readers and aggregators. It’s an unholy collection of nasty XML—although some think “nasty” applies to all XML—but it has the advantage of holding your subscription list and being exportable to HTML so that you can share it others on, say, your blog.

Of course, if you’re an Emacs user you have no need for OPML because you already have a builtin solution1 that does the same thing. I’m talking about using elfeed-org with elfeed as your feed reader. With elfeed-org you can list your feeds in a Org file and add descriptive text to each entry if you like. Naturally, this is exportable to HTML or most any other format you can think of so if you want to share it as a blogroll on your blog or someplace else, it’s easy. You can even export it to OPML so its capabilities are a strict superset of OPML’s.

It’s just another example of how Emacs has our backs. As I’ve said many times before, if you’re reading RSS/Atom feeds—and you should be—you can’t do better than elfeed. And if you want to see the actual articles instead of just the text, take a look at elfeed-webkit.

Footnotes:

1

Well, not really builtin but it’s a use-package away.

-1:-- OPML and RSS (Post jcs)--L0--C0--February 17, 2024 04:51 PM

Marcin Borkowski: Opening external drives in Dired

I use external drives pretty often – for backups, for moving files between machines, and for storing mp4 files, for example. I’ve been using UDisks for quite some time now. It automounts an external drive under the /run/media/$USER/VolumeName directory (where VolumeName is different for each drive, of course). I also use Dired as my main file manager. As most Emacsers know, it’s far from shiny, but it’s incredibly powerful, especially combined with some other Emacs features. One problem I have is that when I insert a drive into one of the USB ports, I’d like to be able to open it in Dired.
-1:-- Opening external drives in Dired (Post)--L0--C0--February 17, 2024 04:30 PM

Protesilaos Stavrou: Emacs: modern minibuffer packages (Vertico, Consult, etc.)

Raw link: https://www.youtube.com/watch?v=d3aaxOqwHhI

In this ~44 minute video I demonstrate packages that upgrade the experience with Emacs. They are about the minibuffer: where we interact with Emacs to call a command by its name or pick a candidate for some action.

The packages I cover are vertico, marginalia, consult, orderless, and embark. I also mention briefly the wgrep package as well as the built-in savehist-mode, recentf-mode.

A related video I did about search (search+replace): https://protesilaos.com/codelog/2023-06-10-emacs-search-replace-basics/.

Sample configuration

;; The `vertico' package applies a vertical layout to the minibuffer.
;; It also pops up the minibuffer eagerly so we can see the available
;; options without further interactions.  This package is very fast
;; and "just works", though it also is highly customisable in case we
;; need to modify its behaviour.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:cff33514-d3ac-4c16-a889-ea39d7346dc5
(use-package vertico
  :ensure t
  :config
  (setq vertico-cycle t)
  (setq vertico-resize nil)
  (vertico-mode 1))

;; The `marginalia' package provides helpful annotations next to
;; completion candidates in the minibuffer.  The information on
;; display depends on the type of content.  If it is about files, it
;; shows file permissions and the last modified date.  If it is a
;; buffer, it shows the buffer's size, major mode, and the like.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:bd3f7a1d-a53d-4d3e-860e-25c5b35d8e7e
(use-package marginalia
  :ensure t
  :config
  (marginalia-mode 1))

;; The `orderless' package lets the minibuffer use an out-of-order
;; pattern matching algorithm.  It matches space-separated words or
;; regular expressions in any order.  In its simplest form, something
;; like "ins pac" matches `package-menu-mark-install' as well as
;; `package-install'.  This is a powerful tool because we no longer
;; need to remember exactly how something is named.
;;
;; Note that Emacs has lots of "completion styles" (pattern matching
;; algorithms), but let us keep things simple.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:7cc77fd0-8f98-4fc0-80be-48a758fcb6e2
(use-package orderless
  :ensure t
  :config
  (setq completion-styles '(orderless basic)))

;; The `consult' package provides lots of commands that are enhanced
;; variants of basic, built-in functionality.  One of the headline
;; features of `consult' is its preview facility, where it shows in
;; another Emacs window the context of what is currently matched in
;; the minibuffer.  Here I define key bindings for some commands you
;; may find useful.  The mnemonic for their prefix is "alternative
;; search" (as opposed to the basic C-s or C-r keys).
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:22e97b4c-d88d-4deb-9ab3-f80631f9ff1d
(use-package consult
  :ensure t
  :bind (;; A recursive grep
         ("M-s M-g" . consult-grep)
         ;; Search for files names recursively
         ("M-s M-f" . consult-find)
         ;; Search through the outline (headings) of the file
         ("M-s M-o" . consult-outline)
         ;; Search the current buffer
         ("M-s M-l" . consult-line)
         ;; Switch to another buffer, or bookmarked file, or recently
         ;; opened file.
         ("M-s M-b" . consult-buffer)))

;; The `embark' package lets you target the thing or context at point
;; and select an action to perform on it.  Use the `embark-act'
;; command while over something to find relevant commands.
;;
;; When inside the minibuffer, `embark' can collect/export the
;; contents to a fully fledged Emacs buffer.  The `embark-collect'
;; command retains the original behaviour of the minibuffer, meaning
;; that if you navigate over the candidate at hit RET, it will do what
;; the minibuffer would have done.  In contrast, the `embark-export'
;; command reads the metadata to figure out what category this is and
;; places them in a buffer whose major mode is specialised for that
;; type of content.  For example, when we are completing against
;; files, the export will take us to a `dired-mode' buffer; when we
;; preview the results of a grep, the export will put us in a
;; `grep-mode' buffer.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:61863da4-8739-42ae-a30f-6e9d686e1995
(use-package embark
  :ensure t
  :bind (("C-." . embark-act)
         :map minibuffer-local-map
         ("C-c C-c" . embark-collect)
         ("C-c C-e" . embark-export)))

;; The `embark-consult' package is glue code to tie together `embark'
;; and `consult'.
(use-package embark-consult
  :ensure t)

;; The `wgrep' packages lets us edit the results of a grep search
;; while inside a `grep-mode' buffer.  All we need is to toggle the
;; editable mode, make the changes, and then type C-c C-c to confirm
;; or C-c C-k to abort.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:9a3581df-ab18-4266-815e-2edd7f7e4852
(use-package wgrep
  :ensure t
  :bind ( :map grep-mode-map
          ("e" . wgrep-change-to-wgrep-mode)
          ("C-x C-q" . wgrep-change-to-wgrep-mode)
          ("C-c C-c" . wgrep-finish-edit)))

;; The built-in `savehist-mode' saves minibuffer histories.  Vertico
;; can then use that information to put recently selected options at
;; the top.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:25765797-27a5-431e-8aa4-cc890a6a913a
(savehist-mode 1)

;; The built-in `recentf-mode' keeps track of recently visited files.
;; You can then access those through the `consult-buffer' interface or
;; with `recentf-open'/`recentf-open-files'.
;;
;; I do not use this facility, because the files I care about are
;; either in projects or are bookmarked.
(recentf-mode 1)

More links to my Emacs configuration

My personal Emacs configuration. It is comprehensive, both in terms of the [custom] code it contains as well as the documentation on what each piece of functionality does.

-1:-- Emacs: modern minibuffer packages (Vertico, Consult, etc.) (Post)--L0--C0--February 17, 2024 12:00 AM

Irreal: Cl-callf

Just a quickie today. I came across this reddit post about cl-callf. It was something I had never heard of. One of the Lisp idioms is to toggle a boolean with

(setq bool (not bool))

I use it all the time but when the variable is named AVeryLongAndStupidName instead of something reasonable like bool, it can be a pain because you have to repeat the name. Of course, no true Scotsman Lisper would use such a name but occasionally one comes across such monstrosities.

It turns out that Elisp has the macro cl-callf that lets you write the idiom as

(cl-callf not bool)

There’s also the related cl-callf2. You should check out the documentation for both because they’re a bit richer than the above suggests.

I’m not sure why Emacs thinks this is a Common Lisp thing because as far as I can tell there is no callf in Common Lisp. It’s probably due to the fact that the variable can be a place rather than a simple variable in which case cl-callf does the equivalent of a setf, a Common Lisp form. In any event, it can save you some typing when using a common idiom.

-1:-- Cl-callf (Post jcs)--L0--C0--February 16, 2024 04:47 PM

Karthik Chikmagalur: For Your Reference: RefTeX in Org Mode

TL;DR: “We have X-references at home”

Cross-references in Org mode are not as well developed as its (relatively) new citation system. Org’s built-in linking system is fairly comprehensive and exports to all formats well enough. But if you’re coming to Org from LaTeX, you might prefer something more familiar and oriented towards the label/references mental mapping than the anchor/link system. Over the decades, I drifted into Org mode from writing LaTeX, and brought along RefTeX support. Sort of.

In the past few months I received a number of questions about my usage of LaTeX-style cross-references in Org, so here’s a short explanation and, uh, reference.

Just give me the code

One half of my long-winded Emacs articles begin or end with a call to action:

…and it’s a package, and here’s a link, it’s on MELPA, try it out.

This is not one of those. There is a package, and more code besides. But this is stuff extracted from my Emacs init file, which is a cross between a palimpsest, a crime scene and a dry-erase board that don’t erase no more. Use at your own peril, or better yet, reuse these ideas to make something coherent and plug-and-play:

  1. consult-reftex: A better interface to RefTeX, with Consult
  2. reftex-xref: xref and eldoc support for RefTeX
  3. Some configuration to turn on these enhancements.

Demos and explanations below.

There are other solutions – actual solutions – for cross-references in Org mode. Org’s built-in linking system can be repurposed for this, and it will export to all document formats. Bruce D’Arcus’ oxr is a proof of concept of a cross-referencing system built on top of Org’s links. For LaTeX exports, there’s John Kitchin’s frighteningly full-featured org-ref, with simple and elegant cross-referencing syntax.

One of these is probably what you want.

One of these is probably what I would have used if I had given my slow transition from LaTeX to Org any active thought. Instead I just continued to use RefTeX.

What’s a RefTeX?

RefTeX is an Emacs minor mode with distinct support for \ref, \label, \cite, and \index commands in (multi-file) LaTeX documents. It is included with Emacs.

The official description undersells it. RefTeX is extensive: it supports cross-references and citations, semi-automatic label creation, reference tracking and updates, indexes, multi-file documents, references to external documents, a dynamic table of contents and more, and provides the means to do everything that we’re about to do more clumsily below.

In a happy coincidence, RefTeX works without a hitch in Org documents… to a point It gets dicey with multi-file Org documents. . This is an oft-ignored advantage of regexp-based, structure-agnostic parsing – once you iron out the many, many edge cases over a few years, it tends to work pretty much everywhere. RefTeX’s Org mode “support” is good enough for my purposes, and having it work the same in Org and LaTeX documents is a bonus.

It even integrates with cdlatex and org-cdlatex, with automatic label insertion when inserting environments. (This is not a coincidence.)

There are two limitations.

  1. Obviously, LaTeX style cross-references only work with LaTeX exports.

  2. The other issue with RefTeX, and the main focus of this write-up, is the UI. It’s very… 90s, let’s say. Better previewing, faster navigation, integration into the modern Emacs API, some DWIM behavior – these are all achievable with a few band-aids. The result is a patchwork, but that’s most things Emacs.

Finally, there’s one other deep connection between RefTeX, CDLaTeX and Org mode. It’s two words. Take your guesses, or open up reftex.el and read the header!


Sprucing up the RefTeX experience

A reassertion: RefTeX already works reasonably well in Org mode, and you can work around edge cases if you encounter them. The write-up until now was thus mainly a PSA. What follows is minor improvements to the UI and the general experience of Org when using RefTeX.

Insert references with consult-reftex

As mentioned above, RefTeX is a kitchen sink library – it provides everything you need to manage references, including a multi-step reference insertion menu. But this process leaves a bit to be desired:

I wrote consult-reftex a while ago to provide a better interface for reference creation: The LaTeX previews in the buffer, in consult-reftex’s previews and elsewhere are from Org’s upcoming LaTeX preview system overhaul.

Play by play
  • Call consult-reftex-insert-reference to insert a reference at point. I’ve bound it to a snippet, so I invoke it by typing in ref and pressing TAB instead.
  • This command presents a completing-read interface where you can narrow the candidates to just equations, figures, sections etc. Candidate objects are previewed above the minibuffer.
  • You can choose how to insert this reference (as a \ref, \eqref, \cref etc).
  • Repeat this a couple of times.

I wrote it for LaTeX documents, but thanks to RefTeX’s flexibility it works all the same in Org mode.

Looking up references: consult-reftex (again)

The preview interface for consult-reftex serves a second purpose – you can use it as a listing of the equations in your document (by label), and act on labels via Embark.

Play by play

Same usage pattern, different focus from the above

  • Cycle through labeled objects (equations/sections etc) in the document
  • Invoke Embark on a candidate to access additional RefTeX actions: change the label (and all references to it), reparse the file, etc.

I don’t do this when writing, but it’s occasionally useful when reviewing work.

Fontify…

RefTeX labels in Org are plain by default,

so a little syntax highlighting goes a long way:

(font-lock-add-keywords
   'org-mode
   '(("\\(\\(?:\\\\\\(?:label\\|ref\\|eqref\\)\\)\\){\\(.+?\\)}"
      (1 font-lock-keyword-face)
      (2 font-lock-constant-face))))

I didn’t bother making a comprehensive regexp for font-locking here, you might want to thrown in more keywords.

This is more than eye-candy: a little further below, we use the faces we specify here as anchors for jumping between references. If you use different faces for the keywords, you’ll need to update the navigation code below.

…and Prettify: TeX-fold-mode

TeX-fold-mode, provided with AucTeX, is useful for shortening long references by placing overlays over them.

Before:

After (with the mouse over one of the references):

The effect is purely visual, moving the cursor into the overlay reveals the full reference:

This requires turning on TeX-fold-mode.

It can fold most LaTeX constructs, including environments, sections, comments and even individual math symbols and operators. We’re only interested in folding references and labels here, though, so we can specify this:

(setq TeX-fold-type-list '(macro))

You can make this Org mode-specific with a hook if you want to fold more constructs in LaTeX documents.

Optional: we can tweak the overlay styling for labels and refs as well:

Reference/Label folding style: code
;; Faces
(set-face-attribute 'TeX-fold-folded-face nil :foreground nil :inherit 'shadow)
;; Custom folded display for labels and refs
(defun my/TeX-fold-ref (text)
  (let* ((m (string-match "^\\([^:]+:\\)\\(.*\\)" text))
         (cat (or (match-string 1 text) ""))
         (ref (or (match-string 2 text) text)))
    (when (> (length ref) 13)
        (setq ref (concat (substring ref 0 6) "..." (substring ref -6))))
    (concat "[" (propertize cat 'face 'shadow) ref "]")))
(defun my/TeX-fold-label (&rest texts)
  (cl-loop for text in texts
           for m = (string-match "^\\([^:]+:\\)\\(.*\\)" text)
           for cat = (or (match-string 1 text) "")
           for ref = (or (match-string 2 text) text)
           collect (concat "[" (propertize cat 'face 'shadow) ref "]") into labels
           finally return (mapconcat #'identity labels ",")))
(setq-default TeX-fold-macro-spec-list
              '((my/TeX-fold-label ("cite"))
                (my/TeX-fold-label ("label"))
                (my/TeX-fold-ref ("ref" "pageref" "eqref" "footref"))))

Lastly, you might want to turn folding on in Org buffers. TeX-fold uses the C-c C-o key as a prefix, watch out for key conflicts with Org!

(add-hook 'org-mode-hook 'TeX-fold-mode)

Moving around references: big ol’ pile o’hacks

This is mostly to avoid having to use the mouse – Isearch or Avy aren’t very useful if the reference is folded. That said, its main advantage is not that it moves the cursor (for editing), but that it enables other actions we might want to take immediately after, such as jumping to the definition of a label with xref.

Play by play

I jump to the next reference with M-g r, and then jump forward and backward across references, labels and citations with M-g r and M-g R. Actually, after invoking it once I jump forward and back with just r and R, thanks to repeat-mode. The code is below.

We want several things to happen when we jump to a reference:

  • Org should open up folded/invisible text if the references is in a hidden region,
  • Any TeX-fold overlay at the reference should be cleared,
  • and if we jumped from a reference it should be folded again by TeX-fold.
  • If a preview is available (via Eldoc, see below), it should be activated.

The code for this is below. It’s not pretty.

Jumping by reference: code
(defun my/next-reference-or-label (_arg)
    (interactive "p")
    (let* ((prop))
      (pcase-let
          ((`(,_ . ,ov)
           (get-char-property-and-overlay (point) 'TeX-fold-type)))
        (when ov (TeX-fold-hide-item ov)))
      (save-excursion
        (and (setq prop (text-property-search-forward
                         'face nil
                         (lambda (_ val)
                           (memq val '(font-lock-constant-face org-cite)))
                         t))))
      (if prop
          (progn (goto-char (prop-match-beginning prop))
                 (when (and (derived-mode-p 'org-mode) (org-invisible-p))
                   (org-fold-show-context 'link-search))
                 (when eldoc-mode (eldoc--invoke-strategy t))
                 (pcase-let
                     ((`(,_ . ,ov)
                      (get-char-property-and-overlay (point) 'TeX-fold-type)))
                   (when ov (TeX-fold-show-item ov))))
        (message "No more references/labels."))))

(defun my/previous-reference-or-label (_arg)
   (interactive "p")
   (let ((p))
     (save-excursion
       (and (text-property-search-backward
             'face nil
             (lambda (_ val)
               (memq val '(font-lock-constant-face org-cite
                           TeX-fold-folded-face)))
             t)
            (setq p (point))))
     (pcase-let
         ((`(,_ . ,ov)
           (get-char-property-and-overlay (point) 'TeX-fold-type)))
       (when ov (TeX-fold-hide-item ov)))
     (when p
       (goto-char p)
       (when (and (derived-mode-p 'org-mode) (org-invisible-p))
         (org-fold-show-context 'link-search))
       (when eldoc-mode (eldoc--invoke-strategy t)))
     (pcase-let
         ((`(,_ . ,ov)
           (get-char-property-and-overlay (point) 'TeX-fold-type)))
       (when ov (TeX-fold-show-item ov)))))

While we’re at it, we might as well make this command repeatable, so you can navigate with just r and R after the first jump:

(keymap-set org-mode-map "M-g r" 'my/next-reference-or-label)
(keymap-set org-mode-map "M-g R" 'my/previous-reference-or-label)
(defvar-keymap my/TeX-ref-map
  :repeat t
  "r" 'my/next-reference-or-label
  "R" 'my/previous-reference-or-label)

Jumping to definition: xref support, more hacks

With the cursor at a reference, jump to the corresponding label using Emacs’ standard xref mechanism:

Play by play
  • Jump to the next reference with M-g r, as before.
  • Jump to the corresponding label with M-., Emacs’ standard keybinding for xref-find-definitions.
  • Jump back with M-,, Emacs’ standard keybinding for xref-go-back.
  • Repeat a couple of times.

Code below. Again, not pretty.

xref support for RefTeX: code
(require 'xref)
(require 'reftex-ref)

(cl-defmethod xref-backend-identifier-at-point ((_backend (eql reftex)))
  (when (looking-back "\\\\\\(?:page\\|eq\\|auto\\|c\\)?ref{[-a-zA-Z0-9_*.:]*"
                                      (line-beginning-position))
		    (reftex-this-word "-a-zA-Z0-9_*.:")))

(cl-defmethod xref-backend-definitions ((_backend (eql reftex)) prompt)
  (unless (symbol-value reftex-docstruct-symbol) (reftex-parse-one))
  (when-let* ((docstruct (symbol-value reftex-docstruct-symbol))
              (data (assoc prompt docstruct))
              (label (nth 0 data))
              (file  (nth 3 data))
              (buffer (or (find-buffer-visiting file)
                          (reftex-get-file-buffer-force
                           file (not reftex-keep-temporary-buffers))))
              (re (format reftex-find-label-regexp-format (regexp-quote label)))
              (found (with-current-buffer buffer
                       (or (re-search-backward re nil t)
                           (progn (goto-char (point-min))
                                  (re-search-forward
                                   (format reftex-find-label-regexp-format2
                                           (regexp-quote label))
                                   nil t))))))
    (list (xref-make prompt (xref-make-buffer-location
                             buffer found)))))

(cl-defmethod xref-backend-apropos ((_backend (eql reftex)) pattern)
  (xref-backend-definitions 'reftex pattern))

(defun reftex-xref ()
  "Function to activate buffer-local backend.
Add this function to `xref-backend-functions' to use xref to find
function and variable definitions in the same buffer.

This should have a low priority among xref backends."
  'reftex)

;; Eldoc support via xref, because why not
(defun reftex-xref--display-in-eldoc (callback)
  "Display reference label location in Eldoc.

Call CALLBACK if possible."
  (when (cl-intersection
         (ensure-list (get-char-property (point) 'face))
         '(font-lock-keyword-face font-lock-constant-face
           TeX-fold-unfolded-face))
    (save-excursion
      (when-let*
          ((inhibit-redisplay t)
           (_macro (car (reftex-what-macro-safe 1)))
           (key (reftex-this-word "^{}%\n\r, \t"))
           (item (car (xref-backend-definitions 'reftex key)))
           (location (xref-item-location item))
           (pos (xref-buffer-location-position location))
           (docstring
            (with-current-buffer (xref-buffer-location-buffer location)
              (goto-char pos)
              (pcase-let
                  ((`(,start . ,end)
                    (condition-case nil
                        (LaTeX-find-matching-begin)
                      (:success
                       ;; Found \begin{}...\end{}
                       (cons (point)
                             (progn (forward-char 1)
                                    (LaTeX-find-matching-end)
                                    (point))))
                      (error
                       ;; No \begin, find \section{} or Org heading instead
                       (goto-char pos)
                       (re-search-backward
                        (pcase major-mode
                          ('org-mode "^*")
                          (_ (concat "\\(" (LaTeX-outline-regexp)
                                     "\\|\\`\\)")))
                        (line-beginning-position -2)
                        t)
                       (cons (line-beginning-position)
                             (line-end-position))))))
                (buffer-substring-no-properties start end)))))
        (if-let* ((prop-and-ov
                   (get-char-property-and-overlay
                    (xref-buffer-location-position location)
                    (pcase major-mode
                      ('org-mode 'org-overlay-type)
                      ('latex-mode 'category))))
                  (_ (memq (car prop-and-ov)
                           '(org-latex-overlay preview-overlay)))
                  (ov (cdr prop-and-ov)))
            (funcall callback (propertize docstring 'display
                                          (overlay-get ov 'preview-image)))
          (funcall callback docstring))))))

(defun reftex-eldoc-activate ()
  (add-hook 'eldoc-documentation-functions
            'reftex-xref--display-in-eldoc
            nil :local))

(defun reftex-xref-activate ()
  "Activate reftex support using xref."
  (add-hook 'xref-backend-functions
            'reftex-xref nil :local))

(provide 'reftex-xref)

To turn on xref support for references in Org mode:

(use-package reftex-xref
  :after (org latex)
  :hook (org-mode . reftex-xref-activate))

Auto-preview reference at point: eldoc support

If you’re jumping to label definitions with xref just to look at the corresponding equations, we can skip that step with previews of the referenced object. With eldoc-mode Eldoc is Emacs’ standard and recommended way of showing contextual help. enabled:

Play by play
  • With eldoc-mode enabled, move the cursor into (or jump into) a reference.
  • This activates a preview of the relevant label, mostly equations in this example.
  • Works for sections too, but not figures or tables (yet).
  • Repeat a few times.

Equations and figures can be pretty tall, so if you’re not using a dedicated Eldoc buffer (or eldoc-box) you might want to change the height that the echo area can expand to.

(setq eldoc-echo-area-use-multiline-p t
      max-mini-window-height 0.4)

To activate reference previews as an Eldoc documentation function, we use the helper function defined as part of reftex-xref above.

(use-package reftex-xref
  :after (org latex)
  :hook (org-mode . reftex-eldoc-activate))

Yep, reftex-dcr exists

Earlier I mentioned that we would be clumsily recreating features RefTeX already includes. Indeed, most of the above features, like jumping to label definitions and previewing the reference at point, are already provided in the reftex-dcr library, included with RefTeX. The problem is that it predates Emacs’ xref and eldoc APIs (if not their libraries), and like most 90s Emacs packages it reinvents all the wheels. This is painful on more than an aesthetic level – I prefer to have one well-supported and convenient way for each semantic task. The mental model is simpler, as are the required keyboard gymnastics.

Thankfully, there’s enough of a separation between the data-parsing RefTeX backend and the UI that I was able to drive a wedge through and beat it into a more consistent shape. Needless to say, this shape is not very robust. Emacs makes it very easy to cobble together an 80% solution – I spent much longer writing this little explainer than I did actually slapping together the code. Due to a pies ≫ fingers situation, I’m not going to be able to get these hacks to a stage beyond “it works for me”. The interest around this topic suggests that perhaps someone might be interested in developing the idea further and modernizing RefTeX, or working on a better cross-referencing system for Org mode. Have at it!

-1:-- For Your Reference: RefTeX in Org Mode (Post)--L0--C0--February 16, 2024 12:48 PM

Charles Choi: Computing Truth Tables in Org

Truth tables are great way to design and verify combinational logic. Useful as truth tables are, they however can be onerous to write: as the number of inputs grow, so does the size of table in exponential fashion (row size = 2number of inputs). Writing this out is not fun.

Thankfully, enumeration of said input values in Emacs can be automated. This post goes over an approach to generating truth table inputs with which you can use to drive combinational logic in Org.

Inserting the Truth Table Input

Before getting into implementation details, let’s go over inserting truth table input into your current Org buffer.

Invoke the command M-x cc/insert-truth-table-input. In the mini-buffer you will be prompted for the number of inputs to enumerate for the truth table input values. Entering the value 2 will insert the following table into your buffer. Note that the second and third columns map to 21 and 20 respectively. Reading across a row, the first column shows the decimal value of the input, whereas the remaining columns shows the binary representation of that input value.

1
2
3
4
5
6
|i|1|0|
|-+-+-|
|0|0|0|
|1|0|1|
|2|1|0|
|3|1|1|

Combinational Logic in Org

If the truth table input is in an Org buffer, we can use its spreadsheet capabilities to compute combinational logic with said values. For example, with the table above we can compute the xor of each input row by entering the following formula for all cells in column $4. Refer to Emacs Lisp forms as formulas for more general documentation on how Org formulas work.

1
2
3
4
5
6
7
| i | 1 | 0 | output |
|---+---+---+--------|
| 0 | 0 | 0 |      0 |
| 1 | 0 | 1 |      1 |
| 2 | 1 | 0 |      1 |
| 3 | 1 | 1 |      0 |
#+TBLFM: $4='(digital-xor $2 $3);N

Note that the formula must include the N flag so that referenced cells are parsed as numbers.

To support combinational logic in Org tables, I’ve created a library to support digital boolean operations, where the only legal input values are 0 and 1. The following boolean logic operations are supported:

  • multi-input and (digital-and)
  • multi-input or (digital-or)
  • multi-input nand (digital-nand)
  • multi-input nor (digital-nor)
  • 2-input xor (digital-xor)

Implementation

Implementation of the behavior described above is done in the following two files:

  • cc-truth-table.el which holds the functions required to implement cc/insert-truth-table-input.
  • cc-digital-logic.el which holds all the functions required to support digital combinational logic with tables created by the above.

All code verified with Emacs 29.1.

Installation

Install the above files into your load-path. Then include the following lines in your Emacs initialization file.

1
2
(require 'cc-truth-table)
(require 'cc-digital-logic)

As this work is relatively new, I’ve yet to try to make this into a MELPA package for public distribution. If enough interest is there, I can be motivated to do this.

On the need to write a digital logic library

At the start I tried to get away with using the base Elisp functions for boolean operations. I quickly found out that such functions were really designed presuming the input values are t or nil. Using the digital convention of using 0 and 1 as input values to the base Elisp boolean operations can lead to surprising results. For example, evaluating (not 0) will return nil. Ultimately this led to writing a library of boolean functions expressly designed to use as input the digital values of 0 and 1.

Closing Thoughts

Programmers deal with combinational logic all the time. As much as I love truth tables, they are a PITA to make because so much of the work is mechanical. Automating this work with Elisp and Org has freed me to focus on just solving the logic problem. Engaged readers might find similar results.

Thanks for reading! Feel free to provide feedback about this post to me on Mastodon.

-1:-- Computing Truth Tables in Org (Post Charles Choi)--L0--C0--February 15, 2024 06:30 PM

Irreal: Zamansy: Using Emacs #82

Mike Zamansky is using some of his newfound free time to learn more about AI. He’s going through Andrew Ng’s Coursera ML class, which requires working in Python. Zamansky used to do a lot of Python but has recently switched to Clojure as his go to language. From his previous Python work, he’s familiar with the idea of virtual Python environments and wanted to upgrade his workflow.

In this latest video he talks about Conda and Direnv to help automate his Python workflow. Conda lets you set up separate environments for such things as the Python version and the packages available to it. This is really handy if you have several different Python projects going on or you want to replicate the environment of someone else’s project.

Of course, you still have to activate the appropriate environment before you start working in it. That’s where Direnv comes in. The idea is that you put a special file in the directory of your project and it will set up whatever environment you need when you enter the directory. It can, for example, activate a Conda environment but it’s more general and not really tied to Python. You can use it anytime you want to automatically set up an environment.

You can get all the details from Zamansky’s post and the associated video. It looks as if Direnv can be a little fussy to set up but he has links to his configuration to help you get going. The video is 15 minutes, 20 seconds long, so you’ll have to set some time aside but, as usual, it’s worth your time. Even if you aren’t using Python, you may find that Direnv can ease your workflow.

-1:-- Zamansy: Using Emacs #82 (Post jcs)--L0--C0--February 15, 2024 04:48 PM

Meta Redux: CIDER: Community Impact

Alone we can do so little; together we can do so much.

– Helen Keller

I’ve started CIDER’s Open Collective some 7-8 years ago and over the course of time it became of the main donation channels for the project. In good years it would raise somewhere between $10,000 and $15,000. While my initial plan was to use the money to fund my own work on the project, I wasn’t in a rush to withdraw them as I had a pretty stable job, that managed to cover our family’s expenses. So, I felt it was better to let the money get accumulated for a time when we could fund the work of other developers, or I find myself out of a job and could focus my entire attention on CIDER for a few months.1 One can dream, right?

Last year, one of CIDER’s long-time contributors V. (a.k.a. vemv) was between jobs and I he offered to work on a long list of problems and improvements if we could fund his work with money from the collective. I was excited, as with me juggling so many projects it’s hard to find enough time to go in “the zone” and work for an extended period of time on one particular project. I’ve noticed that over the years my responsibilities as maintainer shifted from “writing all (most of) the code” to things like:

  • building a long-term strategy for the projects
  • evangelizing the projects (with blog posts, presentations, etc)
  • providing support for the end-users
  • writing user and developer documentation
  • reviewing bug reports and feature requests
  • “recruit” contributors
  • helping contributors and reviewing their submissions

I guess you get the idea. It’s not very different from the professional transition I had in the past from a software engineer to an engineering manager. Anyways, the role of the maintainer is a very big topic that I plan to expand upon in the future, and I’m not going to dwell too much on it right now. What’s relevant for today’s article is that the primary role of the maintainer (at least the way I see it) is to make the right decisions for the health and future of the project.

In this case it seemed prudent to fund the work of a trusted contributor, who happened to have the time and the energy to work on complex bodies of work. We started our collaboration in May 2023 and I hope you’ll agree that the results have been impressive:

  • 6 CIDER feature releases (1.8-1.13)2
  • Massive improvements to error handling, Java documentation rendering, the indentation logic, the test runner and the data inspector
  • Many (obscure) long-standing bugs were finally addressed
  • Countless releases of related projects (e.g. enrich-classpath, orchard and cider-nrepl)
  • Increased bandwidth for user support (as V. provided a lot of support himself over Slack and GitHub)

So much was done last year that I’m certainly forgetting something. At any rate, I’m quite happy with the outcomes, and I believe they showcase great how additional funding can help FOSS projects. The total funding our collective contributed to V.’s work was about $65,000 and while that’s a lot of money, I think it was money well spent. I’ve opted to fund his work at actual market rates, so he’d be most incentivized to deliver the best possible outcomes. That’s the only way to do OSS work sustainably.

CIDER had a great 2023 and it had it because of its awesome community! You rock!

Now our collective is drained and it’s not clear when we’ll be able to do something like this again. If we assume a budget of $10,000/year that would happen 7 years down the road. Sadly, with the economic downturn that affected heavily the IT industry the amount of donations my projects have been getting has plummeted. Lots of companies and individuals pulled out and donations across GitHub Sponsors and OpenCollective in 2023 are down about 20% compared to 2022. I recall there was a smaller drop from 2021 to 2022. Sadly, donations remain a very unstable and unpredictable source of funding.

It’d be great if we had 20+ companies supporting the project with something like $500+/month (which is a drop in the pond for most businesses), but I doubt this will ever happen. Donating to OSS projects is a hard sell in general, and a plugin for Emacs is probably a sale that is as hard as it gets. Perhaps down the road we’ll try some focused fundraisers for specific features/improvements, although I have some reservations about such an approach as well.

CIDER’s success with funding some development via OpenCollective is not an isolated case, of course. Clojurists Together is another great case study for the positive impact funding can have on FOSS projects. I guess no other organization has done more for the Clojure community, and I’m extremely grateful for all the support the members of Clojurists Together have had for CIDER and friends over the years.

I write this article in the hope that seeing how donations get translated into real impact (e.g. productivity gains) more companies and individuals will be willing to help out. CIDER is all of you, the amazing Clojure community. Clojurists together strong!

P.S. I really struggled with the name for this article. I considered options like:

  • “The Impact of Community Funding to CIDER”
  • “CIDER: A Community-funded Success Story”
  • “Making a Difference for CIDER”

In the end I probably went with the most vague, but shortest title I could think of. Somewhat typical for me.

  1. I also hoped the collective would fund more Clojure conference trips for me and other key contributors, but sadly Covid-19 put those plans on halt for quite a while. I haven’t been to a Clojure event since Feb 2020… 

  2. You can find more details in the changelog

-1:-- CIDER: Community Impact (Post Bozhidar Batsov)--L0--C0--February 15, 2024 02:20 PM

Mario Jason Braganza: Note to Self, Emacs Help Stuff


Collection of various little things I find handy when I am lost in Emacs

  • Truly lost and need help about help itself? C-h C-h or C-h ?
  • C-h m should bring up a list of all the shortcuts available to that mode.
  • Want to find out what the docs mean when they refer to something? C-h o and then type in that name.
    For e.g. when the Org Mode docs refer to org-hide-leading-stars, a C-h o => Return => org-hide-leading-stars will give me details about what it is. (According to the docs: org-hide-leading-stars is a variable defined in ‘org.el’. Its value is nil.) Alternatively if you know the type of object, you’re looking at, C-h f will give you help on a function and C-h v will do the same with a variable.
  • If you want to know what function is behind this crazy shortcut you’re typing, a C-h k followed by giving it the shortcut your typing will lead you to the culprit command that powers it.
    For e.g. I blanked out on how to toggle multiple checkboxes in Org Mode today and I was hitting C-c C-x b to on avail. Running C-h k told me that it was bound to org-tree-to-indirect-buffer. So I was madly mashing the wrong keys. A little digging (with C-h m) revealed that what I needed was C-c C-x C-b which ran org-toggle-checkbox which indeed did what I wanted! :)


Feedback on this post? Mail me at feedback at this domain

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.


-1:-- Note to Self, Emacs Help Stuff (Post)--L0--C0--February 15, 2024 02:33 AM

Please note that planet.emacslife.com aggregates blogs, and blog authors might mention or link to nonfree things. To add a feed to this page, please e-mail the RSS or ATOM feed URL to sacha@sachachua.com . Thank you!