Irreal: Some Useful Editing Functions

In my recent post, Some Configuration To Solve Common Problems, I got a bit of pushback from GB who said some of the configuration was outdated. That’s true but does it really make much difference? The Emacs Cat has a followup post in which he agrees that some of his configuration is old—just like mine and probably yours—but that it still works.

That’s another nice thing about Emacs: even though new, better methods of doing things are introduced, the old methods continue to work. Sometimes, those old methods will eventually be deprecated but always with a lot of notification including warnings when you use them.

The Emacs Cat’s second post describes some of his custom Elisp that aids editing. Several of them add functionality to Dired, a functionality that everyone should be using. He also has some functions for inserting date/time stamps of various sort. I have several of these myself and the lesson to take away from them is how easy it is to get exactly the format you want.

Finally, there are a few functions that specialize common requirements such as opening new lines, moving to the beginning of the line, and others. These are the sort of functions that every long term Emacs user eventually accumulates so it should be especially useful for beginners to see them written down in a single post.

-1:-- Some Useful Editing Functions (Post jcs)--L0--C0--December 03, 2024 05:30 PM

Christian Tietze: Org-Mode Emphasis Keymap with Mnemonics

In Emacs Org-Mode, the default key binding to “highlight” or “format” the selection is C-c C-x C-f. This will then ask for the org-mode syntax thingie to use, i.e. */_~=+. Literally, that’s the prompt.

So you have to know which character to use to embolden text. That’s *. But which one is code? It’s ~. And = is for verbatim text, while + is for strike-through.

This is defined in the variable org-emphasis-alist, and it’s default value is:

(("*" bold)
 ("/" italic)
 ("_" underline)
 ("=" org-verbatim verbatim)
 ("~" org-code verbatim)
 ("+"
  (:strike-through t)))

If you don’t know what any of these characters do, but if you know how to browse the Emacs help to get to this piece of information, you can learn the formatting syntax this way.

I don’t like that. If I already know the character to type, I may just as well type it. There’s plenty of options to have some characters surround the selected text, so that you can select text first, then hit *, and have the selection be emboldened. That’s not what I believe a formatting shortcut is useful for.

As an actual convenience for users, UX and all, I believe “b” for “bold” and such would be much better to get started!

That’s also closer to common shortcuts in rich text editors, so that may be a bonus for new users, too. And me, who never knows whether to type = or ~ for inline code.

So here’s how you’d do that in Emacs nowadays: with a keymap.


(defvar-keymap ct/org-emphasis-map
  :doc "Keymap for quickly applying Org emphasis rules."
  :name "[b]old [i]talic [u]nderscore [v]erbatim [c]ode [s]trike-though"
  "b" (lambda () (interactive) (ct/org-emphasize-below-point ?*))
  "i" (lambda () (interactive) (ct/org-emphasize-below-point ?/))
  "u" (lambda () (interactive) (ct/org-emphasize-below-point ?_))
  "v" (lambda () (interactive) (ct/org-emphasize-below-point ?=))
  "c" (lambda () (interactive) (ct/org-emphasize-below-point ?~))
  "s" (lambda () (interactive) (ct/org-emphasize-below-point ?+)))

This adds all mnemonics (the initial letters of the styles) to the keymap and executes a function with the corresponding org-mode formatting character as argument. The function is called ct/org-emphasize-below-point because that’s my personal way to auto emphasize the closest thing:

(defun ct/org-emphasize-below-point (&optional char)
  "Emphasisez region with CHAR.
  
If there's no region, marks the closest s-expression, first.
Opposed to word boundaries, sexp's work with `subword-mode' enabled."
  (interactive)
  (unless (region-active-p)
    (backward-sexp)
    (mark-sexp))
  (org-emphasize char))

subword-mode makes by-word movement commands stop at capital letters in CamelCasedWords, which I find extremely useful in almost all circumstances. Except highlighting the word before the insertion point, because then I want the whole word word, not sub-word. So that’s what my function is for.

If you’re not using any of that, call (org-emphasize ?*) etc. instead in the lambdas.

As a hat-tip to markdown-mode, I bound this to C-c C-s:

(define-key org-mode-map (kbd "C-c C-s") ct/org-emphasis-map)

The Markdown mode’s style helper formats the prompt nicely:

This is how markdown-mode renders the mnemonics for you

I love this attention to detail, but I was too lazy for that; the function that produces the propertized (i.e.: styled) string for the modeline is this:

(defun markdown--style-map-prompt ()
  "Return a formatted prompt for Markdown markup insertion."
  (when markdown-enable-prefix-prompts
    (concat
     "Markdown: "
     (propertize "bold" 'face 'markdown-bold-face) ", "
     (propertize "italic" 'face 'markdown-italic-face) ", "
     (propertize "code" 'face 'markdown-inline-code-face) ", "
     (propertize "C = GFM code" 'face 'markdown-code-face) ", "
     (propertize "pre" 'face 'markdown-pre-face) ", "
     (propertize "footnote" 'face 'markdown-footnote-text-face) ", "
     (propertize "F = foldable" 'face 'markdown-bold-face) ", "
     (propertize "q = blockquote" 'face 'markdown-blockquote-face) ", "
     (propertize "h & 1-6 = heading" 'face 'markdown-header-face) ", "
     (propertize "- = hr" 'face 'markdown-hr-face) ", "
     "C-h = more")))

That could be adapted to my barebones modeline string that just says

"[b]old [i]talic [u]nderscore [v]erbatim [c]ode [s]trike-though"

I leave this as an exercise for the reader.

Really, please implement this, and share. I want it, but I don’t want to spend that time today :)


Hire me for freelance macOS/iOS work and consulting.

Buy my apps.

Receive new posts via email.

-1:-- Org-Mode Emphasis Keymap with Mnemonics (Post)--L0--C0--December 03, 2024 05:15 PM

TAONAW - Emacs and Org Mode: Filtering org-agenda to *exclude* a category

Back in July, I explained how I use Beorg to sync my calendars. To recap, Beorg continuously exports the iOS calendar into a read-only org file, which I then sync to my Mac’s desktop through iCloud.

The calendar.org file shows on my Emacs agenda, where I can see both my Outlook (work-related) meetings and my Gmail (personal) events. This is very nice, but because the calendar.org file is read-only (as it should be—it keeps being overwritten by the Beorg every time the iOS calendar syncs), I can’t use it for anything besides this visual information.

If I want to create a project from a meeting and add notes and sub-tasks, I have to copy the event from my calendar.org file to Now.org, where I keep my current projects and tasks. There, I can add notes and headers.

But then there’s another problem: when I copy my headers over to Now.org, my agenda shows duplicates - one event header comes from calendar.org, and the other comes from Now.org after I copied it there. Here’s what it looks like:

Auto-generated description: A digital calendar displays scheduled events including reviews, onboarding huddles, and meetings, with some marked as DONE or MEETING.

When I start my day, it’s important that I see calendar.org so I can copy over details to now.org. Once I’m done, however (clearing the Calendar tag, adding a keyword like MEETING, and cleaning the text under the header from details I don’t need), I no longer need to see calendar.org, and I want it to disappear.

I was looking around for a solution to this problem, and of course, it was right under my nose. Org-agenda comes with the option of narrowing down (filtering) the agenda to a category at the point with <. What I didn’t know is that C-u < does exactly what I want: the opposite of the above. It hides the category selected. Problem solved.

-1:-- Filtering org-agenda to *exclude* a category (Post)--L0--C0--December 03, 2024 01:51 AM

Charles Choi: Announcement: Casual talk at EmacsConf 2024

Just a heads up that I’ll be presenting at this year’s EmacsConf 2024 with the talk Re-imagining the Emacs User Experience with Casual Suite this Saturday, December 7 at 2:45 pm EST (US/Eastern).

As Emacs users are world-wide, here’s when my talk will happen at different time-zones. If you're interested, please attend! The presentation is prerecorded but I will be there in the chats. EmacsConf is an online conference and is open to all.

America/Los_Angeles America/New_York Europe/Berlin Asia/Seoul
2024-12-07 11:45:00 -0800 2024-12-07 14:45:00 -0500 2024-12-07 20:45:00 +0100 2024-12-08 04:45:00 +0900

See you then!

-1:-- Announcement: Casual talk at EmacsConf 2024 (Post Charles Choi)--L0--C0--December 02, 2024 07:45 PM

Sacha Chua: 2024-12-02 Emacs news

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

View org source for this post
-1:-- 2024-12-02 Emacs news (Post Sacha Chua)--L0--C0--December 02, 2024 06:10 PM

Marcin Borkowski: Automatically inserting Ledger transactions

I wrote a few times that I use and like Ledger a lot. As some of you might know, I even wrote a booklet about personal accounting, using Ledger in examples. One of the nice things about Ledger is that it comes with an Emacs mode to edit its files (which is not a surprise, given that it is written by John Wiegley himself). That is not to say, though, that it suits my needs perfectly.
-1:-- Automatically inserting Ledger transactions (Post)--L0--C0--December 02, 2024 07:41 AM

Irreal: Shrinking And Widening Org Table Columns

Just a quickie today because I’m still recovering from the Thanksgiving festivities. The Irreal bunker sealed the doors and refused to venture outside on Black Friday, a day in which many stores have stupendous sales resulting in unhinged behavior on the part of shoppers. Is Black Friday a thing in the rest of the world? In the U.S. it refers to the day after Thanksgiving—which is, after all, an American holiday—but with the globalization of commerce by Amazon and others, I wouldn’t be surprised to hear that others are suffering from it too.

At any rate, James Dyer has a note to himself that others may find useful. It’s possible to shrink and widen Org table columns. Normally, you wouldn’t need to do that, of course, but if the text in one or more columns exceeds the available length you can (temporarily) widen them and then shrink them again.

This is another one of those things that you hardly ever use so it’s really easy to forget how to do it. That’s why Dyer posted the note to himself. Take a look at Dyer’s post. It’s short and it may tell you something you’ll find useful.

-1:-- Shrinking And Widening Org Table Columns (Post jcs)--L0--C0--November 30, 2024 04:50 PM

J.e.r.e.m.y B.r.y.a.n.t: Creating an info manual by conversion, example for Python

I explain how to enable the ability to look up symbols in the documentation for Python by creating a manual in the info format. (...)
-1:-- Creating an info manual by conversion, example for Python (Post)--L0--C0--November 29, 2024 04:30 PM

The Emacs Cat: Some Excerpts From My Emacs Config - 2: Functions

In his comment on Irreal’s post Some Configuration To Solve Common Problems, gregbognar noted that some of my configuration is seriously outdated. In some sense, he is absolutely correct – I’ve been collecting Emacs settings/tweaks since 2012, and keeping them unmodified for 12+ years.

But on the other hand, it demonstrates how stable and powerful Emacs is – everything is still working! If something works, don’t fix it – I’ve since tried to apply this rule wherever possible.

Here are a few functions/enhancements I found helpful.

-1:-- Some Excerpts From My Emacs Config - 2: Functions (Post)--L0--C0--November 29, 2024 10:45 AM

Alex Popescu: Prot's Basic and Capable Emacs Configuration

I am learning a lot from Prot’s articles on Emacs. This last article:

What follows is a rather simple, yet fully capable, setup to get started with Emacs.

Here’s what I learned from it.

I have replaced the custom.el related code from:

(progn
 (setq custom-file (expand-file-name "custom.el" user-emacs-directory))
 (when (file-exists-p custom-file)
 (load custom-file)))

with the simpler, more legible:

(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)

Then, it’s the code snippet for preventing the Warnings buffer popping up. So useful!

-1:-- Prot's Basic and Capable Emacs Configuration (Post)--L0--C0--November 28, 2024 08:26 PM

Irreal: Casual Calendar

Charles Choi is back with another app in his great set of Casual packages. This time it’s to provide a menu interface to the calendar/diary functions in Emacs.

I don’t use either of these functionalities directly because it’s hard to interface them to my (Apple) smartphone. Still, I do use them second hand with my Org agenda. I schedule some items—usually local task or chores that don’t involve other people such as doctors. For appointments out of the house I use my iCalendar because it seamlessly integrates with my iPhone and iPad.

Choi makes a point of saying how Emacs can support non-Gregorian calendars. In my usual parochial way, I’m having a bit of difficulty imaging a real need for this in day-to-day activities but as I say: parochial.

As usual, Choi’s menu reveals that there’s a lot of little known capabilities to the Emacs calendar and diary. That’s one of the strengths of his apps. They reveal functionality you didn’t know was there. Once you do know it’s there, the menu helps you to remember how to access it.

Take a look at Choi’s post to get an idea of what the new App can do.

-1:-- Casual Calendar (Post jcs)--L0--C0--November 28, 2024 04:46 PM

James Dyer: Shrinking and Widening Org Tables

This post is more of a note to myself, something I can store in my single emacs blog org file, so if I forget again, I can quickly search.

I keep forgetting the keybinding to shrink and expand an org table.

I often define tables to have a narrower width than content using the <num> concept in the top line of the table, but when I want to expand, I just can’t remember the keybinding.

A search in *help* for org-table-shrink and org-table-expand reveals nothing!

Anyway, here is a note to myself:

C-c TAB (translated from C-c <tab>) runs the command org-ctrl-c-tab
(found in org-mode-map), which is an interactive native-compiled
Lisp function in ‘org.el’.

It is bound to C-c TAB.

(org-ctrl-c-tab &optional ARG)

Toggle columns width in a table, or show children.  Call
‘org-table-toggle-column-width’ if point is in a table.  Otherwise
provide a compact view of the children.  ARG is the level to hide.

For clarity, within an Org table, pressing C-c <TAB> will toggle shrink/expand the state of the column that the point is currently in.

C-u C-c <TAB> will shrink all columns AND

C-u C-u C-c <TAB> will expand all columns

Right, that is a note, just for me 😀

-1:-- Shrinking and Widening Org Tables (Post James Dyer)--L0--C0--November 28, 2024 01:02 PM

Protesilaos Stavrou: Emacs: a basic and capable configuration

I have helped several people set up a basic Emacs configuration. In the process, I have learnt about some of the problems they have with the out-of-the-box experience.

What follows is a rather simple, yet fully capable, setup to get started with Emacs. At the end of the file is the code block with the complete configuration (The complete configuration). The other sections explain the rationale for each component.

Use this as a starting point. If you are curious about my own setup, check this: https://protesilaos.com/emacs/dotemacs.

Table of Contents

  1. Where to store the configurations
  2. Put all auto-generated configurations in a separate file
  3. Set up the package manager
  4. Set up use-package
  5. Do not show those confusing warnings when installing packages
  6. Delete the selected text upon text insertion
  7. Make C-g a bit more helpful
  8. Decide what to do with the graphical bars
  9. Use the preferred fonts
  10. Choose a theme and tweak the looks of Emacs
  11. Use icon fonts in various places
  12. Configure the minibuffer and related
  13. Tweak the dired Emacs file manager
  14. The complete configuration

Where to store the configurations

Emacs provides lots of possibilities, starting with where to put the main configuration file. In the interest of not overwhelming the user, I am being opinionated with certain choices:

  • Use ~/.emacs.d/init.el for your configurations.
  • If there is an ~/.emacs file on your system, make sure to delete it (or rename it if you care about its contents). Otherwise, Emacs will prioritise that over your ~/.emacs.d/init.el.

Put all auto-generated configurations in a separate file

Emacs has a graphical interface for modifying the variables that are intended for user configuration. Instead of writing Emacs Lisp code, the user clicks on buttons and fills in forms. This interface is accessed in a variety of ways, such as from the menu bar or with the command M-x customize.

When the user makes a modification in this way and requests to save it, Emacs will append a code block to the user’s configuration file. Putting this code in a separate file makes it easier to reason about what is written by the user and what is generated by the running session. We thus add the following to the configuration file (The complete configuration):

(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)

Here we defined the value of the variable custom-file to be what the function call (locate-user-emacs-file "custom.el") returns. That function finds where the init.el is, gets the directory path, and then returns a new path consisting of that directory plus the name we give it. So it will return ~/.emacs.d/custom.el because of ~/.emacs.d/init.el (Where to store the configurations).

Set up the package manager

Emacs has a basic, yet sufficiently capable, package manager. We use it to extend Emacs with functionality that is provided by the community. There are a lot of useful features built into Emacs, but without the external packages we would be worse off. Also, bear in mind that a package being built into Emacs does not necessarily make it better than third-party alternatives. I do prefer some of the latter even though Emacs has similar functionality built-in.

Start by adding the following to the configuration file (The complete configuration):

(require 'package)
(package-initialize)

(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

Those three lines do the following:

  • Load the package manager.
  • Initialise the installed packages.
  • Add the MELPA repository to list of known package archives. MELPA is a community-driven project that contains lots of useful packages that are not available elsewhere. The core Emacs maintainers do not want to talk about it for political reasons. I disagree with them: we are already understaffed and underfunded. I will not throw away the excellent work of hundreds of developers.

The function call (package-initialize) is technically not necessary, as Emacs can handle this requirement in a different way. In the interest of simplicity, I am leaving it here.

Set up use-package

With use-package, users have a powerful tool for configuring packages in a streamlined way (The complete configuration). Concretely, the user does not need to write a lot of repetitive code, figure out how to load packages when they are needed, installed missing dependencies, and the like. Emacs version 29 has use-package built-in, but earlier versions need to install it manually.

(when (< emacs-major-version 29)
  (unless (package-installed-p 'use-package)
    (unless package-archive-contents
      (package-refresh-contents))
    (package-install 'use-package)))

I explain use-package at greater length in this video: https://protesilaos.com/codelog/2024-07-23-emacs-use-package-essentials/.

Do not show those confusing warnings when installing packages

When installing a new package, Emacs will show a buffer that contains any warnings produced by the byte compiler (Set up the package manager). While this information is useful for developers, it is highly confusing for users. Many times I have received messages along the lines of “my Emacs just broke”, when in truth the warnings are about issues that are of no concern to the user.

The following snippet will prevent those buffers from popping up (The complete configuration). They are still available in the buffer list, if they are needed.

(add-to-list 'display-buffer-alist
             '("\\`\\*\\(Warnings\\|Compile-Log\\)\\*\\'"
               (display-buffer-no-window)
               (allow-no-window . t)))

Delete the selected text upon text insertion

Virtually every program out there will delete the selected/highlighted text as soon as the user types something. Emacs does not do this by default, even though it has the functionality available. Let us then enable it (The complete configuration):

(use-package delsel
  :ensure nil ; no need to install it as it is built-in
  :hook (after-init . delete-selection-mode))

Here we are activating the delete-selection-mode as soon as Emacs is initialised. In other words, it is available when Emacs starts.

Make C-g a bit more helpful

Having observed beginners struggle with C-g not closing the open minibuffer, I know that the following is a quality-of-life refinement:

(defun prot/keyboard-quit-dwim ()
  "Do-What-I-Mean behaviour for a general `keyboard-quit'.

The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open.  Whereas we want it to close the
minibuffer, even without explicitly focusing it.

The DWIM behaviour of this command is as follows:

- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
  (interactive)
  (cond
   ((region-active-p)
    (keyboard-quit))
   ((derived-mode-p 'completion-list-mode)
    (delete-completion-window))
   ((> (minibuffer-depth) 0)
    (abort-recursive-edit))
   (t
    (keyboard-quit))))

(define-key global-map (kbd "C-g") #'prot/keyboard-quit-dwim)

This defines a new command (i.e. an interactive function that we can call with M-x or a key binding). This command provides a superset of the functionality provided by the generic keyboard-quit command, which is bound to C-g by default.

Decide what to do with the graphical bars

Emacs has a menu bar and tool bar by default. The menu bar is helpful to discover more functionality and access some commonly used commands. The tool bar is less useful, as it contains commands that are already in the menu bar. The following snippet contains the code for those two bars as well as the scroll bar. A value of 1 enables the functionality, while -1 disables it. Tweak those accordingly (The complete configuration):

(menu-bar-mode 1)
(scroll-bar-mode 1)
(tool-bar-mode -1)

I personally disable all three of them.

If you are interested in a technically more correct setup for those three, then know that Emacs will read an ~/.emacs.d/early-init.el file (Where to store the configurations). This file is evaluated before the initial Emacs frame is drawn, so you might prefer to disable all those graphical elements at the outset. Still, in the interest of simplicity, I suggest you keep everything in the init.el until you are a bit more experienced.

Use the preferred fonts

Fonts are an integral part of the text-centric Emacs experience (The complete configuration). With the following snippet, we configure the three “faces” that are used to specify font families. Emacs has the concept of a “face” for a bundle of text properties that include typographic properties (font family, font height, font weight, …) and colours (text/foreground colour, background colour).

(let ((mono-spaced-font "Monospace")
      (proportionately-spaced-font "Sans"))
  (set-face-attribute 'default nil :family mono-spaced-font :height 100)
  (set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
  (set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0))

The default face is the only one that must have an absolute :height value. Everything else uses a floating point, which is understood as a multiple of the default.

Change the above snippet to use the preferred font family names. Also adjust the default height to a larger/smaller number.

Choose a theme and tweak the looks of Emacs

I am the author of tens of themes for Emacs. Among them is the modus-themes package, which is also available built-in to the core Emacs distribution. Themes are a personal choice, however, so please do not consider the following as a strong opinion (The complete configuration). I simply pick the theme that most people I have interacted with have chosen as their preferred one (a close second is the ef-dream theme from my ef-themes package). Use this as a template to eventually set up another theme.

(use-package modus-themes
  :ensure t
  :config
  (load-theme 'modus-vivendi-tinted :no-confirm-loading))

Use icon fonts in various places

Continuing with the stylistic refinements to Emacs, the following snippet will show complementary icons in the minibuffer (Configure the minibuffer and related) and in Dired (Tweak the dired Emacs file manager). To make this setup work, the user must type M-x and then call the command nerd-icons-install-fonts. This will store the icon font files in a local directory (on Linux this is ~/.local/share/fonts). To be sure everything is working, a restart to Emacs will guarantee that the new font files are read.

(use-package nerd-icons
  :ensure t)

(use-package nerd-icons-completion
  :ensure t
  :after marginalia
  :config
  (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))

(use-package nerd-icons-corfu
  :ensure t
  :after corfu
  :config
  (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))

(use-package nerd-icons-dired
  :ensure t
  :hook
  (dired-mode . nerd-icons-dired-mode))

Configure the minibuffer and related

[ Also watch my video about the modern minibuffer packages, some of which I do not include here because they are a bit more advanced: https://protesilaos.com/codelog/2024-02-17-emacs-modern-minibuffer-packages/. ]

The minibuffer is a central part of the Emacs experience. It is where the user interacts with Emacs to respond to prompts, switch to another buffer, open a new file, run a command by its full name, and so on. The default minibuffer is minimal, which might be good for experienced users, but does not help with discoverability. Instead of that, we use the vertico package to produce a vertical layout. This makes it easier to see what the available options are.

(use-package vertico
  :ensure t
  :hook (after-init . vertico-mode))

The marginalia package is a nice complement to the vertical layout, as it uses the free space to show helpful information about the options shown there. For example, when the user types M-x to see a list of command names, Marginalia will add a brief description of each command. Depending on the specifics of the minibuffer interaction (opening a file, selecting a buffer, …), there will be the relevant information on display.

(use-package marginalia
  :ensure t
  :hook (after-init . marginalia-mode))

The orderless package offers a life-saver for all those cases where we do not remember the exact order of words. For example, to toggle the display of line numbers in the current buffer, we use the command M-x display-line-numbers-mode. With orderless set up, we may type something like li num dis at the M-x prompt and still get the desired result. This is because Orderless matches the space-separated characters we provide in any order. Emacs has other pattern matching styles built-in, but orderless is a good place to start. We thus make sure the other relevant variables are set to a nil value, so that we get Orderless everywhere.

(use-package orderless
  :ensure t
  :config
  (setq completion-styles '(orderless basic))
  (setq completion-category-defaults nil)
  (setq completion-category-overrride nil))

The built-in savehist package keeps a record of user inputs and stores them across sessions. Thus, the user will always see their latest choices closer to the top (such as with M-x).

(use-package savehist
  :ensure nil ; it is built-in
  :hook (after-init . savehist-mode))

The corfu package provides a popup interface for in-buffer completion. Conceptually, this is similar to what we do in the minibuffer, though it is meant for text expansion inside of a regular file. This is typically used for programming. There is more that can be done to make this useful for programming modes (namely, to set up the Language Server Protocol configuration), but because that will be too specific to each person’s requirements, I am providing only the core functionality here.

(use-package corfu
  :ensure t
  :hook (after-init . global-corfu-mode)
  :bind (:map corfu-map ("<tab>" . corfu-complete))
  :config
  (setq tab-always-indent 'complete)
  (setq corfu-preview-current nil)
  (setq corfu-min-width 20)

  (setq corfu-popupinfo-delay '(1.25 . 0.5))
  (corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'

  ;; Sort by input history (no need to modify `corfu-sort-function').
  (with-eval-after-load 'savehist
    (corfu-history-mode 1)
    (add-to-list 'savehist-additional-variables 'corfu-history)))

The minibuffer and the corfu popup will benefit from the font icons we set up (Use icon fonts in various places).

Tweak the dired Emacs file manager

Emacs comes with a powerful file manager built-in. I have talked about it at length in this video: https://protesilaos.com/codelog/2023-06-26-emacs-file-dired-basics/. The following settings define some basic refinements to the behaviour of common operations for copying, renaming, and deleting files.

The default presentation of Dired contains lots of details (The complete configuration). We use dired-hide-details-mode to hide those. They can be toggled on/off at any time with M-x dired-hide-details-mode, which is bound to the ( key in Dired buffers.

(use-package dired
  :ensure nil
  :commands (dired)
  :hook
  ((dired-mode . dired-hide-details-mode)
   (dired-mode . hl-line-mode))
  :config
  (setq dired-recursive-copies 'always)
  (setq dired-recursive-deletes 'always)
  (setq delete-by-moving-to-trash t)
  (setq dired-dwim-target t))

The dired-subtree package provides commands to quickly view the contents of a folder with the TAB key.

(use-package dired-subtree
  :ensure t
  :after dired
  :bind
  ( :map dired-mode-map
    ("<tab>" . dired-subtree-toggle)
    ("TAB" . dired-subtree-toggle)
    ("<backtab>" . dired-subtree-remove)
    ("S-TAB" . dired-subtree-remove))
  :config
  (setq dired-subtree-use-backgrounds nil))

Earlier we set up Dired to move deleted files to the system trash. The trashed package is then useful to provide a Dired-like interface for that compartment (it works the same way as Dired, so check the video I share above).

(use-package trashed
  :ensure t
  :commands (trashed)
  :config
  (setq trashed-action-confirmer 'y-or-n-p)
  (setq trashed-use-header-line t)
  (setq trashed-sort-key '("Date deleted" . t))
  (setq trashed-date-format "%Y-%m-%d %H:%M:%S"))

The complete configuration

Remember to put this in the right file (Where to store the configurations).

(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)

;;; Set up the package manager

(require 'package)
(package-initialize)

(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

(when (< emacs-major-version 29)
  (unless (package-installed-p 'use-package)
    (unless package-archive-contents
      (package-refresh-contents))
    (package-install 'use-package)))

(add-to-list 'display-buffer-alist
             '("\\`\\*\\(Warnings\\|Compile-Log\\)\\*\\'"
               (display-buffer-no-window)
               (allow-no-window . t)))

;;; Basic behaviour

(use-package delsel
  :ensure nil
  :hook (after-init . delete-selection-mode))

(defun prot/keyboard-quit-dwim ()
  "Do-What-I-Mean behaviour for a general `keyboard-quit'.

The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open.  Whereas we want it to close the
minibuffer, even without explicitly focusing it.

The DWIM behaviour of this command is as follows:

- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
  (interactive)
  (cond
   ((region-active-p)
    (keyboard-quit))
   ((derived-mode-p 'completion-list-mode)
    (delete-completion-window))
   ((> (minibuffer-depth) 0)
    (abort-recursive-edit))
   (t
    (keyboard-quit))))

(define-key global-map (kbd "C-g") #'prot/keyboard-quit-dwim)

;;; Tweak the looks of Emacs

;; Those three belong in the early-init.el, but I am putting them here
;; for convenience.  If the early-init.el exists in the same directory
;; as the init.el, then Emacs will read+evaluate it before moving to
;; the init.el.
(menu-bar-mode 1)
(scroll-bar-mode 1)
(tool-bar-mode -1)

(let ((mono-spaced-font "Monospace")
      (proportionately-spaced-font "Sans"))
  (set-face-attribute 'default nil :family mono-spaced-font :height 100)
  (set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
  (set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0))

(use-package modus-themes
  :ensure t
  :config
  (load-theme 'modus-vivendi-tinted :no-confirm-loading))

;; Remember to do M-x and run `nerd-icons-install-fonts' to get the
;; font files.  Then restart Emacs to see the effect.
(use-package nerd-icons
  :ensure t)

(use-package nerd-icons-completion
  :ensure t
  :after marginalia
  :config
  (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))

(use-package nerd-icons-corfu
  :ensure t
  :after corfu
  :config
  (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))

(use-package nerd-icons-dired
  :ensure t
  :hook
  (dired-mode . nerd-icons-dired-mode))

;;; Configure the minibuffer and completions

(use-package vertico
  :ensure t
  :hook (after-init . vertico-mode))

(use-package marginalia
  :ensure t
  :hook (after-init . marginalia-mode))

(use-package orderless
  :ensure t
  :config
  (setq completion-styles '(orderless basic))
  (setq completion-category-defaults nil)
  (setq completion-category-overrride nil))

(use-package savehist
  :ensure nil ; it is built-in
  :hook (after-init . savehist-mode))

(use-package corfu
  :ensure t
  :hook (after-init . global-corfu-mode)
  :bind (:map corfu-map ("<tab>" . corfu-complete))
  :config
  (setq tab-always-indent 'complete)
  (setq corfu-preview-current nil)
  (setq corfu-min-width 20)

  (setq corfu-popupinfo-delay '(1.25 . 0.5))
  (corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'

  ;; Sort by input history (no need to modify `corfu-sort-function').
  (with-eval-after-load 'savehist
    (corfu-history-mode 1)
    (add-to-list 'savehist-additional-variables 'corfu-history)))

;;; The file manager (Dired)

(use-package dired
  :ensure nil
  :commands (dired)
  :hook
  ((dired-mode . dired-hide-details-mode)
   (dired-mode . hl-line-mode))
  :config
  (setq dired-recursive-copies 'always)
  (setq dired-recursive-deletes 'always)
  (setq delete-by-moving-to-trash t)
  (setq dired-dwim-target t))

(use-package dired-subtree
  :ensure t
  :after dired
  :bind
  ( :map dired-mode-map
    ("<tab>" . dired-subtree-toggle)
    ("TAB" . dired-subtree-toggle)
    ("<backtab>" . dired-subtree-remove)
    ("S-TAB" . dired-subtree-remove))
  :config
  (setq dired-subtree-use-backgrounds nil))

(use-package trashed
  :ensure t
  :commands (trashed)
  :config
  (setq trashed-action-confirmer 'y-or-n-p)
  (setq trashed-use-header-line t)
  (setq trashed-sort-key '("Date deleted" . t))
  (setq trashed-date-format "%Y-%m-%d %H:%M:%S"))
-1:-- Emacs: a basic and capable configuration (Post)--L0--C0--November 28, 2024 12:00 AM

Alvaro Ramirez: ob-chatgpt-shell goes multi-model too

27 November 2024 ob-chatgpt-shell goes multi-model too

A week ago, I announced chatgpt-shell going multi-model. What I failed to mention is that because ob-chatgpt-shell (its org babel Emacs cousin) relies on chatgpt-shell, this babel package has now gone multi-model also.

babel-multi-model.gif

ob-chatgpt-shell follows the familiar babel form. To swap models, use the existing :version param as follows.

#+begin_src chatgpt-shell :results output :version gpt-4o
  Who built you?
#+end_src

#+RESULTS:
: I was developed by OpenAI, a research organization focused on creating and promoting friendly AI for the benefit of all humanity.

#+begin_src chatgpt-shell :results output :version claude-3-5-sonnet-20240620
  Who built you?
#+end_src

#+RESULTS:
: I was created by Anthropic.

#+begin_src chatgpt-shell :results output :version qwen2.5-coder
  Who built you?
#+end_src

#+RESULTS:
: I was built by Alibaba Cloud. How can I assist you today?

#+begin_src chatgpt-shell :results output :version gemini-1.5-pro-latest
  Who built you?
#+end_src

#+RESULTS:
: I was built by Google.  More specifically, I'm a large language model, trained by Google.

Keep in mind that :version depends on chatgpt-shell-models to resolve its models. You may need to add other models. If you add new ones, consider contributing a pull request, so we all benefit from the addition.

Should ob-chatgpt-shell rename?

See this.

Please report issues

In addition to being a fairly new feature, chatgpt-shell multi-model support required quite a few structural changes. We may still need additional polishing follow-ups. If you encounter any issues please report them.

Make this project sustainable

Maintaining, experimenting, implementing feature requests, and supporting open-source packages takes work. Today, chatgpt-shell has roughly 21.5K downloads on MELPA and many untracked elsewhere. If you're one of the happy users, consider sponsoring the project. If you see potential, help fuel development by sponsoring too.

Perhaps you enjoy some of the content I write about? Find my Emacs posts/tips useful?

Alternatively, you want a blogging platform that skips the yucky side effects of the modern web?

Maybe you enjoy one of my other projects?

So, umm… I'll just leave my GitHub sponsor page here.

-1:-- ob-chatgpt-shell goes multi-model too (Post)--L0--C0--November 27, 2024 02:55 PM

Irreal: Digital Vs. Analog Notes Redux

My post on Digital Vs. Analog Notes got some excellent comments as well as a followup post from JTR on his site. JTR mostly agrees with what I wrote but I did get some pushback in the comments on Irreal.

J Tevq has a long and thoughtful comment on why he sometimes prefers handwritten notes to digital. He’s a graduate student in Mathematics so this makes sense. I’ve always found that I can’t do any serious Mathematics on a computer. I need to scribble around on a pad to prove whatever it is before I turn to the computer to write it up.

He also, like many people, likes to read papers on paper. Some folks say that they simply can’t read material like that on a computer.

There are, he says, lots of places where handwritten notes are better. I was like that for a long time but managed to wean myself away from pencil and paper on my journey to a digital life.

MarkB likes to combine the two methods by (essentially) taking handwritten notes and digitizing them later. Actually, of course, this is all done on an iPad with an App that accepts handwritten notes and digitizes them on the fly.

My favorite App for that is Notability. Ali Abdaal has a great video on how he used it to take notes as a medical student. Abdaal also explains the (scientific) reasons for preferring handwritten notes for class and perhaps for other uses as well. Take a look at the video. It changed how I thought about note taking even though I am no longer in a classroom.

Since I do all my writing in Org mode, having notes, papers, and other materials in digital form means it’s easy to import or link to them as I’m writing. For formal journals you still have to make old styles references but, really, hypertext references are so much better. Of course, others feel differently. As always, the best method is the one that works for you.

Anyway, if you’re interested in the subject, do take a look at the comments and JTR’s post. They’re thoughtful and informative.

-1:-- Digital Vs. Analog Notes Redux (Post jcs)--L0--C0--November 26, 2024 04:57 PM

Sacha Chua: 2024-11-25 Emacs news

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

View org source for this post
-1:-- 2024-11-25 Emacs news (Post Sacha Chua)--L0--C0--November 26, 2024 01:09 AM

Alvaro Ramirez: LLM iterate and insert

25 November 2024 LLM iterate and insert

chatgpt-shell includes a couple of mechanisms to operate on an Emacs buffer region. That is, select a region and ask the LLM robots to modify it for us. Until now, both of these mechanisms didn't quite close the loop. They could either modify current region or iterate on a separate solution, but never both.

M-x chatgpt-shell-quick-insert

While chatgpt-shell's quick insert mechanism already enabled selecting a region and requesting changes, it was more of a "I'm feeling lucky" situation. If the changes didn't pan out, you'd have to discard the suggestion and start over.

With the latest changes, in addition to accepting or discarding (y/n bindings) suggestions, we can now iterate using the i binding.

quick-insert-iterate.gif

M-x chatgpt-shell-prompt-compose

While quick insertions rely on minibuffer input, chatgpt-shell-prompt-compose opens a dedicated buffer for a more thorough interactions. Select a region, invoke chatgpt-shell-prompt-compose (my preferred binding being C-c C-e), and a new compose buffer is created with the region text. Add your instructions and submit with C-c C-c. Post submission, the compose buffer becomes read-only and single-character key-bindings take over. For example: r replies (for further iteration) and n/p (or TAB/shift-TAB) navigation. There are more bindings.

Until now, we could easily craft more involved queries and continue iterating from the compose buffer, but integrating suggested changes was a manual process. That is, navigate to a code block, select, copy it and then paste it elsewhere.

With the latest changes, pressing i (insert) while on a code block will attempt to insert it wherever the initial interaction took place.

compose-insert-iterate.gif

Both of these changes show now be available on MELPA. While I demoed ChatGPT, the two mechanism should now work with any of the supported models (ChatGPT, Claude, Gemini, and Ollama).

-1:-- LLM iterate and insert (Post)--L0--C0--November 25, 2024 06:29 PM

Charles Choi: Announcing Casual Calendar

On first impression, it’s a mixed bag using the Emacs built-in calendar & diary, as they both under and over-deliver on features. Users more familiar with GUI-based calendar and journal apps are underwhelmed, as capturing and displaying daily activity with it seem rudimentary at best. Hidden though in calendar is its support for non-Gregorian calendar systems whose inclusion and history are both amazing and idiosyncratic. As common with Emacs modes, calendar & diary rely on user knowledge of its arcane commands and keybindings to make effective use of them. I think such reliance impedes more widespread usage of them, particularly with regards to calendar system conversion and anniversary entry. Given this, I decided to make a Transient-based user interface for calendar & diary.

Announcing Casual Calendar, now available in the Casual v2.2 update distributed on MELPA.

In this interface, emphasis has been given to the following workflows:

  • Navigation
  • Conversion of Gregorian dates to non-Gregorian calendar systems
  • Visualizing holidays
  • Viewing and creating diary events
  • Viewing almanac information (lunar phases, sunrise/sunset times)
  • Configuration of calendar & diary settings

Why Use Calendar & Diary?

Though I have initially described calendar & diary with faint praise, I do strongly recommend getting to know them better. Their utility shows up in many (often surprising) situations where you need to work with dates. Also, their integration into Org Mode alone makes it worth the effort.

My most common use cases for calendar & diary are:

  • Lightweight visualization of months within Emacs (no need to leave Emacs to see a basic calendar!)
  • Keeping track of periodic events that do not require notes (e.g. birthdays, anniversaries) in Org Agenda
  • Org mode timestamp selection

If you share any of these use cases, consider bringing calendar & diary into your Emacs journey.

A notable feature of diary is that it can track periodic events for some non-Gregorian calendars, in particular Bahá’í, Hebrew, Islamic, and Lunar (Chinese). If you find cause to need this feature, you’ll be thankful that you are an Emacs user.

Customizing Calendar & Diary

Both calendar & diary have taken a austere aesthetic when it comes to default settings, where they show the strong influence of the Unix cal command line utility by not visually highlighting holidays or days with diary entries. Users who prefer a richer presentation can follow the following customizations.

To highlight holidays, go to the Settings menu “(,) Settings” and enable “(H) Mark Holidays”.

To highlight diary entries, enable “(E) Mark Diary Entries”.

As sunrise/sunset and lunar phase information is based on location, configuring all the settings in the Location section is advised to enable those features.

Org Agenda users will likely want to include diary integration. Turn this on using the “(d) Include Diary” setting.

An interactive enhancement when navigating (moving) in the calendar window is to add the function call (diary-view-entries 1) to the “(v) Move Hook” setting. This should be called as a lambda expression. In the customize window, it should look like this:

To turn on support for non-Gregorian calendar system diary entries, the two settings “(L) List” and “(M) Mark” must be configured in the section Non-Gregorian Hooks. Note that if you are working with Emacs 29.4 or earlier, you will only see checkboxes for the Bahá’í, Hebrew, and Islamic calendar systems. To get Lunar (Chinese) support requires manual entry of the values diary-chinese-list-entries and diary-chinese-mark-entries for “(L) List” and “(M) Mark” respectfully. Thankfully, the upcoming official Emacs 30 release will fix this.

Closing Thoughts

Suffice to say, it is fundamentally human to mark the passage of time. Emacs, by including calendar & diary acknowledges this obvious fact. Incorporating calendar & diary into my Emacs workflows has yielded considerable benefits, sometimes surprisingly so. My motivation for building Casual Calendar was to make calendar & diary easier to use. Perhaps you’ll share the same sentiment.

Some Self-Promotion

If you enjoy using any of the Casual packages I’ve published over the past year (Agenda, Bookmarks, Calc, Calendar, Dired, EditKit, IBuffer, Info, I-Search, Re-Builder), please consider “starring” the project on GitHub if you already haven’t done so. Financial support is also welcome as it helps the development and maintenance of Casual. Please consider buying me a coffee to provide it. Thank you.

References

-1:-- Announcing Casual Calendar (Post Charles Choi)--L0--C0--November 25, 2024 05:00 PM

Chris Maiorana: My Freewrite Mode

I once read somewhere that P.G. Wodehouse wrote 2,000-3,000 words a day at his peak, but that number crept down to about 1,000 in his 90s, at the end of a long and highly productive career.

I’ve always been interested in the correlation between raw written output and success in writing. Is there such a correlation? If so, how does one encourage output and limit distraction?

There are many different strategies, including brainstorming, which I find interesting. One of the most productive forms of brainstorming, particularly for writing, is something called freewriting. I’ve experimented with this many times over the past few years. What I like is, you can do it on pen and paper, but also on a computer.

To that end, I wondered if there were any little tricks and dodges to encourage a freewriting habit in my text editor, where I do the majority of my writing.

So I devised a little way of doing this in Emacs. And this one I think I will share because it’s pretty basic and universal.

What is Freewriting?

If you don’t know what freewriting is: it’s a brainstorming technique for unlocking new ideas. It’s pretty simple. Goes something like this:

  1. Set a timer
  2. Start writing as quickly as you can without pausing or hesitating
  3. Stop when timer elapses

The key discipline of freewriting is to avoid the temptation to censor or edit yourself. Let the ideas flow without interruption.

It’s easier to do on paper. The only problem is when your hand starts to cramp up.

Most of us who work on keyboards for a living are fast typists. So our computers provide a good avenue for freewriting. However, it’s all too easy to smack that Backspace key, either because you made a typo or you simply don’t like what you just wrote.

Trying a Freewrite Mode In Emacs

To combat the temptation to backspace during a freewrite session, I’ve created a little minor mode for Emacs that disables the backspace key and triggers a beep and message when you hit it. (Though, the beep is not working on my computer, but the message appears just fine.)

Most of you Emacs adepts can probably think of more efficient way of achieving the same effect, but this is working nicely for me, and I like how the creation of the minor mode allows for future iterations.

I particularly like the message that flashes, “You can’t do that!” in the minibuffer.

(defun freewrite-backspace-warning ()
  "Warn the user with a beep and a message when backspace is pressed."
  (interactive)
  (beep) ;; Make the computer beep
  (message "You can't do that!")) ;; Display a warning message

(defvar freewrite-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [backspace] #'freewrite-backspace-warning) ;; Remap backspace to custom function
    map)
  "Keymap for `freewrite-mode'.")

(define-minor-mode freewrite-mode
  "A minor mode where backspace is disabled, and a warning is shown instead."
  :lighter " FreeWrite"
  :keymap freewrite-mode-map)

(provide 'freewrite-mode)

Likewise, all you Emacs adepts know that there a dozen others ways to delete besides the backspace key. I’ve chosen the backspace key here because it’s the most reflexive reaction, I find. This little expedient has helped me up my word count and silence that ever present critical voice that gets in the way of composition.

For future iterations, I’d like to get a little Dennis Nedry popup to scold me.

Try it out, let me know if you have taken similar steps in your work to encourage output.

-1:-- My Freewrite Mode (Post chris)--L0--C0--November 25, 2024 12:50 PM

Irreal: Plugins Again

I thought we had settled this once and for all but apparently not. Over at the Emacs subreddit, sudhirkhanger asks about VS Code’s extension system as compared to Emacs’. He’s “blown away” at how great the VS Code plugin system is. He’s amazed at the integration and says it’s almost like using a native application.

Many of the comments make the point that VS Code is not really free software with all the problems that that implies but I don’t want discuss that aspect. What I’m interested in is the same thing that sudhirkhanger is: the relative merits of VS Code’s extension system versus what Emacs does instead.

Notice that I didn’t say “Emacs’ extension system”. That’s because Emacs doesn’t have an “extension language” as the term is usually understood. Rather, Emacs is a Lisp machine and that means that you can change or add to the core functionality on the fly. That’s different from being merely “open source”. You don’t have to edit and recompile. You just write some code, evaluate it, and suddenly the editor is doing something different. You’re not restricted to what some extension API allows. Your code has the same status as—and, indeed, is indistinguishable from—the code that ships with Emacs.

The takeaway is that if your insist on comparing VS Code and Emacs please don’t do so from the point of view of the extension system. Emacs doesn’t have one. You simply change or add to the base code without any “by your leave” from the developers. The way you “extend” VS Code and Emacs are not comparable. Your aren’t talking about the same thing anymore than you are when—to invoke a cliche—you compare apples and oranges.

-1:-- Plugins Again (Post jcs)--L0--C0--November 23, 2024 05:36 PM

Irreal: Some Configuration To Solve Common Problems

Remember how, when you first started using Emacs, there were all those strange defaults that just didn’t seem right. The worst one, for me, was scrolling. I hated the way the screen would jump when you got near the top or bottom. That feature alone caused me to abandon Emacs more than once.

Of course, like everything in Emacs, that’s configurable but it’s hard for n00bs to know what to do about such things. Sooner or later we all discovered the magic spells to get things more to our liking.

Over at The Emacs Cat, there’s a nice post that shows how to configure things in what many of us consider a saner way. Decent scrolling is covered as is making Emacs Emacs talk UTF-8 exclusively. Both of those took me a long time and a lot of experimentation to get right.

There’s also a section on getting rid of the common annoyances such as having to type “yes” instead of simply “y”, making tabs sane, getting rid of the bell and tool bar, and—for those of you who love controversy—ending sentences with a period and single space.

There’s also a section on some useful modes that you can enable, such as winner-mode, global-hl-line-mode, desktop-save-mode, and help-window-select. I didn’t know about that last one but I’ve frequently been annoyed that I have to switch over to the Help window so it seems useful.

There are a few more useful tweaks that you may or may not want. I found it surprising how closely Emacs Cat’s choices echoed mine. We don’t agree on everything but it’s surprising how much we do agree on. In any event, take a look and see if there’s an answer to a problem you’re having.

-1:-- Some Configuration To Solve Common Problems (Post jcs)--L0--C0--November 22, 2024 05:06 PM

The Emacs Cat: Migration to Bluesky

After reading the quite interesting post Blue Sky by Mike Zamansky I decided to begin my gentle migration to Bluesky, a new and promising social media platform.

I completely agree with Mike’s opinion on Twitter as well as other social platforms. Like many of us, I migrated from Twitter – keeping my existing account there – to Mastodon. Unfortunately, Mastodon, largely due to its distributed architecture, seems to be more fragile than other platforms – recently, I have been warned that emacs.ch, a fediverse server I have been using for two years now, is about to shutdown in this December.

Sure, there is a (bit complex) way to transfer your Mastodon account to another server – and here is detailed explanation on how to do that. Perhaps, I will do that in the nearest future, but who can guarantee that the new server is immune from the same fate?

Anyway, Bluesky looks promising and I decided to give it a try. You can find me at @olddeuteronomy.bsky.social.

-1:-- Migration to Bluesky (Post)--L0--C0--November 22, 2024 10:23 AM

Alvaro Ramirez: Toggle macOS menu bar from you know where

22 November 2024 Toggle macOS menu bar from you know where

I'm a fan of macOS's auto-hide menu bar setting. Unless I'm reaching out to a menu item, I don't typically need to have a visible menu bar, so I set auto-hide to "Always".

control-center.png

On rare occasions, I turn this setting off (say for a screen grab). While reaching out to macOS Control Center is OK, I wanted to quickly toggle it from the comfort of Emacs via M-x fuzzy search.

We can leverage AppleScript to toggle the setting on and off. In the past, I would write shell scripts for this kinda thing and invoke from the command line. These days, I dump them into a dwim-shell-command and quickly forget about its implementation. From then on, I just M-x and fuzzy search for some magical incantation (I'm looking at you ffmpeg). I got a bunch of these things

(defun  dwim-shell-commands-macos-toggle-menu-bar-autohide ()
   "Toggle macOS menu bar auto-hide."
  (interactive)
  (dwim-shell-command-on-marked-files
    "Toggle menu bar auto-hide."
    "current_status=$(osascript -e 'tell application \"System Events\" to get autohide menu bar of dock preferences')

 if [ \"$current_status\" = \"true\" ]; then
     osascript -e 'tell application \"System Events\" to set autohide menu bar of dock preferences to false'
     echo \"Auto-hide disabled.\"
 else
     osascript -e 'tell application \"System Events\" to set autohide menu bar of dock preferences to true'
     echo \"Auto-hide enabled.\"
 fi"
    :utils  "osascript"
    :silent-success t))

…and that's all there is to it.

autohide.webp

-1:-- Toggle macOS menu bar from you know where (Post)--L0--C0--November 22, 2024 09:41 AM

Alvaro Ramirez: chatgpt-shell goes offline

21 November 2024 chatgpt-shell goes offline

Since chatgpt-shell going multi-model, it was only a matter of time until we added support for local/offline models. As of version 2.0.6, chatgpt-shell has a basic Ollama implementation (llama3.2 for now).

chatgpt-shell is more than a shell. Check out the demos in the previous post.

who-offline.gif

For anyone keen on keeping all their LLM interactions offline, Ollama seems like a great option. I'm an Ollama noobie myself and already looking forward to getting acquainted. Having an offline LLM available at my Emacs fingertips will be super convenient.

For the more familiar with Ollama, please give the chatgpt-shell integration a try (it's on MELPA).

v2.0.6 has a basic/inital Ollama implementation. Please give it a good run and report bugs.

You can swap models via:

M-x chatgpt-shell-swap-model

Help make this project sustainable. Sponsor the work.

-1:-- chatgpt-shell goes offline (Post)--L0--C0--November 21, 2024 09:21 PM

Emacs APAC: CANCELED Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, November 23, 2024

Emacs Asia-Pacific (APAC) virtual meetup will not be happening this month. You can join us for the EmacsConf 2024:

-1:-- CANCELED Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, November 23, 2024 (Post)--L0--C0--November 21, 2024 07:41 PM

Sacha Chua: Remove filler words at the start and upcase the next word

[2024-11-21 Thu]: Fixed the second filler words regexp, and make it work at the start of lines too. Thanks to @arialdo@mastodon.online for the feedback!

Like many people, I tend to use "So", "And", "You know", and "Uh" to bridge between sentences when thinking. WhisperX does a reasonable job of detecting sentences and splitting them up anyway, but it leaves those filler words in at the start of the sentence. I usually like to remove these from transcripts so that they read more smoothly.

Here's a short Emacs Lisp function that removes those filler words when they start a sentence, capitalizing the next word. When called interactively, it prompts while displaying an overlay. When called from Emacs Lisp, it changes without asking for confirmation.

(defvar my-filler-words-regexp "\\(\\. \\|^\\)\\(?:So?\\|And\\|You know\\|Uh\\)\\(?:,\\|\\.\\.\\.\\)? \\(.\\)")
(defun my-remove-filler-words-at-start ()
  (interactive)
  (save-excursion
    (let ((case-fold-search nil))
      (while (re-search-forward my-filler-words-regexp nil t)
        (if (and (called-interactively-p) (not current-prefix-arg))
            (let ((overlay (make-overlay (match-beginning 0)
                                         (match-end 0))))
              (overlay-put overlay 'common-edit t)
              (overlay-put
               overlay 'display
               (propertize (concat (match-string 0) " -> "
                                   (match-string 1)
                                   (upcase (match-string 2)))
                           'face 'modus-themes-mark-sel))
              (unwind-protect
                  (pcase (save-match-data (read-char-choice "Replace (y/n/!/q)? " "yn!q"))
                    (?!
                     (replace-match (concat (match-string 1) (upcase (match-string 2))) t)
                     (while (re-search-forward my-filler-words-regexp nil t)
                       (replace-match (concat (match-string 1) (upcase (match-string 2))) t)))
                    (?y
                     (replace-match (concat (match-string 1) (upcase (match-string 2))) t))
                    (?n nil)
                    (?q (goto-char (point-max))))
                (delete-overlay overlay)))
          (replace-match (concat (match-string 1) (upcase (match-string 2))) t))))))
This is part of my Emacs configuration.
View org source for this post
-1:-- Remove filler words at the start and upcase the next word (Post Sacha Chua)--L0--C0--November 21, 2024 06:06 PM

Chris Maiorana: Some Custom Emacs Functions That Are Changing My Game

I have some new, magic commands in Emacs that have become very dear to me in my daily workflow. As you all know, my focus is producing written content, not programming, so these functions were designed to help with that. As always, I don’t share the code because it was mostly produced with language models and my own tinkering, and while they work for me, I can’t vouch for the quality.

Also, these functions are specific to my workflow, so they may not apply to your system. I will share the ideas in case they trigger ideas for you.

Additionally, I’d like to know how you’re using Emacs to solve everyday logistical problems in your work. Feel free to comment below the post.

“Office Export” Function With Template Selection

As I mentioned above, I use Org Mode for writing documents: articles, blog posts, fiction, and virtually anything else I need to write.

One of the key benefits of writing in plain text, and with a markup syntax like Org Mode, is that I can write one document and export it to multiple other types of documents. Every destination for a piece of writing demands a slightly different deliverable format. It can often be difficult, or downright dizzying, to keep track of the unique specifications of different publications.

Writing for the Internet, I can usually do a simple HTML export, but sometimes Google Docs is expected.

For fiction writing, different publications may want the standard manuscript format (12-point, double-spaced, Times New Roman font) or something unique to their own editorial processes. Instead of having to format and re-format in LibreOffice, I’ve set up templates for publications I submit to regularly. So I just run my offfice-export function, and it asks me some questions, with yes or no prompts, and then prompts me to select which template I want to use.

The point is not only to save a few minutes of time here and there, or to be lazy, but also to avoid errors that will inevitably happen with constant formatting changes. One simple export from a master document simplifies the whole process.

Org Mode Jump

There is an existing function for jumping around an Org Mode document. I wrote my function to do something similar. My org jump function prints out all my headings (with their tags) in the minibuffer, and I can select one to “jump” to. With the heading and the tag, and with orderless completion installed, I can easily filter for exactly which heading I want, jump to it, make any edits I need to make, and then jump back to where I was before. This saves me from a lot of needless scrolling in a longer document.

I really like how the function preserves the syntax highlighting:


I want to do a video covering these functions. However, I have been plugging away daily at National Novel Writing Month and sending out new short stories for prospective publication. Videos will return soon. If you’d like to receive occasional updates from me, you can  subscribe here.

-1:-- Some Custom Emacs Functions That Are Changing My Game (Post chris)--L0--C0--November 21, 2024 04:48 PM

Thanos Apollo: org-gnosis | Roam-like note taking system

org-roam was my preferred tool for note taking & journaling, but since it’s author seems to have stepped down & it’s implementation is quite different from how I’d approach making a zettelkasten for Emacs, I decided to start working on a new package, named org-gnosis.

Goals of this project

The primary objective is to achieve full compatibility with existing org-roam notes while resolving current database implementation issues. These goals were met successfully on the first day of development.

Given that I plan to use this system for the next several years, performance with large datasets is crucial. I have conducted tests on org-gnosis with a dataset comprising 70,000 nodes, and it has demonstrated flawless performance without any issues. Although, the initialization can take sometime.

Beyond just performance, I want to keep it small and simple, especially in terms of lines of code. The less lines of code I have to maintain the happier I am :)

How to install

This project is only 26 hours old as of now, I have not published it on any ELPA, but you can find it on this git repo, feel free to clone it and try it out.

Example use-package snippet

(use-package org-gnosis
  :load-path "~/Dev/emacs-lisp/org-gnosis"
  :init
  (define-prefix-command 'thanos/notes-map)
  (define-prefix-command 'thanos/journal-map)
  :config
  (setf org-gnosis-dir "~/Notes"
	org-gnosis-show-tags t) ;; Enable this if you use a completion system like vertico
  :bind (("C-c n" . thanos/notes-map)
	 ("C-c j" . thanos/journal-map) ;; You can use "C-c n j" if you prefer
	 :map thanos/notes-map
         ("f" . org-gnosis-find)
	 ("i" . org-gnosis-insert)
	 :map thanos/journal-map
	 ("j" . org-gnosis-journal)
	 ("f" . org-gnosis-journal-find)
	 ("i" . org-gnosis-journal-insert)
	 :map org-mode-map
	 ("C-c i" . org-id-get-create)))

After installing org-gnosis run M-x org-gnosis-db-sync to sync your database, afterwards just use org-gnosis-find similarly to how you’d use org-roam-find-node.

This package is still under development, if you encounter any bugs feel free to email me.

-1:-- org-gnosis | Roam-like note taking system (Post)--L0--C0--November 21, 2024 12:00 AM

Alvaro Ramirez: chatgpt-shell goes multi-model

20 November 2024 chatgpt-shell goes multi-model

Over the last few months, I've been chipping at implementing chatgpt-shell's most requested and biggest feature: multi-model support. Today, I get to unveil the first two implementations: Anthropic's Claude and Google's Gemini.

Changing course

In the past, I envisioned a different path for multi-model support. By isolating shell logic into a new package ( shell-maker), folks could use it as a building block to create new shells (adding support for their favourite LLM).

While each shell-maker-based shell currently shares a basic common experience, I did not foresee the minor differences affecting the general Emacs user experience. Learning the quirks of each new shell felt like unnecessary friction in developing muscle memory. I also became dependent on chatgpt-shell features, which I often missed when using other shells.

Along with slightly different shell experiences, we currently require multiple package installations (and setups). Depending on which camp you're on (batteries included vs fine-grained control) this may or may not be a downside.

With every new chatgpt-shell feature I showcased, I was often asked if they could be used with other LLM providers. I typically answered with "I've been meaning to work on this…" or "I heard you can do multi-model chatgpt-shell using a bridge like liteLLM". Neither of these where great answers, resulting in me just postponing the chunky work.

Eventually, I bit the bullet, changed course, and got to work on multi-model support. With my initial plan to spin multiple shells via shell-maker, chatgpt-shell's implementation didn't exactly lend itself to support multiple clients. Long story short, chatgpt-shell multi-model support required quite a bit of work. This where I divert to ask you to help make this project sustainable by sponsoring the work.

Make this project sustainable

Maintaining, experimenting, implementing feature requests, and supporting open-source packages takes work. Today, chatgpt-shell has over 20.5K downloads on MELPA and many untracked others elsewhere. If you're one of the happy users, consider sponsoring the project. If you see potential, help fuel development by sponsoring too.

Perhaps you enjoy some of the content I write about? Find my Emacs posts/tips useful?

Alternatively, you want a blogging platform that skips the yucky side effects of the modern web?

Maybe you enjoy one of my other projects?

So, umm… I'll just leave my GitHub sponsor page here.

chatgpt-shell, more than a shell

With chatgpt-shell being a comint shell, you can bring your favourite Emacs flows along.

As I used chatgpt-shell myself, I kept experimenting with different integrations and improvements. Read on for some of my favourites…

A shell hybrid

chatgpt-shell includes a compose buffer experience. This is my favourite and most frequently used mechanism to interact with LLMs.

For example, select a region and invoke M-x chatgpt-shell-prompt-compose ( C-c C-e is my preferred binding), and an editable buffer automatically copies the region and enables crafting a more thorough query. When ready, submit with the familiar C-c C-c binding. The buffer automatically becomes read-only and enables single-character bindings.

Navigation: n/p (or TAB/shift-TAB)

Navigate through source blocks (including previous submissions in history). Source blocks are automatically selected.

Reply: r

Reply with with follow-up requests using the r binding.

Give me more: m

Want to ask for more of the same data? Press m to request more of it. This is handy to follow up on any kind of list (suggestion, candidates, results, etc).

Request entire snippets: e

LLM being lazy and returning partial code? Press e to request entire snippet.

Quick quick: q

I'm a big fan of quickly disposing of Emacs buffers with the q binding. chatgpt-shell compose buffers are no exception.

Confirm inline mods (via diffs)

Request inline modifications, with explicit confirmation before accepting.

Execute snippets (a la org babel)

Both the shell and the compose buffers enable users to execute source blocks via C-c C-c, leveraging org babel.

Vision experiments

I've been experimenting with image queries (currently ChatGPT only, please sponsor to help bring support for others).

Below is a handy integration to extract Japanese vocabulary. There's also a generic image descriptor available via M-x chatgpt-shell-describe-image that works on any Emacs image (via dired, image buffer, point on image, or selecting a desktop region).

Supporting new models

Your favourite model not yet supported? File a feature request. You also know how to fuel the project. Want to contribute new models? Reach out.

Local models

While the two new implementations rely on cloud APIs, local services are now possible. I've yet to use a local LLM, but please reach out, so we can make these happen too. Want to contribute?

Should chatgpt-shell rename?

With chatgpt-shell going multi-model, it's not unreasonable to ask if this package should be renamed. Maybe it should. But that's additional work we can likely postpone for the time being (and avoid pushing users to migrate). For now, I'd prefer focusing on polishing the multi-model experience and work on ironing out any issues. For that, I'll need your help.

Take Gemini and Claude for a spin

Multi-model support required chunky structural changes. While I've been using it myself, I'll need wider usage to uncover issues. Please take it for a spin and file bugs or give feedback. Or if you just want to ping me, I'd love to hear about your experience ( Mastodon / Twitter / Reddit / Email).

  • Be sure to update to chatgpt-shell v2.0.1 and shell-maker v0.68.1 as a minimum.
  • Set chatgpt-shell-anthropic-key or chatgpt-shell-google-key.
  • Swap models with existing M-x chatgpt-shell-swap-model-version or set a default with (setq chatgpt-shell-model-version "claude-3-5-sonnet-20240620") or (setq chatgpt-shell-model-version "claude-gemini-1.5-pro-latest").
  • Everything else should just work 🤞😅

Happy Emacsing!

-1:-- chatgpt-shell goes multi-model (Post)--L0--C0--November 20, 2024 03:59 PM

TAONAW - Emacs and Org Mode: Irreal on my writing habits

Irreal commented on my recent posts about writing (analog vs digital). Irreal doesn’t understand why I’m hesitant:

To be honest, I don’t understand his ambivalence about the matter. He lays out the case for both and shows that, except for a vague feeling of attraction to writing with pen and paper, the digital method is more efficient and satisfying. The digital product is so much more useful and flexible that it seems there should be no question as to which to use.

Spoken like a true sysadmin. But he’s mostly right.

Pen and paper convey an intimate feeling and a connection to what I write that I can’t get out of typing on the keyboard. It’s not about how fast or clear it is. But that’s the thing, it’s a feeling. At the end of the day, if I need to capture information and have it available to me whenever I need it, digital wins by a large margin.

Over the last two weeks, I’ve started to reap the benefits of returning to digital and fully utilizing org-mode.

Meetings notes full of details, organized by date and time; Projects I’m working on are broken down to smaller manageable tasks; floating emails and quick reminders quickly tie into a workflow that I can find later and connect to a system and don’t get forgotten or lost. I can slowly breath again, and I’m starting to find the fun in work again.

Meanwhile, I’m also able to write more on personal events. I don’t have to fully reflect on every event, as I would do in the hand-written journal. Instead, I now have an option of including a list of places I visited with a friend last night or perhaps a picture showing a fun activity. Sure, I could do that in my written journal, but it feels too special: I don’t want to “waste” the page on a simple list of locations. Digital just makes more sense for that, since my agenda with its events listed with details is not the same as my journal.

I don’t know, I guess we’ll see. I do miss the idea of the written journal just enough to pick it up again sooner rather than later.

-1:-- Irreal on my writing habits (Post)--L0--C0--November 20, 2024 01:59 PM

The Emacs Cat: Some Excerpts From My Emacs Config

I’m happy to be back after one year away and it feels great.

Below are some chaotic mini/micro – or even nano – excerpts from my ~/.emacs file I have been tuning in for 12 years. These days, I’m running Emacs 29.4 on Ubuntu (Pop!_OS) 22.04 and, rarely, on macOS.

-1:-- Some Excerpts From My Emacs Config (Post)--L0--C0--November 20, 2024 10:52 AM

Sacha Chua: Updating my audio braindump workflow to take advantage of WhisperX

I get word timestamps for free when I transcribe with WhisperX, so I can skip the Aeneas alignment step. That means I can update my previous code for handling audio braindumps . Breaking the transcript up into sections Also, I recently updated subed-word-data to colour words based on their transcription score, which draws my attention to things that might be uncertain.

Here's what it looks like when I have the post, the transcript, and the annotated PDF.

2024-11-17_20-44-30.png
Figure 1: Screenshot of draft, transcript, and PDF

Here's what I needed to implement my-audio-braindump-from-whisperx-json (plus some code from my previous audio braindump workflow):

(defun my-whisperx-word-list (file)
  (let* ((json-object-type 'alist)
         (json-array-type 'list))
    (seq-mapcat (lambda (seg)
                  (alist-get 'words seg))
                (alist-get 'segments (json-read-file file)))))

;; (seq-take (my-whisperx-word-list (my-latest-file "~/sync/recordings" "\\.json")) 10)
(defun my-whisperx-insert-word-list (words)
  "Inserts WORDS with text properties."
  (require 'subed-word-data)
  (mapc (lambda (word)
            (let ((start (point)))
              (insert
               (alist-get 'word word))
              (subed-word-data--add-word-properties start (point) word)
              (insert " ")))
        words))

(defun my-audio-braindump-turn-sections-into-headings ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "START SECTION \\(.+?\\) STOP SECTION" nil t)
    (replace-match
     (save-match-data
       (format
        "\n*** %s\n"
        (save-match-data (string-trim (replace-regexp-in-string "^[,\\.]\\|[,\\.]$" "" (match-string 1))))))
     nil t)
    (let ((prop-match (save-excursion (text-property-search-forward 'subed-word-data-start))))
      (when prop-match
        (org-entry-put (point) "START" (format-seconds "%02h:%02m:%02s" (prop-match-value prop-match)))))))

(defun my-audio-braindump-split-sentences ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "[a-z]\\. " nil t)
    (replace-match (concat (string-trim (match-string 0)) "\n") )))

(defun my-audio-braindump-restructure ()
  (interactive)
  (goto-char (point-min))
  (my-subed-fix-common-errors)
  (org-mode)
  (my-audio-braindump-prepare-alignment-breaks)
  (my-audio-braindump-turn-sections-into-headings)
  (my-audio-braindump-split-sentences)
  (goto-char (point-min))
  (my-remove-filler-words-at-start))

(defun my-audio-braindump-from-whisperx-json (file)
  (interactive (list (read-file-name "JSON: " "~/sync/recordings/" nil nil nil (lambda (f) (string-match "\\.json\\'" f)))))
  ;; put them all into a buffer
  (with-current-buffer (get-buffer-create "*Words*")
    (erase-buffer)
    (fundamental-mode)
    (my-whisperx-insert-word-list (my-whisperx-word-list file))
    (my-audio-braindump-restructure)
    (goto-char (point-min))
    (switch-to-buffer (current-buffer))))

(defun my-audio-braindump-process-text (file)
  (interactive (list (read-file-name "Text: " "~/sync/recordings/" nil nil nil (lambda (f) (string-match "\\.txt\\'" f)))))
  (with-current-buffer (find-file-noselect file)
    (my-audio-braindump-restructure)
    (save-buffer)))
;; (my-audio-braindump-from-whisperx-json (my-latest-file "~/sync/recordings" "\\.json"))

Ideas for next steps:

  • I can change my processing script to split up the Whisper TXT into sections and automatically make the PDF with nice sections.
  • I can add reminders and other callouts. I can style them, and I can copy reminders into a different section for easier processing.
  • I can look into extracting PDF annotations so that I can jump to the next highlight or copy highlighted text.
This is part of my Emacs configuration.
View org source for this post
-1:-- Updating my audio braindump workflow to take advantage of WhisperX (Post Sacha Chua)--L0--C0--November 19, 2024 01:33 PM

Irreal: Digital Vs. Analog Notes

Over at The Art Of Not Asking Why, JTR has a couple of posts that explore his struggle with deciding between digital and analog note taking. None of you will be surprised where I come down on the issue—I’m all in on living a digital life and eschew using pen and paper as much as I can—but it’s informative to read about JTR’s thought process about how he decides which method to use.

To be honest, I don’t understand his ambivalence about the matter. He lays out the case for both and shows that, except for a vague feeling of attraction to writing with pen and paper, the digital method is more efficient and satisfying. The digital product is so much more useful and flexible that it seems there should be no question as to which to use.

One thing he says that really resonated with me is that if he writes a lot with a pen, his hand cramps. That definitely happens to me too. Related to that is speed.

When I was still young and was hunt and pecking on an actual typewriter, an adult told me that it was really hard to type faster than you can write by hand. That seems laughable to me now. I can type much faster than I can write. A lot of that is probably because my handwriting is so bad that I print everything but it’s still a fact.

I, too, like the idea of sitting down with a beautiful paper journal and good pen but the results aren’t that useful. I can’t back them up. I can’t carry them on my iPhone. I can’t easily link them to and from other notes.

I write virtually everything in Org mode. The main exception is the memo book that resides on my iPhone and I simply import those notes directly into an Org mode table so that they, too, end up living in Org. All of this is automatically backed up, searchable, portable to my iPhone, and easy to link to.

If you really need the feel of pen on paper, take up calligraphy. It will satisfy your need for handwriting without sacrificing the usefulness of your notes.

-1:-- Digital Vs. Analog Notes (Post jcs)--L0--C0--November 18, 2024 05:07 PM

Sacha Chua: 2024-11-18 Emacs news

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

View org source for this post
-1:-- 2024-11-18 Emacs news (Post Sacha Chua)--L0--C0--November 18, 2024 02:29 PM

James Dyer: Reducing Friction when Copying Whole Buffer To Kill Ring

Just a quick one today.

In keeping with the ongoing effort to reduce friction in the venerable Emacs text editor, I realized that a common action I often perform is copying the entire contents of the buffer, usually for pasting elsewhere.

To perform this I have chained together some emacs commands, namely (mark-whole-buffer) and then (kill-ring-save)

The problem with pushing the buffer to the kill ring in this manner is that I lose the current cursor position/point and end up using isearch to navigate my way back. Strangely, it is only recently that I have found this annoying!

There are a few options to address this:

  • Use Emacs marks
  • Create a macro
  • Create a defun

Initially I tried the setting mark option meaning that I C-<SPC> C-<SPC> to set the mark at the current position and then C-u C-<SPC> to pop back to my previous mark set. The only issue is that (mark-whole-buffer) creates a mark at the end of the selected region and my first mark pop would be to this position, so I have to mark pop again.

The benefit of this approach is that I will start becoming more familiar with setting marks and navigating more efficiently within Emacs, which I really think I should learn. However, it all feels a little clunky, and you know what? I’m just going to write a simple elisp defun and bind it.

save-excursion, in this case, can be extremely useful!

(defun my/copy-buffer-to-kill-ring ()
  "Copy the entire buffer to the kill ring without changing the point."
  (interactive)
  (save-excursion
    (kill-ring-save (point-min) (point-max))))

(bind-key* (kbd "M-s z") #'my/copy-buffer-to-kill-ring)
-1:-- Reducing Friction when Copying Whole Buffer To Kill Ring (Post James Dyer)--L0--C0--November 18, 2024 10:24 AM

Marcin Borkowski: Discovering functions and variables in Elisp files

Sometimes I have an Elisp file which I suspect contains some useful functions. Even if the file is well-documented (for example, it belongs to Emacs itself), that does not mean that every function in it is described in the manual. What I need in such a case is a list of functions and variables (possibly also macros) defined in this file.
-1:-- Discovering functions and variables in Elisp files (Post)--L0--C0--November 18, 2024 05:41 AM

Sacha Chua: Changing Org Mode underlines to the HTML mark element

Apparently, HTML has a mark element that is useful for highlighting. ox-html.el in Org Mode doesn't seem to export that yet. I don't use _ to underline things because I don't want that confused with links. Maybe I can override org-html-text-markup-alist to use it for my own purposes…

(with-eval-after-load 'org
  (setf (alist-get 'underline org-html-text-markup-alist)
        "<mark>%s</mark>"))

Okay, let's try it with:

Let's see _how that works._

Let's see how that works. Oooh, that's promising.

Now, what if I want something fancier, like the way it can be nice to use different-coloured highlighters when marking up notes in order to make certain things jump out easily? A custom link might come in handy.

(defun my-org-highlight-export (link desc format _)
  (pcase format
    ((or '11ty 'html)
     (format "<mark%s>%s</mark>"
             (if link
                 (format " class=\"%s\"" link)
               link)
             desc))))
(with-eval-after-load 'org
  (org-link-set-parameters "hl" :export 'my-org-highlight-export)
  )

A green highlight might be good for ideas, while red might be good for warnings. (Idea: I wonder how to font-lock them differently in Emacs…)

I shouldn't rely only on the colours, since people reading through RSS won't get them and also since some people are colour-blind. Still, the highlights could make my blog posts easier to skim on my website.

Of course, now I want to port Prot's excellent colours from the Modus themes over to CSS variables so that I can have colours that make sense in both light mode and dark mode. Here's a snippet that exports the colours from one of the themes:

(format ":root {\n%s\n}\n"
        (mapconcat
         (lambda (entry)
           (format "  --modus-%s: %s;"
                   (symbol-name (car entry))
                   (if (stringp (cadr entry))
                       (cadr entry)
                     (format "var(--modus-%s)" (symbol-name (cadr entry))))))
         modus-operandi-palette
         "\n"))

So now my style.css has:

/* Based on Modus Operandi by Protesilaous Stavrou */
:root {
   // ...
   --modus-bg-red-subtle: #ffcfbf;
   --modus-bg-green-subtle: #b3fabf;
   --modus-bg-yellow-subtle: #fff576;
   // ...
}
@media (prefers-color-scheme: dark) {
   /* Based on Modus Vivendi by Protesilaous Stavrou */
   :root {
      // ...
      --modus-bg-red-subtle: #620f2a;
      --modus-bg-green-subtle: #00422a;
      --modus-bg-yellow-subtle: #4a4000;
      // ...
   }
}
mark { background-color: var(--modus-bg-yellow-subtle) }
mark.green { background-color: var(--modus-bg-green-subtle) }
mark.red { background-color: var(--modus-bg-red-subtle) }

Interesting, interesting…

View org source for this post
-1:-- Changing Org Mode underlines to the HTML mark element (Post Sacha Chua)--L0--C0--November 17, 2024 09:44 PM

J.e.r.e.m.y B.r.y.a.n.t: Basics of Emacs manual documentation

I introduce the basics of Emacs manual documentation, how to open the manuals, reference the manuals, and print the manuals. (...)
-1:-- Basics of Emacs manual documentation (Post)--L0--C0--November 17, 2024 04:30 PM

Sacha Chua: Checking caption timing by skimming with Emacs Lisp or JS

Sometimes automatic subtitle timing tools like Aeneas can get confused by silences, extraneous sounds, filler words, mis-starts, and text that I've edited out of the raw captions for easier readability. It's good to quickly check each caption. I used to listen to captions at 1.5x speed, watching carefully as each caption displayed. This took a fair bit of time and focus, so… it usually didn't happen. Sampling the first second of each caption is faster and requires a little less attention.

Skimming with subed.el

Here's a function that I wrote to play the first second of each subtitle.

(defvar my-subed-skim-msecs 1000 "Number of milliseconds to play when skimming.")
(defun my-subed-skim-starts ()
  (interactive)
  (subed-mpv-unpause)
  (subed-disable-loop-over-current-subtitle)
  (catch 'done
    (while (not (eobp))
      (subed-mpv-jump-to-current-subtitle)
      (let ((ch
             (read-char "(q)uit? " nil (/ my-subed-skim-msecs 1000.0))))
        (when ch
          (throw 'done t)))
      (subed-forward-subtitle-time-start)
      (when (and subed-waveform-minor-mode
                 (not subed-waveform-show-all))
        (subed-waveform-refresh))
      (recenter)))
  (subed-mpv-pause))

Now I can read the lines as the subtitles play, and I can press any key to stop so that I can fix timestamps.

Skimming with Javascript

I also want to check the times on the Web in case there have been caching issues. Here's some Javascript to skim the first second of each cue in the first text track for a video, with some code to make it easy to process the first video in the visible area.

function getVisibleVideo() {
  const videos = document.querySelectorAll('video');
  for (const video of videos) {
    const rect = video.getBoundingClientRect();
    if (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    ) {
      return video;
    }
  }
  return null;
}

async function skimVideo(video=getVisibleVideo(), msecs=1000) {
  // Get the first text track (assumed to be captions/subtitles)
  const textTrack = video.textTracks[0];
  if (!textTrack) return;
  const remaining = [...textTrack.cues].filter((cue) => cue.endTime >= video.currentTime);
  video.play();
  // Play the first 1 second of each visible subtitle
  for (let i = 0; i < remaining.length && !video.paused; i++) {
    video.currentTime = remaining[i].startTime;
    await new Promise((resolve) => setTimeout(resolve, msecs));
  }
}

Then I can call it with skimVideo();. Actually, in our backstage area, it might be useful to add a Skim button so that I can skim things from my phone.

function handleSkimButton(event) {
   const vid = event.target.closest('.vid').querySelector('video');
   skimVideo(vid);
 }

document.querySelectorAll('video').forEach((vid) => {
   const div = document.createElement('div');
   const skim = document.createElement('button');
   skim.textContent = 'Skim';
   div.appendChild(skim);
   vid.parentNode.insertBefore(div, vid.nextSibling);
   skim.addEventListener('click', handleSkimButton);
});

Results

How much faster is it this way?

Some code to help figure out the speedup
(-let* ((files (directory-files "~/proj/emacsconf/2024/cache" t "--main\\.vtt"))
        ((count-subs sum-seconds)
         (-unzip (mapcar
                  (lambda (file)
                    (list
                     (length (subed-parse-file file))
                     (/ (compile-media-get-file-duration-ms
                         (concat (file-name-sans-extension file) ".webm")) 1000.0)))
                  files)))
        (total-seconds (-reduce #'+ sum-seconds))
        (total-subs (-reduce #'+ count-subs)))
  (format "%d files, %.1f hours, %d total captions, speed up of %.1f"
          (length files)
          (/ total-seconds 3600.0)
          total-subs
          (/ total-seconds total-subs)))

It looks like for EmacsConf talks where we typically format captions to be one long line each (< 60 characters), this can be a speed-up of about 4x compared to listening to the video at normal speed. More usefully, it's different enough to get my brain to do it instead of putting it off.

Most of the automatically-generated timestamps are fine. It's just a few that might need tweaking. It's nice to be able to skim them with fewer keystrokes.

View org source for this post
-1:-- Checking caption timing by skimming with Emacs Lisp or JS (Post Sacha Chua)--L0--C0--November 17, 2024 12:29 PM

Rodrigo Morales: My subed customizations for editing captions of Emacsconf 2024

I’m a caption volunteer for Emacsconf 2024, so far I have used subed, an Emacs package, for editing the captions of two presentations. In this article, I share my personal subed customizations which have been of great help for me when editing subtitles. I hope these customizations can help other subed users or caption volunteers to edit captions efficiently.

I wrote most of these configurations some months ago while I was working on a project where I used subed to correct the timestamps of subtitles of 765 *.mp3 audio files in Spanish (approximately 46 hours of audio).

(defun my-subed-mpv-jump-to-0% ()
  (interactive)
  (let ((cur-sub-start (subed-subtitle-msecs-start))
        (cur-sub-stop (subed-subtitle-msecs-stop)))
    (subed-mpv-jump cur-sub-start)))

(defun my-subed-mpv-jump-to-25% ()
  (interactive)
  (let ((cur-sub-start (subed-subtitle-msecs-start))
        (cur-sub-stop (subed-subtitle-msecs-stop)))
    (subed-mpv-jump
     (+ cur-sub-start
        (* (- cur-sub-stop cur-sub-start) 0.25)))))

(defun my-subed-mpv-jump-to-50% ()
  (interactive)
  (let ((cur-sub-start (subed-subtitle-msecs-start))
        (cur-sub-stop (subed-subtitle-msecs-stop)))
    (subed-mpv-jump
     (+ cur-sub-start
        (* (- cur-sub-stop cur-sub-start) 0.5)))))

(defun my-subed-mpv-jump-to-75% ()
  (interactive)
  (let ((cur-sub-start (subed-subtitle-msecs-start))
        (cur-sub-stop (subed-subtitle-msecs-stop)))
    (subed-mpv-jump
     (+ cur-sub-start
        (* (- cur-sub-stop cur-sub-start) 0.75)))))

(defun my-subed-mpv-jump-to-90% ()
  (interactive)
  (let ((cur-sub-start (subed-subtitle-msecs-start))
        (cur-sub-stop (subed-subtitle-msecs-stop)))
    (subed-mpv-jump
     (+ cur-sub-start
        (* (- cur-sub-stop cur-sub-start) 0.90)))))

(defmacro my-subed-define-functions-change-time-by-ms (milliseconds)
  "Define functions for increasing and decreasing start-time and
stop-time for a given number of milliseconds."
  `(progn
     (defun ,(intern (concat "my-subed-increase-start-time-by-" (number-to-string milliseconds) "ms")) ()
       (interactive)
       (subed-adjust-subtitle-time-start ,milliseconds)
       (when (subed-loop-over-current-subtitle-p)
         (subed--set-subtitle-loop)))
     (defun ,(intern (concat "my-subed-decrease-start-time-by-" (number-to-string milliseconds) "ms")) ()
       (interactive)
       (subed-adjust-subtitle-time-start ,(* -1 milliseconds))
       (when (subed-loop-over-current-subtitle-p)
         (subed--set-subtitle-loop)))
     (defun ,(intern (concat "my-subed-increase-stop-time-by-" (number-to-string milliseconds) "ms")) ()
       (interactive)
       (subed-adjust-subtitle-time-stop ,milliseconds)
       (when (subed-loop-over-current-subtitle-p)
         (subed--set-subtitle-loop)))
     (defun ,(intern (concat "my-subed-decrease-stop-time-by-" (number-to-string milliseconds) "ms")) ()
       (interactive)
       (subed-adjust-subtitle-time-stop ,(* -1 milliseconds))
       (when (subed-loop-over-current-subtitle-p)
         (subed--set-subtitle-loop)))))

(my-subed-define-functions-change-time-by-ms 500)
(my-subed-define-functions-change-time-by-ms 1000)

;; I sometimes set the playback speed to 0.5 when I can't clearly
;; identify what the speaker said. Playing it in low speed helps me to
;; focus on the part that I find difficult.
;;
;; I also set playback speed to 0.5 when the speaker speaks so fast
;; and I want to split a subtitle in a very specific part.
(defun my-subed-mpv-playback-speed-0.5 () (interactive) (subed-mpv-playback-speed 0.5))
(defun my-subed-mpv-playback-speed-1 () (interactive) (subed-mpv-playback-speed 1))
(defun my-subed-mpv-playback-speed-2 () (interactive) (subed-mpv-playback-speed 2))
(defun my-subed-mpv-playback-speed-3 () (interactive) (subed-mpv-playback-speed 3))
(defun my-subed-mpv-playback-speed-4 () (interactive) (subed-mpv-playback-speed 4))

(defun my-subed-mpv-seek-backward ()
  (interactive)
  (subed-mpv-seek -100))

(defun my-subed-mpv-seek-forward ()
  (interactive)
  (subed-mpv-seek 100))

(define-minor-mode
  my-subed-custom-keys-minor-mode
  "Custom keys for `subed-mode'."
  :keymap (let ((map (make-sparse-keymap)))
            ;; Default keybindings
            (define-key map (kbd "C-M-f") 'forward-sexp)
            (define-key map (kbd "C-M-b") 'backward-sexp)
            (define-key map (kbd "C-k") 'subed-kill-subtitle)
            ;; Jump to specific parts in the audio
            (define-key map (kbd "M-H") 'my-subed-mpv-jump-to-0%)
            (define-key map (kbd "M-J") 'my-subed-mpv-jump-to-25%)
            (define-key map (kbd "M-K") 'my-subed-mpv-jump-to-50%)
            (define-key map (kbd "M-L") 'my-subed-mpv-jump-to-75%)
            (define-key map (kbd "M-:") 'my-subed-mpv-jump-to-90%)
            ;; Increase and decrease start time and stop time by 500ms
            (define-key map (kbd "M-s-[") 'my-subed-decrease-start-time-by-1000ms)
            (define-key map (kbd "M-s-]") 'my-subed-increase-start-time-by-1000ms)
            (define-key map (kbd "M-s-{") 'my-subed-decrease-stop-time-by-1000ms)
            (define-key map (kbd "M-s-}") 'my-subed-increase-stop-time-by-1000ms)
            ;; Increase and decrease start time and stop time by 1000ms
            (define-key map (kbd "s-[") 'my-subed-decrease-start-time-by-500ms)
            (define-key map (kbd "s-]") 'my-subed-increase-start-time-by-500ms)
            (define-key map (kbd "s-{") 'my-subed-decrease-stop-time-by-500ms)
            (define-key map (kbd "s-}") 'my-subed-increase-stop-time-by-500ms)
            ;; Change playback speed
            (define-key map (kbd "M-0") 'my-subed-mpv-playback-speed-0.5)
            (define-key map (kbd "M-1") 'my-subed-mpv-playback-speed-1)
            (define-key map (kbd "M-2") 'my-subed-mpv-playback-speed-2)
            (define-key map (kbd "M-3") 'my-subed-mpv-playback-speed-3)
            (define-key map (kbd "M-4") 'my-subed-mpv-playback-speed-4)
            ;; Seek
            (define-key map (kbd "M-;") 'my-subed-mpv-seek-backward)
            (define-key map (kbd "M-'") 'my-subed-mpv-seek-forward)
            map))

(defun my-subed-set-fill-column-to-60 ()
  ;; We set it to 60 because 2024-11-01T15:28:46+0000, https://emacsconf.org/captioning/ states:
  ;;
  ;; #+BEGIN_QUOTE
  ;; First, let's start with reflowing. We like to have one line of
  ;; captions about 60 characters long so that they'll display nicely
  ;; in the stream.
  ;; #+END_QUOTE
  (set-fill-column 60))

(defun my-subed-set-sentence-end ()
  ;; We set sentence-end to period and comma so that we can jump to the
  ;; end of a sentence by using backward-sentence (by default bound to
  ;; M-a in global-map in GNU Emacs 29.4) and forward-sentence (by
  ;; default bound to M-e in global-map in GNU Emacs 29.4)
  ;;
  ;; We use setq-local to set sentence-end because the value of
  ;; sentence-end is used by other modes and we don't want to disrupt
  ;; them. For example, M-q calls fill-paragraph. fill-paragraph ->
  ;; fill-region -> fill-region-as-paragraph -> fill-delete-newlines
  ;; -> canonically-space-region -> sentence-end (the arrow "->"
  ;; denotes "calls"). The function sentence-end returns the value of
  ;; the variable sentence-end when set.
  (setq-local sentence-end "\\(\\.\\|,\\|\\?\\)"))

(setq subed-mode-hook
      '(display-fill-column-indicator-mode
        subed-waveform-minor-mode
        subed-enable-replay-adjusted-subtitle
        subed-enable-loop-over-current-subtitle
        subed-enable-sync-player-to-point
        my-subed-set-fill-column-to-60
        my-subed-set-sentence-end
        my-subed-custom-keys-minor-mode))

;; 2024-11-05T23:43:19-0500: These variables have not yet merged to
;; development branch. See pull request:
;; https://github.com/sachac/subed/pull/78
(setq subed-waveform-image-width 800)
(setq subed-waveform-image-height 100)
-1:-- My subed customizations for editing captions of Emacsconf 2024 (Post Rodrigo Morales)--L0--C0--November 17, 2024 02:53 AM

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