Manuel Uberti: I complete. You?

Tracking the Emacs development by building its master branch may not be a smooth experience for everyone, but for the average enthusiast is the perfect way to see their favourite tool evolve, trying experimental things, and reporting back to the core developers to help them. Sure, one has to deal with occasional build failures, but with Git at one’s service it’s always easy to reset to a working commit and find happiness again.

Recently a shiny new mode has been implemented on master: icomplete-vertical-mode. Now, if you had the chance to read this blog in the past you know that when it comes to candidate completion I am a gangsta jumping back and forth among packages with excessive self-satisfaction. But you should also already know that I like to use as many Emacs built-ins as possible. Hence, I could not wait to give icomplete-vertical-mode a try.

Turning it on is trivial:

(icomplete-mode +1)
(add-hook 'icomplete-mode-hook #'icomplete-vertical-mode)

Since other completion systems have spoiled me, I prefer scrolling over the rotating behaviour of the standard icomplete:

(setq icomplete-scroll t)

Furthermore, I always want to see the candidate list:

(setq icomplete-show-matches-on-no-input t)

This is pretty much it. I use icomplete-fido-backward-updir to move up one directory and I have exit-minibuffer bound to C-j for convenience.

I have been using icomplete-vertical-mode daily for a while now and everything has been working as expected. For the record, this mode works seamlessly with your favourite completion-styles settings, so moving from, say, Vertico to icomplete-vertical-mode is simple and easy.

-1:-- I complete. You? (Post)--L0--C0--June 24, 2021 12:00 AM

Sacha Chua: Emacs steno interface so far

So here's what I have so far:

  • a stroke log (using plover-websocket)
  • Clippy suggestions (using inotify to watch clippy.txt)
  • a scratch pad that I can quickly add the Clippy suggestions to

    Screenshot_20210623_005114.png

    Figure 1: stroke log, suggestions, scratch pad

  • cheatsheets that pop up with a stroke and pass the next keystroke through so that I can just keep typing

    Screenshot_20210623_005824.png

    Figure 2: Emily's modifiers cheatsheet

  • briefing with Briefpedia suggestions, conflicts, and alternatives for the conflicting entry.

    Screenshot_20210623_010154.png

    Figure 3: briefing

  • steno practice that runs off Org tables or word lists, displays hints, and doesn't require me to star out misstrokes (although I can if I want)

    Screenshot_20210623_010517.png

    Figure 4: suggestions, phrases

    Screenshot_20210623_010825.png

    Figure 5: Afterwards, there's a simple review list that includes the entry before the misstroked one.

  • word-based commands

    Screenshot_20210623_014231.png

    Figure 6: I can use friendly names and a custom order for commands, still be able to access all the other commands by name, and have a specific action handle input that doesn't match any of the completions.

  • defining briefs from Org Mode tables
  • automatically toggling dictionaries for types of prompts (ex: reading outlines)
  • automatically toggling modes and removing extra dashes/spaces for things like defining a function
  • outlines for toggling Emacs (raising or lowering the window) and other matching windows
  • and a plugin that allows dictionary entries such as:
    • {plover:emacs_mx:my/plover-brief-with-check}
    • {plover:emacs_key:M-RET}
    • {plover:emacsclient_current:(my/org-edit-special-dwim)}

It probably won't work for other people yet, but maybe I can play around with ideas and we can try to get some working on other people's computers. Some of the code is in the Plover section of my config (https://sachachua.com/dotemacs#plover), if you want to explore!

-1:-- Emacs steno interface so far (Post Sacha Chua)--L0--C0--June 23, 2021 12:00 AM

Andrea: How (simple is) to install a Clojure tree-sitter grammar and use it from Emacs

-1:-- How (simple is) to install a Clojure tree-sitter grammar and use it from Emacs (Post)--L0--C0--June 22, 2021 12:00 AM

Andrea: Back to Emacs while in Nyxt: how to edit the web in a Lispy editor

Example of going back and forth from Emacs to Nyxt
-1:-- Back to Emacs while in Nyxt: how to edit the web in a Lispy editor (Post)--L0--C0--June 22, 2021 12:00 AM

Tassilo Horn: Using Eldoc with Magit

I use Magit for almost all my interactions with Git and love it. As many have said before, Magit is invaluable and by far the best Git porcelaine I've ever used.

-1:-- Using Eldoc with Magit (Post)--L0--C0--June 21, 2021 08:10 PM

Christian Tietze: Trash File from Emacs with Put-Back Enabled on macOS

I’ve enabled using the macOS system trash in Emacs to feel safer experimenting with dired and other file manipulation functions in Emacs.

This enables using the trash by default:

(setq delete-by-moving-to-trash t)

When this is enabled, move-file-to-trash is used to delete files. The manual reveals what’s affected, but only the function API docs from within Emacs tell the whole story (and are super hard to search online for whatever reason):

If the function system-move-file-to-trash is defined, call it with FILENAME as an argument.

Otherwise, if trash-directory is non-nil, move FILENAME to that directory.

Otherwise, trash FILENAME using the freedesktop.org conventions, like the GNOME, KDE and XFCE desktop environments. Emacs moves files only to “home trash”, ignoring per-volume trashcans.

So by default, the delete-by-moving-to-trash variable enablement does nothing.

Set the user-default trash path to make deletion move a file to trash:

(setq trash-directory "~/.Trash")  ;; fallback for `move-file-to-trash'

With this, all files are moved into the trash, but you cannot use the macOS ‘Put Back’ functionality. I expected an extended file attribute to control this, but it’s something else, apparently. Reading the attributes of files trashed by Finder doesn’t reveal anything.

But there’s a command-line utility called trash that can be told to use Finder’s functionality to move a file to the trash and enable put-back.

Successful trashing of a file, see the message in the minibuffer

So if we revisit the function docs, it says trash-directory is just a fall-back. The first attempt is made using the function system-move-file-to-trash if it exists. If we declare that function, we can use it to use the trash CLI app; and for good measure, we’ll limit it to macOS:

(when (memq window-system '(mac ns))
  (defun system-move-file-to-trash (path)
    "Moves file at PATH to the macOS Trash according to `move-file-to-trash' convention.

Relies on the command-line utility 'trash' to be installed.
Get it from:  <http://hasseg.org/trash/>"
    (shell-command (concat "trash -vF \"" path "\""
                           "| sed -e 's/^/Trashed: /'")
                   nil ;; Name of output buffer
                   "*Trash Error Buffer*")))

This just forwards the file path to the trash CLI utility and then prepends a string to format the output; trash -F uses the Finder trash functionality; trash -v returns the path name of the trashed file on success; and with the sed replacement, we get "Trashed: /path/to/file.txt". This output is printed in the minibuffer after deletion as you see in the screenshot.

References:


Receive Christian’s new posts via email

-1:-- Trash File from Emacs with Put-Back Enabled on macOS (Post)--L0--C0--June 21, 2021 06:55 PM

Irreal: Demonstration of Dired Video

Like most Emacs users, I consider Dired the way of dealing with file operations. That means a lot coming from me because I’m an old timer used to dealing with such things from the command line. But even after a lifetime of learning and using the command line utilities, I find that Dired is simply better and easier. And I don’t have to leave Emacs.

Over at the Emacs Elements YouTube Channel, there’s a video demonstration of configuring and using Dired. As the video shows, there is a lot of configuration possible with Dired. You can hide files, turn off confirmation prompts, change the format of the output, and many other things. With a configuration adopted to your workflow, your Dired operations can have significantly less friction.

The other point of the video is how powerful Dired is. You can perform almost any file operation with a simple, short command. In most cases, you don’t have to remember any syntax because Dired prompts you for any information it needs. To me, one of the most magical of Dired tricks is that you can make the buffer writable and any changes you make will be applied to the files affected.

Even if you’re an experienced Dired user, the video may teach you something new. It’s just short of 18 minutes so plan accordingly.

-1:-- Demonstration of Dired Video (Post jcs)--L0--C0--June 21, 2021 03:23 PM

Alvaro Ramirez: Previewing SwiftUI layouts in Emacs (revisited)

20 June 2021 Previewing SwiftUI layouts in Emacs (revisited)

Back in May 2020, I shared a snippet to extend ob-swift to preview SwiftUI layouts using Emacs org blocks.

ob-swiftui.gif

When I say extend, I didn't quite modify ob-swift itself, but rather advised org-babel-execute:swift to modify its behavior at runtime.

Fast-forward to June 2021 and Scott Nicholes reminded me there's still interest in org babel SwiftUI support. ob-swift seems a little inactive, but no worries there. The package offers great general-purpose Swift support. On the other hand, SwiftUI previews can likely live as a single-purpose package all on its own… and so I set off to bundle the rendering functionality into a new ob-swiftui package.

Luckily, org babel's documentation has a straightforward section to help you develop support for new babel languages. They simplified things by offering template.el, which serves as the foundation for your language implementation. For the most part, it's a matter of searching, replacing strings, and removing the bits you don't need.

The elisp core of ob-swiftui is fairly simple. It expands the org block body, inserts the expanded body into a temporary buffer, and finally feeds the code to the Swift toolchain for execution.

(defun  org-babel-execute:swiftui (body params)
   "Execute a block of SwiftUI code in BODY with org-babel header PARAMS.
 This function is called by ` org-babel-execute-src-block '"
  (message  "executing SwiftUI source code block")
  (with-temp-buffer
    (insert (ob-swiftui--expand-body body params))
    (shell-command-on-region
     (point-min)
     (point-max)
      "swift -" nil 't)
    (buffer-string)))

The expansion in ob-swiftui–expand-body is a little more interesting. It decorates the block's body, so it can become a fully functional and stand-alone SwiftUI macOS app. If you're familiar with Swift and SwiftUI, the code should be fairly self-explanatory.

From an org babel's perspective, the expanded code is executed whenever we press C-c C-c (or M-x org-ctrl-c-ctrl-c) within the block itself.

It's worthing mentioning that our new implementation supports two babel header arguments (results and view). Both extracted from params using map-elt and replaced in the expanded Swift code to enable/disable snapshotting or explicitly setting a SwiftUI root view.

(defun  ob-swiftui--expand-body (body params)
   "Expand BODY according to PARAMS and PROCESSED-PARAMS, return the expanded body."
  (let ((write-to-file (member  "file" (map-elt params  :result-params)))
        (root-view (when (and (map-elt params  :view)
                              (not (string-equal (map-elt params  :view)  "none")))
                     (map-elt params  :view))))
    (format
      "
 // Swift snippet heavily based on Chris Eidhof's code at:
 // https://gist.github.com/chriseidhof/26768f0b63fa3cdf8b46821e099df5ff

 import Cocoa
 import SwiftUI
 import Foundation

 let screenshotURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString + \".png\")
 let preview = %s

 // Body to run.
 %s

 extension NSApplication {
   public func run(_ view: V) {
     let appDelegate = AppDelegate(view)
     NSApp.setActivationPolicy(.regular)
     mainMenu = customMenu
     delegate = appDelegate
     run()
   }

   public func run(@ViewBuilder view: () -> V) {
     let appDelegate = AppDelegate(view())
     NSApp.setActivationPolicy(.regular)
     mainMenu = customMenu
     delegate = appDelegate
     run()
   }
 }

 extension NSApplication {
   var customMenu: NSMenu {
     let appMenu = NSMenuItem()
     appMenu.submenu = NSMenu()

     let quitItem = NSMenuItem(
       title: \"Quit  \ (ProcessInfo.processInfo.processName)\",
       action: #selector(NSApplication.terminate(_:)), keyEquivalent: \"q\")
     quitItem.keyEquivalentModifierMask = []
     appMenu.submenu?.addItem(quitItem)

     let mainMenu = NSMenu(title: \"Main Menu\")
     mainMenu.addItem(appMenu)
     return mainMenu
   }
 }

 class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
   var window = NSWindow(
     contentRect: NSRect(x: 0, y: 0, width: 414 * 0.2, height: 896 * 0.2),
     styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
     backing: .buffered, defer: false)

   var contentView: V

   init(_ contentView: V) {
     self.contentView = contentView
   }

   func applicationDidFinishLaunching(_ notification: Notification) {
     window.delegate = self
     window.center()
     window.contentView = NSHostingView(rootView: contentView)
     window.makeKeyAndOrderFront(nil)

     if preview {
       screenshot(view: window.contentView!, saveTo: screenshotURL)
       // Write path (without newline) so org babel can parse it.
       print(screenshotURL.path, terminator: \"\")
       NSApplication.shared.terminate(self)
       return
     }

     window.title = \"press q to exit\"
     window.setFrameAutosaveName(\"Main Window\")
     NSApp.activate(ignoringOtherApps: true)
   }
 }

 func screenshot(view: NSView, saveTo fileURL: URL) {
   let rep = view.bitmapImageRepForCachingDisplay(in: view.bounds)!
   view.cacheDisplay(in: view.bounds, to: rep)
   let pngData = rep.representation(using: .png, properties: [:])
   try! pngData?.write(to: fileURL)
 }

 // Additional view definitions.
 %s
 "
     (if write-to-file
          "true"
        "false")
     (if root-view
         (format  "NSApplication.shared.run(%s())" root-view)
       (format  "NSApplication.shared.run {%s}" body))
     (if root-view
         body
        ""))))

For rendering inline SwiftUI previews in Emacs, we rely on NSView's bitmapImageRepForCachingDisplay to capture an image snapshot. We write its output to a temporary file and piggyback-ride off org babel's :results file header argument to automatically render the image inline.

Here's ob-swiftui inline rendering in action:

obswiftui50.gif

When rendering SwiftUI externally, we're effectively running and interacting with the generated macOS app itself.

ob-swiftui-window.gif

The two snippets give a general sense of what's needed to enable org babel to handle SwiftUI source blocks. Having said that, the full source and setup instructions are both available on github.

ob-swiftui is now available on melpa.

-1:-- Previewing SwiftUI layouts in Emacs (revisited) (Post)--L0--C0--June 21, 2021 08:44 AM

Sacha Chua: 2021-06-21 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar and emacs-devel.

-1:-- 2021-06-21 Emacs news (Post Sacha Chua)--L0--C0--June 21, 2021 12:00 AM

Irreal: YASnippet in Eshell

Álvaro Ramírez has a great, quick tip for Eshell users. Actually, it’s a good tip for any shell you’re using in Emacs. Ramírez starts by revisiting level 4 from Vivek Haldar’s classic post on the levels of Emacs proficiency. I wrote about my own experience with his post here.

Level 4 is about discovering the shell within Emacs. In Ramírez’s case that shell is Eshell but he had trouble remembering the syntax for some of the commands that he used infrequently such as for. Then he remembered that Eshell is just another Emacs buffer so he could use YASnippets to complete the commands for him. Thus for the for he simply typed for and Tab to get a for template that he could fill in by tabbing through the fields.

Ramírez has a link to a YASnippet tutorial if you aren’t already familiar with writing the templates. As I said, the same idea would work with other shells because they, too, are just Emacs buffers. It’s a small thing but it can eliminate one piece of annoying friction in your workflow.

-1:-- YASnippet in Eshell (Post jcs)--L0--C0--June 20, 2021 06:14 PM

Alvaro Ramirez: Blurring the lines between shell and editor

19 June 2021 Blurring the lines between shell and editor

yas-eshell.gif

I recently tweeted that Vivek Haldar's 10-year old post rings true today just the same. He writes about the levels of Emacs proficiency. All 6 levels are insightful in their own right, but for the sake of this post, let's quote an extract from level 4. Shell inside Emacs:

"And then, you learned about it: M-x shell.

It was all just text. Why did you need another application for it? Why should only the shell prompt be editable? Why can’t I move my cursor up a few lines to where the last command spewed out its results? All these problems simply disappear when your shell (or shells) simply becomes another Emacs buffer, upon which all of the text manipulation power of Emacs can be brought to bear."

In other words, we aren't merely removing shell restrictions, but opening up possibilities…

Take Emacs eshell looping, for example. I use it so infrequently, I could never remember eshell's syntax. I would refer back to EmacsWiki's Eshell For Loop or Mastering Emacs's Mastering Eshell comments for a reminder. It finally dawned on me. I don't need to internalize this eshell syntax. I have YASnippet available like any other buffer. I could just type "for" and let YASnippet do the rest for me.

yas-for.gif

All I need is a tiny YASnippet:

#name : Eshell for loop
#key : for
# --
for f in ${1:*} { ${2:echo} "$f"; $3} $0

Want a gentle and succinct YASnippet intro? Check out Jake's YASnippet introduction video.

UPDATE: Bash, Zsh, and others welcome

If you're a shell-mode user, YASnippet would have you covered in your favorite shell. The expansion snippet can be modified to a Bash equivalent, giving us the same benefit. We type "for" and let YASnippet expand and hop over arguments. Here's a Bash equivalent emphasizing the hopping a little more:

yasbash.gif

#name : bash for loop
#key : for
# --
for f in ${1:*}; do ${2:echo} $f; done $0

ps. Looks like vterm, term, or ansi-term work too. See Shane Mulligan's post: Use YASnippets in term and vterm in emacs.

-1:-- Blurring the lines between shell and editor (Post)--L0--C0--June 18, 2021 11:00 PM

Irreal: Red Meat Friday: Emacs or VS Code

For those of you wondering if you should adopt Emacs or VS Code, here’s your answer. You’re welcome.

-1:-- Red Meat Friday: Emacs or VS Code (Post jcs)--L0--C0--June 18, 2021 05:33 PM

Sacha Chua: Using inotify to add Plover Clippy suggestions into Emacs

Update 2021-06-19: Changed to a vertical layout, added extra notes.

I don't have a lot of screen space on my laptop, so I don't usually have the Plover suggestion window open as I type. I came up with a Plover plugin to let me flash the last Plover Clippy suggestion as a temporary notification. It went by too quickly, though, so I wrote something that uses inotify to monitor the clippy.txt log and put it an Emacs buffer instead. It results in text like this:

Clippy
KHREUP PEU
added
ATD
Extra notes go here
(defvar my/clippy-recent-suggestions nil "Recent suggestions, limited by `my/clippy-recent-suggestions-limit`.")
(defvar my/clippy-recent-suggestions-limit 3 "Keep this many suggestions.")
(defvar my/clippy-extra-notes nil "Extra notes to add at the end.")
(defun my/clippy-last ()
  (let ((value (string-trim (shell-command-to-string "tail -1 ~/.config/plover/clippy.txt | cut -c 23-"))))
    (when (string-match "^\\(.*?\\)[ \t]+|| .*? -> \\(.+\\)" value)
      (cons (match-string 1 value) (match-string 2 value)))))

(defun my/clippy-show (&rest _)
  (interactive)
  (with-current-buffer (get-buffer-create "*Clippy*")
    (let ((last (my/clippy-last)))
      (unless (member last my/clippy-recent-suggestions)
        (setq my/clippy-recent-suggestions (seq-take (cons last my/clippy-recent-suggestions) my/clippy-recent-suggestions-limit)))
      (erase-buffer)
      (insert (mapconcat (lambda (o) (format "%s\n%s\n" (car o) (cdr o))) my/clippy-recent-suggestions "")
              (or my/clippy-extra-notes "")))))

(defvar my/clippy-monitor nil)
(defun my/clippy-toggle-monitor ()
  (interactive)
  (if (inotify-valid-p my/clippy-monitor)
      (progn
        (message "Turning off")
        (inotify-rm-watch my/clippy-monitor))
    (message "Turning on")
    (setq my/clippy-monitor
          (inotify-add-watch
           (expand-file-name "~/.config/plover/clippy.txt") 'modify
           #'my/clippy-show))))

(defun my/clippy-org-subtree-to-notes ()
  "Copy this subtree's text to `my/clippy-extra-notes'."
  (interactive)
  (save-excursion
    (unless (org-at-heading-p) (org-previous-visible-heading 1))
    (setq my/clippy-extra-notes
          (substring-no-properties
           (org-agenda-get-some-entry-text
            (point-marker)
            most-positive-fixnum)))
    (my/clippy-show)))

(defun my/clippy-add-note (string)
  (interactive "MNote: ")
  (setq my/clippy-extra-notes (concat (string-trim string) "\n" (or my/clippy-extra-notes "")))
  (my/clippy-show))
This is part of my Emacs configuration.
-1:-- Using inotify to add Plover Clippy suggestions into Emacs (Post Sacha Chua)--L0--C0--June 18, 2021 12:00 AM

Andrea: Moldable Emacs: vision, basic concepts and design

-1:-- Moldable Emacs: vision, basic concepts and design (Post)--L0--C0--June 18, 2021 12:00 AM

Andrea: Moldable Emacs: how to explore JSON via Elisp

-1:-- Moldable Emacs: how to explore JSON via Elisp (Post)--L0--C0--June 18, 2021 12:00 AM

James Cash: Some Emacs advice

First, A Note

Last year, I experimented with making some videos about Emacs. I really enjoyed the process and the very nice feedback I received, but – obviously – I haven’t made one in a while.

There are a few reasons for that. The first is that I’ve been quite busy – I moved at the end of the year and work on various projects has kept me quite occupied. That’s more of an excuse though.

The bigger reason is that I realized how much YouTube as a platform made me unhappy. I drastically cut back on my consumption, now only watching a few channels via RSS & youtube-dl (in Emacs via elfeed, of course). It felt disingenuous to me to then continue to create content for others on a platform that I don’t want to use.

I’ve considered looking in to putting videos on Peertube or something like that, but that would certainly be more effort, which brings me to the third reason: I personally don’t really like consuming content, particularly technical content, by video. I much prefer to read things and it’s hard to motivate myself to make content that I wouldn’t really want to consume myself.

Hopefully that makes sense; now, on to the fun part.

Advising Emacs

As I’ve mentioned in some of my previous Emacs posts & videos, there are many ways to customize the behaviour of existing packages in Emacs. Sometimes you’re lucky enough that the thing you want to change is just an option and you can simply set a variable – or even, if the setting was defined with defcustom, you can use M-x customize to use a nice little GUI to change the setting.

If that’s not an option, or doesn’t fit what you want to do, “hooks” are next level of customization. The idea of hooks is that you can register functions to be called at a particular time. In the library, this is as simple as defining a variable ((defvar my-hook nil) or (defcustom my-hook nil :type 'hook)) and then running it at an appropriate time with (run-hook 'my-hook). Hooks are very powerful, since you can run arbitrary code, but you’re limited to the hooks that the library author has put in place – if there’s another time or place you want to run some of your own code and there isn’t already a hook there, you’re out of luck.

Enter “advice” – a way to have some of your own code run whenever a particular function gets invoked.

History

The concept of advice, as far as I know, comes from the Common Lisp Object System (CLOS) and was briefly popular in the Java world under the term “Aspect Oriented Programming”. It is a mechanism for not just overriding, but adding some additional functionality before, after, or around an existing function. In Emacs terms, you could almost think of it as every existing function not only having a “before” and “after” hook, but with functions in those hooks being able to change the arguments or return value, or even not call the original function.

There are two different ways of using advice in Emacs – defadvice, from the original advice.el (written in 1993) and the newer advice-add from nadvice.el (added in 2012). The newer library is much simpler in both functionality and implementation (advice.el is 3262 lines long, while nadvice.el is less than a fifth as long, at a compact 592 lines) and is generally recommended unless you need some of the advanced features of defadvice.

Using Advice

We’ll take a quick like at the older defadvice first; just the most basic features, since it has a lot of flexibility, but I never use it.

The basic usage looks like:

(defadvice function-to-be-advised (type-of-advice name-of-this-advice)
  ;; code
  )

Where type-of-advice is before, after, or around. There are many other options and ways of using defadvice that you can learn about by reading the documentation.

For example, let’s say you want to make it easier to create new entries in org-mode lists, so you want to be able to hit M-RET anywhere in the current line and have it create a new entry below, instead of splitting the current entry. One way to do this with advice would be as follows:

(defadvice org-meta-return (before eol-before)
  (org-end-of-line))
(ad-activate 'org-meta-return)

;; or, equivalently

(defadvice org-meta-return (before eol-before-2 activate)
  (org-end-of-line))

Here’s the equivalent using advice-add, something I actually have in my emacs configuration:

(advice-add 'org-meta-return :before #'org-end-of-line)

advice-add is a bit simpler and has fewer options. It has some optional arguments, but you mostly just need the three required ones here: A symbol naming the function to advise, the type of advice, and the advising function. There are actualy ten different types of advice you can use (see the help for add-function for a list and an explanation of what they all are), but in practice one mainly just uses four: :before, :after, :override and :around.

:before and :after are pretty straight-forward – they get called either before or after the original function is called and will recieve the same arguments as the original function.

:override simply replaces the original function with the advising one.

:around is more flexible – it recieves as argument the original function itself and all the arguments it was called with, so you can essentially do whatever you want: Do things before, then call the function but change some arguments, modify the return value, and do something after. Sky’s the limit!

The other options are essentially convenience wrappers to make some simple patterns of :around advice simpler to implement.

The inverse of advice-add is advice-remove. You can either use it to remove some advice you’ve added to a function, making it a much cleaner process if you want to only temporarily or conditionally change a function’s definition.

Examples of Use

Finally, let’s have a look at some practical uses of advice. I have a couple uses of advice in my Emacs config. All of them are using the new advice-add – I haven’t had anything arise thus far that would require defadvice.

Tracking the Active Window

;; variable to keep track of window
(defvar cogent-line-selected-window (frame-selected-window))

;; set current
(defun cogent-line-set-selected-window (&rest _args)
  (when (not (minibuffer-window-active-p (frame-selected-window)))
    (setq cogent-line-selected-window (frame-selected-window))
    (force-mode-line-update)))

;; unset current
(defun cogent-line-unset-selected-window ()
  (setq cogent-line-selected-window nil)
  (force-mode-line-update))

;; use hooks when we can
(add-hook 'window-configuration-change-hook #'cogent-line-set-selected-window)
(add-hook 'focus-in-hook #'cogent-line-set-selected-window)
(add-hook 'focus-out-hook #'cogent-line-unset-selected-window)

;; when no hooks exist, advice!
(advice-add 'handle-switch-frame :after #'cogent-line-set-selected-window)
(advice-add 'select-window :after #'cogent-line-set-selected-window)

Here’s a combination of using hooks when we can and advice when no appropriate hook exists. This bit of functionality was taken from the modeline library from Spacemacs; it keeps track of which window has been selected, which is then used to render the modeline different in the focused window versus the others.

In this case, we use the simple :after advice to just run our function, cogent-line-set-selected-window after handle-switch-frame and select-window are called.

Making LSP “Peek” in a Posframe

via https://github.com/emacs-lsp/lsp-ui/issues/441:

(defun lsp-ui-peek--peek-display (src1 src2)
  (-let* ((win-width (frame-width))
          (lsp-ui-peek-list-width (/ (frame-width) 2))
          (string (-some--> (-zip-fill "" src1 src2)
                    (--map (lsp-ui-peek--adjust win-width it) it)
                    (-map-indexed 'lsp-ui-peek--make-line it)
                    (-concat it (lsp-ui-peek--make-footer)))))
    (setq lsp-ui-peek--buffer (get-buffer-create "*lsp-peek--buffer*"))
    (posframe-show lsp-ui-peek--buffer
                   :string (mapconcat 'identity string "")
                   :min-width (frame-width)
                   :poshandler #'posframe-poshandler-frame-center)))

(defun lsp-ui-peek--peek-destroy ()
  (when (and (boundp 'lsp-ui-peek--buffer)
             (bufferp lsp-ui-peek--buffer))
    (posframe-delete lsp-ui-peek--buffer))
  (setq lsp-ui-peek--buffer nil
        lsp-ui-peek--last-xref nil)
  (set-window-start (get-buffer-window) lsp-ui-peek--win-start))

(advice-add #'lsp-ui-peek--peek-new :override #'lsp-ui-peek--peek-display)
(advice-add #'lsp-ui-peek--peek-hide :override #'lsp-ui-peek--peek-destroy))

This uses advice to override the existing lsp-ui-peek--display and lsp-ui-peek--destroy functions to show in a posframe instead of the normal way they do. Pretty straight-forward; unlike just re-defining the functions though, using advise means we could also use advice-remove to restore the original behaviour without any shenanigans.

Making My Own Little Theming System

(defvar cogent/theme-hooks nil "((theme-id . function) ...)")

(defun cogent/load-theme-advice (f theme-id &optional no-confirm no-enable &rest args) ;
  "Enhance `load-theme' by disabling other enabled themes & calling hooks"
  (unless no-enable ;
    (mapc #'disable-theme custom-enabled-themes))
  (prog1
      (apply f theme-id no-confirm no-enable args) ;
    (unless no-enable ;
      (pcase (assq theme-id cogent/theme-hooks)
        (`(,_ . ,f) (funcall f))))))

(advice-add 'load-theme :around #'cogent/load-theme-advice) ;
  • advice-add is installing our advising function – cogent/load-theme-advice – “around” the load-theme function. Because we specifiy :around, our function will receive original function as an argument, plus all the arguments it receives, and it will be our responsibility to invoke it
  • cogent/load-theme-advice is the function that is getting called in place of the advised function, load-theme
  • (unless …) some code we’re running before we call the original function; in this case, disabling the existing themes (by default, load-theme is additive, which can be somewhat confusing)
  • (apply f …) is invoking the advised function. In this case, we’re just passing in the same arguments that we received, but we have the flexibility to do whatever we want with it.
  • (unless … (pcase …)) after we run the original function, we run this code to run my own “hook” functions for the particular theme that’s been loaded
-1:-- Some Emacs advice (Post)--L0--C0--June 17, 2021 12:00 AM

Irreal: Configuring Emacs as a Go IDE

Bhavin Gandhi has a post that may be useful to those who want to use Emacs for Go programming. It’s easy to get things like syntax highlighting but for the full IDE experience you need the Go LSP server. Gandhi’s post, How to setup Emacs LSP Mode for Go, explains how to set Emacs up as an IDE for Go.

It is not, of course, all that difficult but as usual with such things it can be fiddly. Gandhi’s post is a nice go by to get up and running is short order. Once you have things working, you can address optimizing your configuration to suit your preferred workflow.

-1:-- Configuring Emacs as a Go IDE (Post jcs)--L0--C0--June 16, 2021 04:15 PM

Philip K.: Lesser known functionalities in core Emacs

Lesser known functionalities in core Emacs

Lesser known functionalities in core Emacs

While reading EmacsWiki or browsing the Emacs Source Code itself, one often encounters libraries and packages that are unrightfully underappreciated.

Here I’d like to briefly collect a few packages and libraries from core Emacs that I could be useful.

M-x find-library autoinsert RET (1985)

The main command here is auto-insert, with a dash.

This command can easily be confused with abbrev or yasnippet. But both of these are meant expand abbrevations (e.g. “lag” to “ladies and gentlemen”) or interactive snippets (e.g. “err” to an error handling block in Go).

Auto-insert is instead intended to be used when a new file is created: Create an empty HTML buffer, and then run M-x auto-insert – after querying the title a skeleton of a HTML file is generated.

This can even be added to a mode hook:

(add-hook 'html-mode-hook #'auto-insert)

Emacs will make sure that you actual want auto-insertion to take place, and then proceed like above.

All in all, it is a useful mode, especially for well structured file formats that might be hard to memorize. The main reason I think auto-insert is not talked about that much is that not that many major modes are supported out-of-the-box. The user option auto-insert-alist, that defines the behaviour for a file type only has 13 entries, for these file types:

  1. C/C++ (header and main program)
  2. Makefiles
  3. HTML
  4. LaTeX and BibTeX
  5. Shell Scripts
  6. Ada
  7. Man pages (not mdoc)
  8. .dir-locals.el (useful but replaceable using add-dir-local-variable)
  9. Emacs Lisp files
  10. TeXinfo

Adding support for more modes, both for modes in Emacs itself and third-party code would probably help to make this more useful.

M-x find-library htmlfontify RET (2002)

Most people use htmlize, I suspect mainly because Org-mode uses it too.

html-fontify is basically the same story, just built in and it ignores the current theme. While maybe a disadvantage, I personally was more frequently annoyed when exporting a .org file while using a dark theme, and only later noticing that the resulting HTML is hard to read.

The main use I have for this library is exporting the public part of my personal configuration to my website, using this command:

(defun local/export-config ()
  "Export this configuration as HTML to personal WWW directory."
  (interactive)
  (require 'htmlfontify)
  (let ((hfy-default-face-def '((t :family "monospace")))
        (hfy-display-class '((background . light)
                             (class . color)))
        (hfy-optimizations nil))
    (with-current-buffer (find-file-noselect (locate-user-emacs-file "init.el"))
      (with-current-buffer (htmlfontify-buffer user-emacs-directory "init.el")
        (write-region (point-min) (point-max)
                      "~/Code/web/www/data/files/init.el.html")
        (kill-buffer (current-buffer))))))

I am not sure why this library is overlooked. I had to guess, I’d say it is because it is unwieldy to use, and the internals a bit messy.

M-x find-library package-x RET (2007)

package.el is widely known, and the de-facto default for distributing and installing third-party packages. Recently I stumbled upon its forgotten relative, package-x.el. Instead of downloading and maintaining packages, it is mean to be used for creating your own package archives.

From what I see, it was created by Tom Tromey to maintain the original ELPA.

It’s basic use is to set package-archive-upload-base to a TRAMP directory, usually pointing to a directory hosted by a web server, and then uploading packages using package-upload-file. You can either upload a singular file, or a tar archive when a package consists of multiple files. It should be possible to extend this to directories, along the lines of invoking package-install-from-buffer in a Dired buffer.

One of the reasons this feature is not talked about a lot is that it is somewhat inconvenient to use: Only a single mirror can be specified, and some of the functionality such as creating maintaining log and RSS feed of new versions is not accessible without writing one’s own elisp – in other words nothing a patch or two couldn’t fix.

M-x find-library type-break RET (1994)

This is for anyone who wants to use something like the Pomodoro Technique1 to make sure they don’t work for too long.

There is one caveat – to quote the commentary:

This package relies on the assumption that you live entirely in Emacs, as the author does. If that’s not the case for you (e.g. you often suspend Emacs or work in other windows) then this won’t help very much;

All you need to do to use this is enable type-break-mode and optionally configure type-break-interval and type-break-good-rest-interval (the defaults are 60 minute sessions and 10 minute breaks).

It does the job, though it’s interface can be annoying with the warnings it displays in the echo area, and the breaks are started manually, by default. That might be one of the reasons this library is not talked about that much?

M-x find-library strokes RET (1997)

Interesting and perhaps controversial for mouse-haters. The idea is to configure a system, where particular movements of the mouse trigger the invocation of some command – just like how every key press runs a command.

The details on how to use this is explained by running the command strokes-help, the main gist is:

  1. Activate strokes-mode
  2. Define and bind a stroke with strokes-global-set-stroke
  3. Invoke the stroke by pressing shift and the middle-mouse button

The last part might be an issue for laptop users, or people with bad mice. The main issue with this feature is probably the flimsily mouse-support in Emacs, for anything beyond simple clicking and dragging. Nevertheless, strokes can recognise the patters one draws and manages to associate similar mouse-movements.

A lot of people (falsely) shun using a mouse with more advanced editors, as a matter of principle. While I disagree with the insistence on doing everything with the keyboard, I do understand why most people probably don’t want to draw an snake or a triangle with their mouse too often.

M-x find-library mode-local RET (2004)

Finally a interesting library for configurations. More precisely, the macro setq-mode-local allows the user to configure variables only for certain major modes. E.g.

(setq-mode-local go-mode compile-command "go build")

would make sure that for every go-mode buffer, the variable compile-command is initialise properly. The alternative would be something like

(add-hook 'go-mode-hook
	  (lambda ()
	    (setq-local compile-command "go-build")))

But there is no reason to stick to inconvenience and repeat patterns with Lisp.

This macro is probably most interesting for people with simple configurations2. I could imagine it being more popular, but am also not sure if there are any issues that should be mentioned.


These have been a few packages I think deserve some more attention than they currently receive. Between the well known and impressive features already built-in to Emacs (Org, Calc, Gnus, …) and those I don’t even know how to use (MPC, SES), this posts means to show that there is still space to appreciate what Emacs has to offer out of the box.


  1. From my understanding type-break is somewhat simpler.↩︎

  2. I have unknowingly replicated the same feature in my own package via the more general :local-set macro.↩︎

-1:-- Lesser known functionalities in core Emacs (Post)--L0--C0--June 15, 2021 09:12 PM

Marcin Borkowski: New behavior of yank-pop

I have another short Emacs tip for today. Quite recently (well, in November 2020, which – given the scale of Emacs history – is pretty recently, but I only learned that a few weeks ago or so) the yank-pop command (bound to M-y by default) acquired a very cool new feature. Its main use is (and has been for decades) to cycle through the kill-ring right after the yank command (C-y). It was one of the things that made Emacs a sophisticated editor and not a toy. Of course, M-y did nothing if pressed not immediately after C-y or another M-y, since what it could do then? Well, it’s obvious, isn’t it?
-1:-- New behavior of yank-pop (Post)--L0--C0--June 14, 2021 07:35 PM

Irreal: More on the Why of Emacs

Jeremy Friesen has a bit more to say on why he uses Emacs. Last time we stopped by his blog, he explained how he molds Emacs to enhance his blogging workflow. This time he’s answering a question on the Emacs/reddit as to whether there’s any reason to prefer Emacs to a “modern looking” IDE such as VS Code. I generally stop reading when I see terms like “modern looking” but Friesen is more patient than I and posted a thoughtful response that explained why he thought there was.

Friesen’s reasons boil down to synergy. He says he uses Emacs for 3 main purposes:

  • Coding
  • Blogging
  • Note Taking

and observes that when he implements something—a new function or key binding, say—to improve one area, he automatically gets gains in the other areas as well.

He expands on his reply a bit in his blog post. Using Emacs means fewer context switches and the opportunity to master Emacs. He’s found that the lessons he’s learned extend beyond Emacs. He sums it up by saying that Emacs helps him get better at dealing with digital information.

I don’t know for sure but I’d guess that most people who have taken the time to really learn Emacs have experienced that. I know that Emacs has made me really good at wrangling text and therefore with dealing with digital data in general.

-1:-- More on the Why of Emacs (Post jcs)--L0--C0--June 14, 2021 04:56 PM

TAONAW: How to make all-day events stand out in org-mode

Last week, I managed to colorize all-day events on my agenda in a unique color that stands out from the rest. A bit of a hack and a workaround, this is a highly specific solution to a challenge I’ve been trying to solve for about a year. In this post, I will explain why I need this, and how I did it.

This picture will show you what the result looks like on my agenda:

A part of my agenda showing the main colors in use and their respective header type. Notice the color key in the brown square

All-day events such as holidays or vacations show in bold yellow (color 3). Scheduled events show in green (color 2). Appointment and meetings are in sky blue (color 1).

Background

My use-case is important to understand the “why” here. There are three time-related categories I use in org-mode for different purposes.

  • Timed events (color 1) are non-flexible. These are events that need to happen at a certain time. In the image, you can see I have a Dr. Appointment in 5 days, a Family Zoom at 8:15, and that I went to the gym at 13:00 (1PM). The gym “appointment” became an appointment after the act. I wanted to mark down to myself what time I went there1.

  • Scheduled events (color 2) are flexible. I plan to start working on an event around a certain time in the day. I don’t treat these as hard time stamps which I have to keep. Scheduled events tend to get pushed around regularly, as I constantly prioritize tasks and break them down to sub-tasks, which makes the parent-task a project.

  • All-day events (color 3) this is what this post is about. Until very recently, I did not have a third color for this third category. Holidays, vacations, sick days, birthdays, etc - these were all colored in color 1, the same as timed events. This created confusion because such events are usually just indications, they are not meant to work the same way as scheduled and timed events. I could use a timestamp without a specific hour to create these, but the difference was always too subtle.

A couple of weeks ago, I stumbled upon an option to add Emacs' holidays into org-agenda using %%(org-calendar-holiday). By default, this shows all holidays Emacs has included, which is quite a few. For me, this is good, as I want to be aware of additional events and holidays relate to other religions which affect people I work with.

Conditions

Now that I reminded you how I work with org-mode and why I wanted a third color on my agenda, we can dive into the how.

Here are the conditions I have to make this happen:

  1. Integrating Emacs' diary with diary-block entries
  2. Modifying the theme to customize headers created by Emacs' diary
  3. Nesting the diary-blocks under timeless headers (tags are OK)
  4. Changing the default in agenda, so events without a specific time of day (hour) show at the top (org-mode’s default is to show them at the bottom)

Emacs' diary was introduced before org-mode, and org-mode can integrate certain diary features. Here, we will be dealing with special diary entries that can be implemented directly into org files as-is.

A quick look through the manual entry above will show you what a diary-block is, and its timestamp format (which is different than org-mode format). Fortunately for us org-mode users, entering a special diary entry also works from org-mode’s agenda. Just hover over the day you want and use the sexp entries as explained in the manual above: i a for an anniversary (like a birthday, repeats every year), i b for block (several days, as in a case of a vacation) and i-c for cyclic event’s, Diary’s way to repeat a certain even every X number of days. That last is not as useful for me, because all events that cycle this way that I can think of fall within the timed event category for me, as explained in the first part.

So, in a way, creating all-day events in org-mode has its own built-in “capture template”.

Consider the “Demo for post” in the image above which I created for this demonstration. To create it, I hovered over Saturday. I hit i b to create a block. A diary window popped up and automatically create the diary timestamp range for that one day: %%(diary-block 6 12 2021 6 12 2021). I then jumped to the beginning of the line (C-a), activated the marker (C-spc), jumped to the end of the line (C-e), and killed (C-k) the date. I then opened my weekly.org file, which contains weekly events, and under a header I made for that specific test event (we will get to that in a minute), I planted that line.

Even better: You can activate your marker on the agenda and start highlighting days from the first day you want to the last. Creating a diary-block event at this point with i b will create a range of these days automatically for you.

With a bit of tweaking, I can probably create a simple macro to capture the different strokes and make this whole process automatic. For now I do it manually.

The next part of the process requires that we customize our Emacs' theme to get the color we want for all-day events. In the image above, this is color 3.

Customizing a theme in Emacs is not too hard if you introduce little changes, like tweaking with a specific color. You can read how I did this before. In our case, the diary events in the agenda are marked differently than their neighboring events created by org-mode. You can see this when you place the mark over a certain heading and call C-x customize-face (no need to change anything yet, just notice how Emacs calls the header in the parenthesis).

Diary events are called “org-agenda-calendar-sexp”. This means that in the theme, we can add a line that defines this face and gives it a certain color. In my theme, I’ve added this line:

(org-agenda-calendar-sexp :foreground yellow :weight bold)

My theme defines the color “yellow” and the weight “bold” elsewhere at the beginning of the theme. Yours probably does something similar. Just look to see how the other org-mode related theme adjustments are named and follow suit.

Next, we want to be able to work with these special events. Since they are not created in org-mode, they do not have a header by default, so we can’t do anything with them like adding tags, link back to them in the journal, or organize and collapse them. To fix this, I created a header for each one of the diary events. For example, say I create a header called “Alex” in my routine.org file. Under the header, I will nest two %%(diary-anniversary) events: One for Alex’s birthday, the other for our anniversary. Similarly, on the weekly.org file (my weekly planner), I can create a header “Visiting parents @ NJ” for the three days I’m planning to go with Alex. Under that header, I’ll place a %%(diary-block) with the appropriate date range.

These headers shouldn’t have a timestamp, otherwise they’ll interfere with the information the agenda displays from the diary entries. However, one thing I will do is to tag the header with “Alex” as I usually do with people I know on a regular basis. Later on, when I’m in NJ on the vacation, I will link to that header (because I can’t link to the diary entry) from my journal.org and reflect on my experiences2 as they happen.

Finally, I noted that my agenda shows hourless events first. This helps with the general order of things, as it will show deadlines and tasks I haven’t scheduled first, so I can take care of them. This is achieved by modifying org-sort-agenda-notime-is-late to nil:

(setq org-sort-agenda-notime-is-late nil)

Comments

As I noted, this system is highly specific and will probably not work for you as-is. There are a few issues here that I’m aware of and I want to fix:

  1. As easy as it is to enter diary events in org-mode, these are not capture templates. This creates an interruption in the forc… um, flow.

  2. The diary itself (which you need to create) can break at times and not understand what date you’re highlighting. It will display a message saying something like “I don’t know what date you mean.” The only way to fix this that I found so far is to exit Emacs. There has to be a reason for this and a better solution.

  3. While this works, this is a somewhat ugly hack, which requires modifying my theme and incorporate diary parts which I usually don’t use. I might find a mode elegant elisp solution.

Footnotes


  1. I often reflect on events that happened the previous day when I journal and add them in retro-respect. Besides giving me a clear idea of what I did that day and where I went to, I can also write a journal entry that is connected to that event to reflect on what happened, with who, where. Org-mode is very easy to work throughout time: I can enter an event that happened three weeks ago just as easily as I can enter a new one today. ↩︎

  2. This is a good place to point out that I stopped using org-mode’s default links, which point to a header in an org file. These are too fragile: as soon as you refile a header or change the title, it breaks. A way around that is to use unique IDs (briefly mentioned here, but there’s more to it), which goes beyond what I’m covering in this post. This way my journal (in the above case) will always point to that specific header, even after it moved out of weekly.org and into its respective yearly week number or file, which is how I store my org files long-term. ↩︎

-1:-- How to make all-day events stand out in org-mode (Post)--L0--C0--June 14, 2021 04:00 AM

Sacha Chua: 2021-06-14 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar and emacs-devel.

-1:-- 2021-06-14 Emacs news (Post Sacha Chua)--L0--C0--June 14, 2021 12:00 AM

Jeremy Friesen: Delving Further into the Why of Emacs

Over on /r/emacs, one of the community members asked the about Integrated Development Environments (IDEs 🔍) with the following post: I have been using emacs for an year now mostly for Clojure development with little bit of golang and python here and there. When i started using emacs here i convinced myself that it makes me more productive by allowing me to do everything from keyboard multi language support with packages highly customisable with thousands of packages and config.
-1:-- Delving Further into the Why of Emacs (Post)--L0--C0--June 13, 2021 12:17 AM

Irreal: Tramp Mode with Zsh

Lennart Karssen has a tip for those of you who use tramp and zsh. He observed that if zsh was running on the remote machine and he tried to open a file with Ctrl+x Ctrl+f long timeouts would occur making tramp essentially unusable.

It turns out that the problem involved the shell prompt on the remote machine and that a simple one-liner to .zshrc completely resolved the timeouts. If you’re having this problem, take a look at Karssen’s blog to see how to fix it.

-1:-- Tramp Mode with Zsh (Post jcs)--L0--C0--June 12, 2021 04:55 PM

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, June 26, 2021

This month’s Emacs Asia-Pacific (APAC) virtual meetup is scheduled for Saturday, June 26, 2021 with Jitsi Meet and #emacs on Libera Chat IRC. The timing will be 1400 to 1500 IST. The meetup might get extended by 30 minutes if there is any talk, this page will be updated accordingly. If you would like to give a demo or talk (maximum 20 minutes) on GNU Emacs or any variant, please contact bhavin192 on Libera Chat with your talk details:
-1:-- Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, June 26, 2021 (Post)--L0--C0--June 12, 2021 12:27 AM

Christian Tietze: Multi-Monitor Compatible Code to Center Emacs Frames on Screen

When centering my Emacs windows (aka ‘frames’) on my monitors, I noticed that with 2 monitors active, the computation doesn’t work: It doesn’t center in the main monitor; it centers in the area of both monitors combined. That’s not what I want.

Here’s a fix and an explanation of the problem.

Why 2 monitors caused trouble

Looking at the documentation for display-pixel-width, I learned that this is intended.

There’s a distinction between “monitor” and “display” in Emacs that was absolutely not what I expected. “Display” is the total available space, while “monitor” is a single device.

To get per-monitor geometry information, the documentation advices the reader to look at display-monitor-attributes-list. That shows information for all monitors known to the system, though, so you’d have to find the right onw; looking at the docs there accidentally brought up a related function, frame-monitor-attributes, that limits the information to the monitor of the current Emacs frame. Since I want to center a newly created frame on screen, this works perfectly.

Using the monitor workarea

The result of (frame-monitor-attributes) for the main monitor is a list of attributes and value like this:

((geometry 0 0 3440 1440)
 (workarea 0 25 3440 1415)
 (mm-size 801 335)
 (frames #<frame  *Minibuf-1* 0x7f9c773b3418>)
 (source . "NS"))

This has the added benefit of defining a ‘workarea’ next to ‘geometry’ that excludes the space taken up by main menu. So I don’t need to compute this area myself.

I never worked with an attribute list like that, so I had to look up a couple of functions for hash maps, propertly lists, dictionaries etc. until one eventually worked. In the process I noticed there also is the function (frame-monitor-workarea) that does the job, so I’ll be using that. In case you’re curious how to unpack an attribute, though, try: (alist-get 'workarea (frame-monitor-attributes)).

Now the result of this is the list of values:

(0 25 3440 1415)

To get to the 4th item in this list, i.e. the usable height, cadddr is used. That’s a shorthand:

  • 3x cdr calls, which would be cdddr. In modern languages, we’d be calling this “drop first”. It returns rest of the list sans the first element.
  • That on its own would produce a list with 1 item, (1415). If you’re new to Lisp, thats basically an array with 1 element.
  • So we add a car call that fetches the tip of the list. For example (car '(foo bar fizz buzz)) returns foo. This gives us the number.

To unpack the 3rd item in this list, i.e. the usable width, we use caddr – note it does one less cdr call, so the temporary list result is (3440 1415), and then the car fetches the number, 3440.

Update 2021-06-11: You can also use (nth INDEX LIST). I prefer that because even though it looks like I’m a Lisp noob, it’s easier to figure out which element you get.

Armed with this knowledge, I’ve added these two functions to get the width and height, including proper documentation:

(defun ct/frame-monitor-usable-height (&optional frame)
  "Return the usable height in pixels of the monitor of FRAME.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame.

Uses the monitor's workarea. See `display-monitor-attributes-list'."
  (cadddr (frame-monitor-workarea frame)))

(defun ct/frame-monitor-usable-width (&optional frame)
  "Return the usable width in pixels of the monitor of FRAME.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame.

Uses the monitor's workarea. See `display-monitor-attributes-list'."
  (caddr (frame-monitor-workarea frame)))

If you never ever use these functions anywhere else, the following would do the trick, too, as local variables in a function:

;; ...
  (let* ((workarea (frame-monitor-workarea frame))
          (width (caddr workarea))
          (height (cadddr workarea)))
    ;; use width and height here
    )
;; ...

I guess with more Elisp experience, I’d be using these directly, but at the moment I benefit from dedicated functions with documentation to encapsulate concepts like “unpack the width from a monitor workarea value list”.

Update 2021-06-11: Göktuğ Kayaalp shared a condensed single-function approach that I tweaked a bit and shared in another post.

Resulting code to center a frame on screen

Here is the complete, fully updated version:

(defun ct/frame-monitor-usable-height (&optional frame)
  "Return the usable height in pixels of the monitor of FRAME.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame.

Uses the monitor's workarea. See `display-monitor-attributes-list'."
  (cadddr (frame-monitor-workarea frame)))

(defun ct/frame-monitor-usable-width (&optional frame)
  "Return the usable width in pixels of the monitor of FRAME.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame.

Uses the monitor's workarea. See `display-monitor-attributes-list'."
  (caddr (frame-monitor-workarea frame)))

(defun ct/center-box (w h cw ch)
  "Center a box inside another box.

Returns a list of `(TOP LEFT)' representing the centered position
of the box `(w h)' inside the box `(cw ch)'."
  (list (/ (- cw w) 2) (/ (- ch h) 2)))

(defun ct/frame-get-center (frame)
  "Return the center position of FRAME on it's display."
  (ct/center-box (frame-pixel-width frame) (frame-pixel-height frame)
                 (ct/frame-monitor-usable-width frame) (ct/frame-monitor-usable-height frame)))

(defun ct/frame-center (&optional frame)
  "Center a frame on the screen."
  (interactive)
  (let* ((frame (or (and (boundp 'frame) frame) (selected-frame)))
         (center (ct/frame-get-center frame)))
    (apply 'set-frame-position (flatten-list (list frame center)))))

Now it works, without any hacks and manual macOS menu offsetting.

Also see the shorter single-function variant.


Receive Christian’s new posts via email

-1:-- Multi-Monitor Compatible Code to Center Emacs Frames on Screen (Post)--L0--C0--June 11, 2021 12:43 PM

Christian Tietze: Single Function to Center Emacs Window on Screen

Fellow Emacs-user Göktuğ Kayaalp condensed my frame centering stuff into one function as I mentioned one could do, and then improved it by adding a condition for full-screen frames. This is probably the last post on this topic, ever, because what else could be said. Right?!

It’s so nice, I want to share the code, reformatted a bit. And Göktuğ didn’t add the binding of frame to selected-frame as a fallback, so I added that back in, plus longer docs.

(defun my/frame-recenter (&optional frame)
  "Center FRAME on the screen.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame."
  (interactive)
  (unless (eq 'maximised (frame-parameter nil 'fullscreen))
    (let* ((frame (or (and (boundp 'frame) frame) (selected-frame)))
            (frame-w (frame-pixel-width frame))
            (frame-h (frame-pixel-height frame))
            ;; frame-monitor-workarea returns (x y width height) for the monitor
            (monitor-w (nth 2 (frame-monitor-workarea frame)))
            (monitor-h (nth 3 (frame-monitor-workarea frame)))
            (center (list (/ (- monitor-w frame-w) 2)
                          (/ (- monitor-h frame-h) 2))))
      (apply 'set-frame-position (flatten-list (list frame center))))))

(add-hook 'after-init-hook #'my/frame-recenter)
(add-hook 'after-make-frame-functions #'my/frame-recenter)

I wasn’t aware that instead of add-to-list a hook for after-make-frame-functions would do the job, too. That’s what you get from following some of the docs on the web to the letter. The after-init-hook is a great addition, too.

Since I have this auto-centering functionality in place, I find myself opening and closing new frames quite a lot. For most of 2020, I have been using a single frame, often in full-screen mode, and then have been splitting panes and experimenting with workspaces and tabs. Using the macOS windowing system instead integrates much easier with the rest of my computer life. (Though I still envy the Linux folks with their tiling window managers who use multiple tabs in emacs. Looks so tidy!)

You can also run this function in the terminal no problem. It will just exit with t and you won’t see any effect. The important part is that there are no errors when you’re not in a GUI.

See also:


Receive Christian’s new posts via email

-1:-- Single Function to Center Emacs Window on Screen (Post)--L0--C0--June 11, 2021 12:33 PM

Irreal: Molding Emacs

Jeremy Friesen has an interesting post on how he uses Emacs to blog. That’s easy, right? Just use Org-mode and some Org → Blog interface such as Hugo or org2blog and you’ve automated away almost everything except the actual writing of the posts.

Except Friesen isn’t using Org to blog. He has some custom code that acts sort of like Org capture that prompts him for a title and builds a port stub that he can expand into a post. The idea is to make it easy for him to capture an idea for a blog post when he has it and come back to the actual writing later. He’s molded Emacs to fit his workflow.

It’s not that Friesen is against Org. He envisions that his blogging workflow will eventually become Org based but for the time being his current solution is working for him and he doesn’t want to spend resources solving a problem he doesn’t have.

Take a look at his post for the code he uses and a few more details on his blogging workflow. He has a follow on post that expands his code so check that out too if you’re interested in his approach.

-1:-- Molding Emacs (Post jcs)--L0--C0--June 10, 2021 03:21 PM

Tory Anderson: Two modes on loading a file

** This question was posted to the emacs stackexchange. 1 Clojure files should open in Clojure-mode. I also have Clojure files that contain color info and are used to generate my styles; they live under a /styles path. For these files, I want them to have both clojure-mode (a major mode) and rainbow-mode (a minor mode). I can implement this on a file-by-file basis be starting such files with a line like this:
-1:-- Two modes on loading a file (Post)--L0--C0--June 10, 2021 12:00 AM

Christian Tietze: Automatically Center New Emacs Windows (Aka Frames) on Screen

When I open a new GUI window of Emacs on macOS (which Emacs calls frame) it’s positioned in the top-left corner. Since I have an ultrawide monitor at my desk, that’s pretty annoying. Unlike regular macOS apps, Emacs doesn’t remember where I dragged the last NSWindow to, so it doesn’t spawn new windows next to that. It also doesn’t stagger them like NSDocument-type apps usually do.

So I took some code from the internet to determine the window’s center position and the screen’s center position and then align both. It’s part of a larger Gist that would even tile your windows on the screen, but I only want new windows to always start in the middle. I can move them ouyt of the way if needed.

So the whole code is from https://gist.github.com/ieure/80638, only shortened for this particular use case.

Update 2021-06-09: The code below only works on single-monitor setups. If you have 2+ monitors, this unintentionally tries to center the window in the area described by all monitors combined. I published a fixed version that I encourage you to use instead!

(defun ct/screen-usable-height (&optional display)
  "Return the usable height of the display.

Some window-systems have portions of the screen which Emacs
cannot address. This function should return the height of the
screen, minus anything which is not usable."
  (- (display-pixel-height display)
     (cond ((eq window-system 'ns) 22) ;; macOS Menu Bar offset
           (t 0))))

(defun ct/screen-usable-width (&optional display)
  "Return the usable width of the display."
  (display-pixel-width display))

(defun ct/center-box (w h cw ch)
  "Center a box inside another box.

Returns a list of `(TOP LEFT)' representing the centered position
of the box `(w h)' inside the box `(cw ch)'."
  (list (/ (- cw w) 2) (/ (- ch h) 2)))

(defun ct/frame-get-center (frame)
  "Return the center position of FRAME on it's display."
  (let ((disp (frame-parameter frame 'display)))
    (ct/center-box (frame-pixel-width frame) (frame-pixel-height frame)
                   (ct/screen-usable-width disp) (ct/screen-usable-height disp))))
(defun ct/frame-center (&optional frame)
  "Center a frame on the screen."
  (interactive)
  (apply 'set-frame-position
         (let* ((frame (or (and (boundp 'frame) frame) (selected-frame)))
                (center (ct/frame-get-center frame)))
           ;; Flatten the X/Y list in `center` into a single list with `frame`
           ;; so this list can be applied as parameters to `set-frame-position`:
           `(,frame ,@center))))

Then to automatically center the frame after frame creation:

(add-to-list 'after-make-frame-functions #'ct/frame-center)

All these Emacs Lisp backtick-at-comma shorthands begin to confuse me. I’m fine with #'functionname to signity that this isn’t just a string, but a function name. But the commas, and the backticked list, and the at sign?! Geez.

If we skipped the shorthand characters and just wrote (list frame center), we’d get the elements (frame (top left)), because center itself is a tuple already. This essentially flattens the nested list.

That’s the thing with these quotes. They are not just shorthands. The result of (list frame center) is different from '(frame center). Both expressions produces a list, but if you call the list function, the variadic arguments are evaluated and their result is put into the list; if you use the single quote, you get a list of the literal elements. It’s equivalent to (list 'frame 'center), which we clearly didn’t want. The backquote allows to evaluate some elements of the quotes list with the , operator, and by evaluating the variables, we get their names in the original code.

Or we don’t use the quoted forms and have a straight-forward result.

Compare these:

  • Quoted: \(,frame ,@center)` packs a lot of info in characters that are harder to look up than functions;
  • Function calls (flatten-list (list frame center)) is very much readable.

Here’s how I’d really write it, putting the local variable bindings first and not inside the function call to put less lines between function name and parameter list:

(defun ct/frame-center (&optional frame)
  "Center a frame on the screen."
  (interactive)
  (let* ((frame (or (and (boundp 'frame) frame) (selected-frame)))
         (center (ct/frame-get-center frame)))
    (apply 'set-frame-position (flatten-list (list frame center)))))

Receive Christian’s new posts via email

-1:-- Automatically Center New Emacs Windows (Aka Frames) on Screen (Post)--L0--C0--June 09, 2021 10:28 AM

Jeremy Friesen: Further Molding Emacs to Reinforce Habits

Yesterday I wrote about Molding Emacs to Reinforce Habits I Want to Develop. In that post, I outlined how I wrote some functions to accelerate grabbing some text and starting a new blog post. Today I refined and further extended those functions. To start, I wrote down the desired behavior. Desired Behavior I want a function that will pre-populate an Amplifying post from an elfeed entry. If there’s an active region (e.
-1:-- Further Molding Emacs to Reinforce Habits (Post)--L0--C0--June 09, 2021 02:42 AM

Tory Anderson: How to get readable mode in emacs w3m?

I’ve made the switch to w3m from eww, which has been great overall since the eww thread-blocking was insufferable. However, one of the biggest benefits of eww was the ability to engage a readability mode that cut all but the content of the page, which is also a feature I use a lot on Firefox on my phone. A previous answer from 20201 did something like this but was referencing elfeed, which has nothing to do with my use case, and used a bootstrap Python script.
-1:-- How to get readable mode in emacs w3m? (Post)--L0--C0--June 09, 2021 12:00 AM

Emacs Redux: Emacs as Your Calendar

Even in the age of smartphones I still prefer to check the calendar on my computer and I’ve often been disappointed by the horrible default calendars that ship with some operating systems (e.g. macOS’s calendar accessible via its systray).

Fortunately for us, Emacs users, we always have access to a proper calendar regardless of our OS and desktop environment (if any) - M-x calendar. By default it looks something like this:

calendar_default.png

Nothing fancy here, just a simple calendar that highlights the current date. You can easily jump to any date by pressing o (it stands for other) and go back to the current date by pressing .. You can also use < and > to move back and forward in chunks of 3 months (by default).

You might have noticed that the calendar by default assumes that the week starts on Sunday, although in many countries (Bulgaria included) it actually starts on Monday. You can easily change this:

(setq calendar-week-start-day 1)

Sunday is 0, Monday is 1 and so on.

You can go a step further and localize the calendar like this:

;; everything's better in Bulgarian
(setq calendar-day-name-array ["Неделя" "Понеделник" "Вторник" "Сряда" "Четвъртък" "Петък" "Събота"]
      calendar-day-abbrev-array ["Нд" "Пн" "Вт" "Ср" "Чт" "Пт" "Сб"]
      calendar-day-header-array ["Нд" "Пн" "Вт" "Ср" "Чт" "Пт" "Сб"]
      calendar-month-name-array ["Януари" "Февруари" "Март" "Април" "Май"
	                             "Юни" "Юли" "Август" "Септември"
				                 "Октомври" "Ноември" "Декември"])

If you already have the calendar open you’ll have to do M-x calendar or M-x calender-redraw to reflect the new settings.

Did you notice that the configuration uses Emacs Lisp arrays? They are so uncommon that I’m pretty sure most people don’t even know about their existence.

And here’s the final result:

calendar_bulgarian.png

There are many more things that you can with the calendar (e.g. configure there national holidays, leverage integrations with org-mode and so on), but they are beyond the scope of this short intro. As usual I encourage all of you to share your favorite tips and tricks related to the calendar in the comments.

I hope you learning something useful today. Keep hacking!

-1:-- Emacs as Your Calendar (Post Bozhidar Batsov)--L0--C0--June 08, 2021 06:23 AM

Andrea: Emacs, Nyxt and Engine-mode: how to browse URLs via Nyxt and Slime

-1:-- Emacs, Nyxt and Engine-mode: how to browse URLs via Nyxt and Slime (Post)--L0--C0--June 08, 2021 12:00 AM

Jeremy Friesen: Molding Emacs to Reinforce Habits I Want to Develop

Before I switched to Emacs 🔍, I had a Rake 🔍 task that I invoked to create new blog posts. I’d hop on the terminal, run the task. That task would both stub out a new blog post and open the task in my editor of choice. This worked, but the script wasn’t integrated into my text editor. So there was a slight context shift to go from I have an idea for a blog post to I’m starting that blog post.
-1:-- Molding Emacs to Reinforce Habits I Want to Develop (Post)--L0--C0--June 07, 2021 03:19 PM

Irreal: Updating Emacs Packages from the Command Line

Tassilo Horn has a (very) short post on how he updates all his Emacs packages from the command line. It involves installing auto-package-update but after that it’s simply a matter of calling Emacs and running auto-package-update-now. It would be easy, for example, to set a cron job to update your packages at night when you’re not using the system.

His post is definitely worth a look if you have packages installed and like to update them regularly.

-1:-- Updating Emacs Packages from the Command Line (Post jcs)--L0--C0--June 07, 2021 03:15 PM

Marcin Borkowski: Emacs Lisp book update – what the book is going to look like

About a month ago I decided to write about the book on Emacs Lisp I am writing. I also made it available on Leanpub where you can download the first chapter and pay me to get the (roughly) 40% of the book that is already written. This post is the first in a series of updates I plan to publish about once per month. Read on if you are curious about the process of writing a book, or want to learn if it is for you, or want to actually help me to write it! First of all, I’d like to thank all the people who trusted me so far and say that I was surprised and humbled to have so many of you. Now I feel even more encouraged to stick to my writing schedule.
-1:-- Emacs Lisp book update – what the book is going to look like (Post)--L0--C0--June 07, 2021 03:52 AM

Tory Anderson: Tip: BBDB mode for contact management

BBDB1 is one of those libraries that has been around for so long it would be highly irregular if we weren’t talking about Emacs. The size and age of the thing is what originally scared me away from it in favor of org-contacts2, which had the advantage of being orgmode but was deprecated a long time ago. With BBDB you don’t usually view the elisp database that lies beneath, instead using its robust interface to interact.
-1:-- Tip: BBDB mode for contact management (Post)--L0--C0--June 07, 2021 12:00 AM

Tory Anderson: dired no hiline

I was trying to make sure that hl-line mode (to highlight a line) is not on in my dired buffers without changing its presence anywhere else, since it messes up display of my diredfl permission faces. Unfortunately, the following didn’t work, probably because “setting” isn’t the same thing as calling the variable. What was the best way to selectively disable hl-line-mode? There were many good answers on Reddit1. (add-hook 'dired-mode-hook (lambda () (setq-local global-hl-line-mode nil hl-line-mode nil) ;; I wasn't sure on the relationship between ;; these two, or even which one was being used )) Learning along the way: global- is unrelated to hl-line-mode It turns out that global-hl-line does not share anything in common with hl-line-mode other than the end result, so altering one does not alter the other.
-1:-- dired no hiline (Post)--L0--C0--June 07, 2021 12:00 AM

Sacha Chua: 2021-06-07 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar and emacs-devel.

-1:-- 2021-06-07 Emacs news (Post Sacha Chua)--L0--C0--June 07, 2021 12:00 AM