Echo AreaJust a blog, ok2023-12-07T22:45:51Ztekutihttp://blog.ryuslash.org/feed/atomTom Willemsenhttp://blog.ryuslash.org/TIL: I can use elfeed-search-face-alist to highlight certain headlines in Elfeedhttp://blog.ryuslash.org/2023/12/07/til-i-can-use-elfeed-search-face-alist-to-highlight-certain-headlines-in-elfeed2023-12-07T22:45:51Z2023-12-07T22:45:51Z

I rediscovered that I can use elfeed-search-face-alist to customize how headlines are displayed in Elfeed. I had read it before on Chris Wellons' blog, but I didn't have a use for it then.

With elfeed-search-face-alist I can define a face to be used for an article with a specific tag. I added the Blabbermouth RSS feed to my feeds.

(setq elfeed-feeds '(("https://blabbermouth.net/feed" music)))

And created 2 taggers. One tags reviews, because those always have /reviews/ in the url. The other has a list of bands that I'm especially interested in.

(defvar oni-elfeed-blabbermouth-review-tagger
  (elfeed-make-tagger :feed-url (rx "blabbermouth.net")
                      :entry-link (rx "/reviews/")
                      :add 'review)
  "Tagger that marks any reviews from Blabbermouth.")

(defvar oni-elfeed-blabbermouth-favourite-tagger
  (elfeed-make-tagger :feed-url (rx "blabbermouth.net")
                      :entry-title (rx (or "SLIPKNOT"
                                           (seq "DREAM" whitespace "THEATER")
                                           ;; And so on...
                                           ))
                      :add 'favourite)
  "Tagger that highlights specific bands from Blabbermouth.")

(add-hook 'elfeed-new-entry-hook oni-elfeed-blabbermouth-favourite-tagger)
(add-hook 'elfeed-new-entry-hook oni-elfeed-blabbermouth-review-tagger)

And then I can just set up the feeds:

(add-to-list 'elfeed-search-face-alist '(review :slant italic) t)
(add-to-list 'elfeed-search-face-alist '(favourite :foreground "#f17272") t)

As long as the face definitions don't conflict a headline's face would be the combination of all that apply. For example, by default unread headlines are bold, so unread favourite messages would be bold and somewhat reddish.

Tom Willemsenhttp://blog.ryuslash.org/Switch TODO state when clocking inhttp://blog.ryuslash.org/2023/07/10/switch-todo-state-when-clocking-in2023-07-10T05:56:39Z2023-07-10T05:56:39Z

This Emacs configuration snippet for org-mode changes a task's state from whatever “head” state it's in into the next state in its sequence when you clock in to a task. I do this by setting the org-clock-in-switch-to-state variable.

Different sequences of TODO states

First, just to make sure that this is explained in here, it's possible in org-mode to specify multiple sequences of task states, for example I had this in one of my org files:

#+SEQ_TODO: TODO WIP BLOCKED | DONE
#+SEQ_TODO: READ READING | FINISHED STOPPED
#+SEQ_TODO: WATCH WATCHING | WATCHED
#+SEQ_TODO: LISTEN LISTENING | DONE

This means that there are 4 sequences I've set up. A task can start as either TODO, READ, WATCH, or LISTEN, and then it'll move to a different next state1 depending on which initial state was picked. WIP comes after TODO, WATCHING after WATCH, etc. They generally don't cross, although org-mode will get confused as soon as I change any TODO or LISTEN task to DONE since at that point it can't figure out what it would change back to if it turns out I wasn't done after all. It'll make it TODO if I move forward from DONE in either case.

Here is the graph showing the paths of each sequence:

TODO TODO WIP WIP TODO->WIP BLOCKED BLOCKED WIP->BLOCKED DONE DONE BLOCKED->DONE DONE->TODO READ READ READING READING READ->READING FINISHED FINISHED READING->FINISHED STOPPED STOPPED FINISHED->STOPPED STOPPED->READ WATCH WATCH WATCHING WATCHING WATCH->WATCHING WATCHED WATCHED WATCHING->WATCHED WATCHED->WATCH LISTEN LISTEN LISTENING LISTENING LISTEN->LISTENING LISTENING->DONE

Although this doesn't actually affect me at all in any way because I have org-use-fast-todo-selection set to t.

Making The Switch

Getting back to my snippet: org-clock-in-switch-to-state can be set to either a string, which will just always change it to that particular state when you clock in, or a function that takes a single parameter (the current state of the task you're clocking in to). For this case I want the function, because I won't know which state I want to change to until I know the current state, since TODO will change to WIP, READ to READING, etc. but also when a task is already in the state READING, for example, I don't want it to change at all.

(defun oni-org-maybe-change-todo-state (current-state)
  "Change the state of the current task to its next state.
Only do this outside of a capture buffer and when CURRENT-STATE
is the head state of whichever sequence of states applies to this
task."
  (if (and (not org-capture-mode)
           (member current-state org-todo-heads))
      (cadr (member current-state org-todo-keywords-1))
    current-state))

First I make sure that we're not in a capture buffer. Some of my capture templates state that they should clock in to whatever I'm capturing right away, and in that case the task I'm capturing might immediately change from TODO to WIP, for example.

Then I check to see if the current state is in the org-todo-heads variable, which contains only the first todo state of each possible sequence of states. Let's assume my todo states are:

#+SEQ_TODO: TODO WIP BLOCKED | DONE

Checking that the current-state is in org-todo-heads basically means I check to make sure that current-state is TODO and not any of the other ones. I do this so that if I clock in to a WIP task, it doesn't automatically switch to blocked.

If I'm not in a capture buffer, and the current state is one of the head ones, I search for the current state in the org-todo-keywords-1 which is a simple flat list of all the possible todo states org-mode knows about. This is easier to work with than org-todo-keywords, since that is an alist of (type . list-of-states) and has a bunch of information I don't need. I return whatever comes right after the current state.

Returning whatever next state is in the list does mean that if the next state is DONE, it'll immediately set it to done. But there is no real way to check that with the way I've done this. There is just the next state.

Finally you just set this function as the value of org-clock-in-switch-to-state and then you're good to go.

(setq org-clock-in-switch-to-state #'oni-org-maybe-change-todo-state)

Footnotes:

1

By which I mean by pressing C-c C-t when org-use-fast-todo-selection is nil or pressing C-S-<right> on the headline.

Tom Willemsenhttp://blog.ryuslash.org/IELM & Paredithttp://blog.ryuslash.org/2023/04/12/ielm-paredit2023-04-12T07:02:45Z2023-04-12T07:02:45Z

For a while I've been bothered by being unable to use IELM to evaluate Emacs Lisp expressions. Then Making IELM More Comfortable pointed out that using paredit in IELM causes the RET and C-j keybindings to be bound to the wrong functions and proposes to fix them. The given fix, however, appears to change the keybindings for all buffers using paredit. I don't want that, and Emacs can do better!

A quick search yielded Buffer-locally overriding minor-mode key bindings in Emacs suggesting a fix. My adaptation of that solution:

(defun oni-elisp-ielm-remove-paredit-newline-keys ()
  "Disable ‘C-j’ and ‘RET’ keybindings from ‘paredit-mode’."
  (let ((oldmap (map-elt minor-mode-map-alist 'paredit-mode))
        (newmap (make-sparse-keymap)))
    (set-keymap-parent newmap oldmap)
    (define-key newmap (kbd "RET") nil)
    (define-key newmap (kbd "C-j") nil)
    (make-local-variable 'minor-mode-overriding-map-alist)
    (push `(paredit-mode . ,newmap) minor-mode-overriding-map-alist)))

(add-hook 'ielm-mode-hook #'oni-elisp-ielm-remove-paredit-newline-keys)

Defining RET and C-j as nil means that the keybinding defaults back to the major-mode keybinding.

Tom Willemsenhttp://blog.ryuslash.org/Combining Shell and Lisp in Eshellhttp://blog.ryuslash.org/2021/09/11/combining-shell-and-lisp-in-eshell2021-09-11T09:03:08Z2021-09-11T09:03:08Z

The code in this post is entirely useless since Perforce already provides this feature out of the box, I just didn't know about it at the time. Still, I wanted to post something and this seemed as fun as anything else.

I have been working on vc-p4 off-and-on for a while to make working with Perforce more enjoyable in Emacs. I have some plans for that package. One of the bigger things that I have done so far was add the option to specify the client in the .dir-locals.el.

So in my .dir-locals.el I would have something along the lines of the following:

((nil . (vc-p4-client . "SOME-CLIENT-NAME")))

This would let Emacs switch automatically between the different clients.

I wanted to use the p4 command-line rather than P4V more, but a thing that bugged me was that this didn't allow me to automatically pick the client, and I didn't like having to type in p4 -c SOME-CLIENT-NAME ... all the time. I felt like with Emacs, Eshell, and the feature I added to vc-p4 surely I should be able to do something about this.

The first problem I ran into is that Eshell doesn't load the directory-local variables when I change into a directory. So I wrote something to do that:

(defun oni-eshell-set-local-variables ()
  (dolist (elt file-local-variables-alist)
    (set (make-local-variable (car elt)) (default-value (car elt))))
  (setq file-local-variables-alist nil)
  (hack-dir-local-variables-non-file-buffer))

First it goes through all of the local variables that have been set before and resets them to their default value. This is so that any variables that are set locally don't hang around when you leave the directory. It then sets the list of currently set file local variables to nil so that it doesn't consider them cached and skips over giving them new values. Finally it calls the hack-dir-local-variables-non-file-buffer function that specifically exists to set directory-local values for variables in a buffer that isn't associated with a file.

(add-hook 'eshell-directory-change-hook #'oni-eshell-set-local-variables)

The function needs to run every single time the current directory changes, which Eshell has a hook for.

This would let me use a single variable essentially for specifying the current client to use. As long as there is a value this would work:

p4 --client $vc-p4-client ...

That's definitely easier than having to remember exactly which client I was using. It can still be better. I know that in Eshell shell-like constructs can be combined with Lisp easily by using either ${} or $(). So really I can just use a single command that checks whether there is a value for the client or not and calls p4 accordingly:

p4 $(when vc-p4-client (list "--client" vc-p4-client)) ...

This is very wordy though. Since this can be called any time it's nice to just make it an alias:

alias p4 'p4 $(when vc-p4-client (list "--client" vc-p4-client)) $*'

Now I can just call p4 and it'll specify the client for me automatically.

I haven't done a lot of stuff in Eshell, so I liked being able to write a fun little alias that combined some shell command with some simple Lisp code. Of course only after this I discovered (well, someone pointed out) that Perforce has the P4CONFIG environment variable, which names a file name to look for up the directory tree from the current directory (much like the .dir-locals.el works) and read settings from there. So I set that p4 set P4CONFIG=.p4config, and then specify the client in there.

P4CONFIG=SOME_CLIENT_NAME

And now I don't have to go through any of this, I can remove the whole feature I added to vc-p4 too.

Tom Willemsenhttp://blog.ryuslash.org/Loading the Emacs Info manuals in MSYS2http://blog.ryuslash.org/2021/07/04/loading-the-emacs-info-manuals-in-msys22021-07-04T01:58:03Z2021-07-04T01:58:03Z

I've been annoyed at MSYS2 for a while because Info manuals included with Emacs wouldn't show up when I opened info. The few manuals that were installed through ELPA packages showed up fine.

Some time ago I discovered this was because I installed the mingw-w64-x86_64-emacs package from MSYS2, and this package installs all the info manuals into /mingw64/share/info instead of /usr/share/info and there was no dir file in there. I couldn't quite remember how this worked so I left it alone. At least I understood what was going on.

Recently I finally took the time to look at it again. I remembered that pacman has some capabilities for hooks. I wrote a very simple one for myself to keep my Pacman mirror list updated automatically. But I couldn't remember where the default ones were located. pacman to the rescue. pacman -Ql pacman | less with a quick search for hooks and I discovered that these hooks live in /usr/share/libalpm/hooks/. A quick look in there showed that MSYS2 distributes a couple of hooks: texinfo-install.hook and texinfo-remove.hook. When a package gets installed, upgraded, or removed one of these hooks gets called.

Basically what the -install hook does is go through each file in the installed packages that is under /usr/share/info and call install-info on it. That's great, easy to reproduce on the command line:

find /mingw64/share/info -fype f -name '*.info' -exec install-info '{}' /mingw64/share/info/dir \;

This sets it up the first time, since I already have Emacs installed and didn't want to reinstall it.

[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Target = mingw64/share/info/*

[Action]
Description = Updating the mingw64 info directory file...
When = PostTransaction
Exec = /usr/bin/sh -c 'while read -r f; do install-info "$f" /mingw64/share/info/dir 2> /dev/null; done'
NeedsTargets

Put this in /etc/pacman.d/hooks/texinfo-install-mingw64.hook (or C:/msys2/etc/pacman.d/hooks/texinfo-install-mingw64.hook if you're working from Emacs), and now every time a package gets installed or upgraded and it has any files in /mingw64/share/info/ it should automatically update the dir file and give you access to all those info manuals.

The remove hook is basically the same, except it passes in the --delete option to install-info to remove the entries from the dir file.

[Trigger]
Type = Path
Operation = Remove
Target = mingw64/share/info/*

[Action]
Description = Removing old entries from the mingw64 info directory file...
When = PreTransaction
Exec = /usr/bin/sh -c 'while read -r f; do install-info --delete "$f" /mingw64/share/info/dir 2> /dev/null; done'
NeedsTargets
Tom Willemsenhttp://blog.ryuslash.org/olivetti-modehttp://blog.ryuslash.org/2021/06/04/olivetti-mode2021-06-04T05:45:00Z2021-06-04T05:45:00Z

I've been using olivetti-mode for a little while when I write notes in org-mode and I must say that I really enjoy it. It seems like a very simple package. It doesn't have very many interactive functions or customizable options. Essentially it comes down to you enable it and you pick the default width of the text that you want. The initial 70 is a bit too small for me, but 80 or 85 is pretty comfy.

It's also possible to have it enable and disable visual-line-mode. Personally I always have this on in Org, ever since I started using org-indent-mode.

Tom Willemsenhttp://blog.ryuslash.org/Yoshi Theme 6http://blog.ryuslash.org/2015/09/08/yoshi-theme-62015-09-08T07:00:00Z2015-09-08T07:00:00Z

According to github I released version 6 of my Yoshi theme 8 days ago. Consequently I uploaded it to Marmalade. I felt I should mention that, and it also gives me an excuse to write something.

This new version brings a newly added CHANGELOG, inspired by Keep a CHANGELOG. It doesn't completely follow the format suggested in there because it's in Org mode format and because I felt I could style this format better than the one they propose, at least when using Org mode. I still need to export it to somewhere nice.

Since the last release, which I don't think anyone has ever seen, there have been some major changes in magit's faces, so those have been added. The original ones are also still there in the hopes that anyone using an older version will still have nice colors.

I've also removed some color variations because I felt they were impurities in the theme. I don't think they actually bring anything to the table and it looks cleaner with just the basic set of colors.

If you have any suggestions, wishes, questions or insults you want to throw my way, please do so in the issue tracker.

Tom Willemsenhttp://blog.ryuslash.org/Introducing ox-coleslawhttp://blog.ryuslash.org/2015/08/05/introducing-oxcoleslaw2015-08-05T07:00:00Z2015-08-05T07:00:00Z

I have a big problem: I can't write a blog in anything other than Org mode. I have another problem: I haven't found a good way to write a blog only in Org mode. This always keeps me going back and forth between blogging systems. I've used tekuti, WordPress, and I've tried a few others. Currently I'm using Coleslaw. I haven't written anything lately though because it supports Markdown and HTML and I was getting antsy for some Org mode again. So I've been on the lookout for something new.

Well… I've had enough. I'm not going away this time. I'm going to fix my problems and commit to this system. I picked Coleslaw because it's written en Common Lisp and has some interesting features. I'm going to write an exporter for org to whatever Coleslaw needs!

I've known that it's pretty easy to write an exporter for Org mode for some time, but I've never actually tried to write one. I modified some bits and bobs on org-blog, but that didn't really work out. Today though, while reading an old(er) post on Endless Parentheses, I ran into ox-jekyll. Jekyll has a pretty similar page/post definition syntax to Coleslaw, so it seemed easy to read what they're doing and copy the relevant parts. It's a very small Emacs Lisp file, which made it very easy. So congrats to them and the people writing Org mode for making some very clear code.

So I wrote (or copied) ox-coleslaw based on ox-jekyll. It's slightly smaller than ox-jekyll because, frankly, it offers less. I just need a simple way to export a .org file to a .post file, nothing fancy.

To write posts I will use Org mode. Once ox-coleslaw is loaded I use the org export function to export it to an HTML file with the proper header. You can also do this non-interactively from, for example, a Makefile, but that is a story for another time.

This document is the first attempt at publishing a blog post using ox-coleslaw.

Tom Willemsenhttp://blog.ryuslash.org/display-graphic-p, cl-letf and themeshttp://blog.ryuslash.org/2014/10/27/displaygraphicp-clletf-and-themes2014-10-28T04:18:00Z2014-10-28T04:18:00Z

I had a problem with my theme a while where the colors would be different depending on whether I was starting Emacs as a daemon or not. The colors module has some code that changes the way it works depending on whether a graphical or non-graphical display is run. This was messing with some of the colors in my theme.

My first thought was that it should just wait until the first frame is created before setting the theme. For a while this worked.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame) (load-theme yoshi t)))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi t))))

Some time later, that stopped working, because apparently display-graphic-p stopped returning true during the execution of the after-make-frame-functions hook. So I set out to change it so it would work again. This would have been easy in earlier versions of Emacs, where flet wasn���t yet deprecated.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame)
                (flet ((display-graphic-p () t))
                  (load-theme yoshi t))))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi t))))

Unfortunately, as stated, flet is deprecated. Whenever you compile it you���re warned and told that you should use cl-flet or cl-letf instead. I couldn���t quite figure out how, though. cl-flet does almost the same thing as flet, but lexically, so you can���t dynamically rebind display-graphic-p that way. cl-letf didn���t seem to be anything like flet at all, so I didn���t know how to ever use that to accomplish this.

Thankfully, there was noflet. This, for my purposes at least, works the same way as flet did. It is not part of Emacs, however. But in this case I didn���t know what else to do.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame)
                (noflet ((display-graphic-p (&optional display) t))
                  (load-theme yoshi t))))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi t))))

This worked perfectly, but I really don���t like depending on external packages for starting up emacs.

Then Artur Malabarba wrote a post about using cl-letf and things became very clear.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame)
                (cl-letf (((symbol-function display-graphic-p))
                          (lambda (&optional display) t))
                  (load-theme yoshi-theme t))))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi-theme t))))

It���s a bit more involved than the noflet solution, but it does remove a requirement from my init.el. What I failed to realize before reading Artur���s post is that cl-letf works a lot like Emacs��� new setf, which works a lot like Common Lisp���s setf, which makes it very powerful.

By now, my theme doesn���t use the colors module anymore.

Tom Willemsenhttp://blog.ryuslash.org/Quick normal-statehttp://blog.ryuslash.org/2014/07/11/quick-normalstate2014-07-11T07:47:00Z2014-07-11T07:47:00Z

I realized today that most of the time (over 90%) of the time I save a file I feel I have finished typing something. Typing this out makes it sound very obvious, actually. One other thing I noticed is that I still forget to return to normal-state after I���m done editing. Emacs being hackable and not being content with twisting my brain to suit my editor but rather twisting my editor to suit me, I thought I might automate it a little.

My first idea was to have evil automatically revert to normal-state whenever I save and also whenever I switch buffers. I haven���t found a hook that runs when switching buffers, so I���ll need to brush up on my advising skills and have it run just before switching, perhaps even when execute-extended-command or smex run, so any explicit minibuffer action returns to normal state.

For now it���s just the save file, and only for buffers that aren���t in the emacs-state as default list:

(defun modes-starting-in (state)
  "Get a list of modes whose default state is STATE."
  (symbol-value (evil-state-property state :modes)))

(defun maybe-switch-to-normal-state ()
  "Switch the current buffer to normal state.

Only do this when the mode is not in emacs state by
default."
  (unless (memql major-mode (modes-starting-in emacs))
    (evil-normal-state)))

(with-eval-after-load evil
  (add-hook after-save-hook
            #maybe-switch-to-normal-state))

I personally only use either normal-state or emacs-state as default states when a mode loads, if you want to check more you���ll have to add some more calls to memq and change emacs to, for example insert or visual or whichever you need.