Categories: geek » emacs

RSS - Atom - Subscribe via email

2024-01-22 Emacs news

| emacs, emacs-news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, lobste.rs, kbin, programming.dev, 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

Yay Emacs: Using elisp: links in Org Mode to note the time and display messages on stream

| yay-emacs, org

I like adding chapters to my videos so that people can jump to sections. I can figure out the sections by reading the transcript, adding NOTE comments, and extracting the times for those with my-youtube-copy-chapters. It could be nice to capture the times on the fly. org-timer could let me insert relative timestamps, but I think it might need some tweaking to synchronize that with when the stream starts according to YouTube. I've set up a capture, too, so I can take notes with timestamps.

It turns out that I don't have a lot of mental bandwidth when I'm on stream, so it's hard to remember keyboard shortcuts. (Maybe if I practise using the hydra I set up…) Fortunately, Org Mode's elisp: link type makes it easy to set up executable shortcuts. For example, I can add links like [[elisp:my-stream-message-link][TODO]] to my livestream plans like this:

2024-01-20-elisp-links.svg
Figure 1: Shortcuts with elisp: links

I can then click on the links or use C-c C-o (org-open-link-at-point) to run the function. When I follow the TODO link in the first item, Emacs displays a clock and a message based on the rest of the line after the link.

2024-01-20-message.svg
Figure 2: Displaying a clock and a message

In the background, the code also sets the description of the link to the wall-clock time.

2024-01-20-time.svg
Figure 3: Link description updated with the time

If I start the livestream with a clock displayed on screen, I can use that to translate wall-clock times to relative time offsets. I'll probably figure out some Elisp to translate the times automatically at some point, maybe based on something like org-timer-change-times-in-region.

I figured it might be fun to add a QR code automatically if we detect a URL, taking advantage of that qrencode package I started playing around with.

2024-01-20-qr.svg
Figure 4: With a QR code

You can also use elisp: links for more complicated Emacs Lisp functions, like this: elisp:(progn ... ...).

Here's the code that makes it happen. It's based on emacsconf-stream.el.

(defvar my-stream-message-buffer "*Yay Emacs*")
(defvar my-stream-message-timer nil)

(defun my-stream-message-link ()
  (interactive)
  (save-excursion
    (when (and (derived-mode-p 'org-mode)
               (eq (org-element-type (org-element-context)) 'link))
      (my-stream-update-todo-description-with-time)
      (goto-char (org-element-end (org-element-context)))
      (my-stream-message (org-export-string-as (buffer-substring (point) (line-end-position)) 'ascii t)))))
(defun my-stream-update-todo-description-with-time ()
  (when (and (derived-mode-p 'org-mode)
             (eq (org-element-type (org-element-context)) 'link))
    (my-org-update-link-description (format-time-string "%-I:%M:%S %p"))))

(defun my-stream-message (&optional message)
  (interactive "MMessage: ")
  ;; update the description of the link at point to be the current time, if any
  (switch-to-buffer (get-buffer-create my-stream-message-buffer))
  (erase-buffer)
  (delete-other-windows)
  (when (string= message "") (setq message nil))
  (face-remap-add-relative 'default :height 200)
  (insert
   "Yay Emacs! - Sacha Chua (sacha@sachachua.com)\n"
   (propertize
    "date"
    'stream-time (lambda () (format-time-string "%Y-%m-%d %H:%M:%S %Z (%z)")))
   "\n\n"
   message)
  ;; has a URL? Let's QR encode it!
  (when-let ((url (save-excursion
                    (when (re-search-backward ffap-url-regexp nil t)
                      (thing-at-point-url-at-point)))))
    (insert (propertize (qrencode url) 'face '(:height 50)) "\n"))
  (insert  "\nYayEmacs.com\n")
  (when (timerp my-stream-message-timer) (cancel-timer my-stream-message-timer))
  (my-stream-update-time)
  (setq my-stream-message-timer (run-at-time t 1 #'my-stream-update-time))
  (goto-char (point-min)))

(defun my-stream-update-time ()
  "Update the displayed time."
  (if (get-buffer my-stream-message-buffer)
      (when (get-buffer-window my-stream-message-buffer)
        (with-current-buffer my-stream-message-buffer
          (save-excursion
            (goto-char (point-min))
            (let (match)
              (while (setq match (text-property-search-forward 'stream-time))
                (goto-char (prop-match-beginning match))
                (add-text-properties
                 (prop-match-beginning match)
                 (prop-match-end match)
                 (list 'display
                       (funcall (get-text-property
                                 (prop-match-beginning match)
                                 'stream-time))))
                (goto-char (prop-match-end match)))))))
    (when (timerp my-stream-message-timer)
      (cancel-timer my-stream-message-timer))))

Let's see if that makes it easy enough for me to remember to actually do it!

View org source for this post

Running the current Org Mode Babel Javascript block from Emacs using Spookfox

| emacs, org, spookfox

I often want to send Javascript from Emacs to the web browser. It's handy for testing code snippets or working with data on pages that require Javascript or authentication. I could start Google Chrome or Mozilla Firefox with their remote debugging protocols, copy the websocket URLs, and talk to the browser through something like Puppeteer, but it's so much easier to use the Spookfox extension for Mozilla to execute code in the active tab. spookfox-js-injection-eval-in-active-tab lets you evaluate Javascript and get the results back in Emacs Lisp.

I wanted to be able to execute code even more easily. This code lets me add a :spookfox t parameter to Org Babel Javascript blocks so that I can run the block in my Firefox active tab. For example, if I have (spookfox-init) set up, Spookfox connected, and https://planet.emacslife.com in my active tab, I can use it with the following code:

#+begin_src js :eval never-export :spookfox t :exports results
[...document.querySelectorAll('.post > h2')].slice(0,5).map((o) => '- ' + o.textContent.trim().replace(/[ \n]+/g, ' ') + '\n').join('')
#+end_src
  • Mario Jason Braganza: Updated to Emacs 29.2
  • Irreal: Zamansky: Learning Elisp #16
  • Tim Heaney: Lisp syntax
  • Erik L. Arneson: Many Posts of Interest for January 2024
  • William Denton: Basic citations in Org (Part 4)

Evaluating a Javascript block with :spookfox t

To do this, we wrap some advice around the org-babel-execute:js function that's called by org-babel-execute-src-block.

(defun my-org-babel-execute:js-spookfox (old-fn body params)
  "Maybe execute Spookfox."
  (if (assq :spookfox params)
      (spookfox-js-injection-eval-in-active-tab
       body t)
    (funcall old-fn body params)))
(with-eval-after-load 'ob-js
  (advice-add 'org-babel-execute:js :around #'my-org-babel-execute:js-spookfox))

I can also run the block in Spookfox without adding the parameter if I make an interactive function:

(defun my-spookfox-eval-org-block ()
  (interactive)
  (let ((block (org-element-context)))
    (when (and (eq (org-element-type block) 'src-block)
               (string= (org-element-property :language block) "js"))
      (spookfox-js-injection-eval-in-active-tab
       (nth 2 (org-src--contents-area block))
       t))))

I can add that as an Embark context action:

(with-eval-after-load 'embark-org
  (define-key embark-org-src-block-map "f" #'my-spookfox-eval-org-block))

In Javascript buffers, I want the ability to send the current line, region, or buffer too, just like nodejs-repl does.

(defun my-spookfox-send-region (start end)
  (interactive "r")
  (spookfox-js-injection-eval-in-active-tab (buffer-substring start end) t))

(defun my-spookfox-send-buffer ()
  (interactive)
  (my-spookfox-send-region (point-min) (point-max)))

(defun my-spookfox-send-line ()
  (interactive)
  (my-spookfox-send-region (line-beginning-position) (line-end-position)))

(defun my-spookfox-send-last-expression ()
  (interactive)
  (my-spookfox-send-region (save-excursion (nodejs-repl--beginning-of-expression)) (point)))

(defvar-keymap my-js-spookfox-minor-mode-map
  :doc "Send parts of the buffer to Spookfox."
  "C-x C-e" 'my-spookfox-send-last-expression
  "C-c C-j" 'my-spookfox-send-line
  "C-c C-r" 'my-spookfox-send-region
  "C-c C-c" 'my-spookfox-send-buffer)

(define-minor-mode my-js-spookfox-minor-mode "Send code to Spookfox.")

I usually edit Javascript files with js2-mode, so I can use my-js-spookfox-minor-mode in addition to that.

I can turn the minor mode on automatically for :spookfox t source blocks. There's no org-babel-edit-prep:js yet, I think, so we need to define it instead of advising it.

(defun org-babel-edit-prep:js (info)
  (when (assq :spookfox (nth 2 info))
    (my-js-spookfox-minor-mode 1)))

Let's try it out by sending the last line repeatedly:

Sending the current line

I used to do this kind of interaction with Skewer, which also has some extra stuff for evaluating CSS and HTML. Skewer hasn't been updated in a while, but maybe I should also check that out again to see if I can get it working.

Anyway, now it's just a little bit easier to tinker with Javascript!

View org source for this post
This is part of my Emacs configuration.

Emacs tweaks: Choosing what to hack on

Posted: - Modified: | emacs, life, productivity

[2024-01-22 Mon]: How do you combat being overwhelmed by choice? is somewhat relevant. I particularly like this comment which talks about delaying the decision to see if it still makes sense.

Is it the Emacs lifecycle that you tweak your config for few months and then you live off of fat of the land for >4 years? My Emacs config is a Org-tangle spaghetti that I touch only if I want to set some more sane config variable.

@xgqt@emacs.ch

This got me thinking about how tweaking my config fits in with other things I want to do–how I choose what to hack on and for how long.

Text from sketch

Choosing what to hack on - 2024-01-16-01

When it comes to computer work, I can usually choose what to do.

Idea -> Test

XKCD on automation: time to automate vs frequency x time saved

I focus more on what I'll enjoy (both the destination and the journey)

  1. How can I make this better?
  2. What's the smallest step I can take? What can I fit in 15-30 minutes?
  3. What's nearby?
    • Relevant functions or packages
    • Next steps, possibilities
  4. What kinds of notes can I leave for myself or others?
  5. like desire paths: where, what size
  6. or like a river wearing down rocks

Sometimes when I chat with other people about automation, this XKCD chart about Is It Worth the Time? comes up.

I realized that this isn't quite how I consider things. I'm lucky in that when it comes to computer things, I get to choose most of the things I spend my time on. My consulting clients have very long wishlists that I pick from based on interests and priority, and I play with Emacs for fun.

Drawing of a task sandwich with Emacs tweaking, the actual task, and blog posts and comments

Because I enjoy tinkering around with Emacs, I often build a little Emacs hacking into my tasks. 15 or 20 minutes of exploring an idea can make it even more fun to do the actual task it's supposed to help with because then I want to test it out. Then after the task is done, I get to write about it. It's like making a little task sandwich with really nice bread. This is also a little related to sharpening the saw, which is pretty fun in Emacs. (Vim people do it too!)

These little changes add up over time, making things even more enjoyable. It's a little like the way desire paths show where people actually walk between buildings and give a sense of how much they are used, or how rivers smooth down the edges of stones. The easier I make something, the more likely I am to do it, and the more I'll get to enjoy the results of my code. It's a little like the Igors described in this essay.

When I think about something I might tweak about my Emacs configuration, I usually consider the following:

1. How can I make this better?

I like looking for ways to reduce manual work or looking-up. I tend to have a hard time with tedious, repetitive tasks. I also keep an eye out for things I've been meaning to learn.

2. What's the smallest step I can take? What can I fit in 15-30 minutes?

Small steps make it easy to squeeze in things here and there. I know my brain's going to suggest half a dozen things along the way, so it helps to start as small as possible and capture most of the other things in my inbox for later. That way, I can get to experience the benefits right away without feeling lost.

Another advantage of picking really small tasks and using Org Mode to capture the rest of the ideas is that I can try to avoid the Ovsiankina effect.1 I spend most of my day taking care of our 7-year-old, so I squeeze in my focused-time tasks early in the morning before she wakes up. Sometimes I have little opportunities to work on things throughout the day, like when she wants to read a book or watch a video. She might do that for 15-30 minutes before wanting to connect again. If I pick the wrong-sized task or I don't dump enough rough notes into my inbox so that I can get the open loops out of my head and trust that I can pick things up again, the unfinished part pulls on my brain and makes it harder to enjoy time with her. Then I get tempted to let her binge-watch Minecraft or Rubik's cube videos2 so that I can finish a thought, which doesn't quite feel like good parenting.

Lastly, I don't usually understand enough about my needs to build something complex from the start. Trying things out helps me discover more about what's possible and what I want.

3. What's nearby?

Thanks to Emacs's amazing community, there are usually relevant functions or packages that I can borrow code from. I mostly have a sense of things from the blog posts and forum threads that cross my radar because of Emacs News, and I should probably get used to skimming the descriptions in the "New packages" list because that can help me find even more things.

When coming up with possible approaches, I also sometimes think about other related ideas I've had before. Filing those ideas into the appropriate subtrees in my Org files sometimes helps me come across them again. If I can take a small step that also gets me closer to one of those ideas, that's handy.

I also like to think about next steps and possibilities. For example, even if I spend an hour or two learning more about data visualization with Org Mode and plotting, that's something I can use for other things someday. This works pretty well with keeping things small, too, since small parts can be combined in surprisingly interesting ways.

Let me try to trace through a web of related features so I can give you a sense of how this all works in teeny tiny steps.

G defun defun my-include:...?from-regexp=...&to-regexp... my-include:...?from-regexp=...&to-regexp... defun->my-include:...?from-regexp=...&to-regexp... my details my details defun->my details defvar defvar defun->defvar emacsconf-el emacsconf-el defun->emacsconf-el context context defun->context my-include:...?name= my-include:...?name= my-include:...?from-regexp=...&to-regexp...->my-include:...?name= :summary :summary my details->:summary defun-open defun-open :summary->defun-open web links web links emacsconf-el->web links Embark Embark :comments both :comments both Embark->:comments both QR code QR code Embark->QR code :comments both->context web links->Embark

  • defun: I often wanted to write about a specific function, so I wrote some code to find the function definition and copy it into my export post hidden inside a details tag with the first line of the docstring as the summary. 2023-01-02
  • my-include:...?from-regexp=...&to-regexp...: Sometimes I wanted to write about longer pieces of code. I wanted to include code without repeating myself. The regular #+INCLUDE can handle line numbers or headings, but neither of them worked for the Elisp files I referred to since the line numbers kept changing as I edited the code above it and it wasn't an Org Mode file. I made my own custom link so I could specify a start and end regexp. 2023-01-08
  • my_details: I wanted to put the code in a details element so that it could be collapsible. I made an org-special-blocks template for it. special-blocks
  • :summary: For Org source blocks, I wanted to be able to do that kind of collapsible block by just adding a :summary attribute. 2023-01-27
  • defun-open: I wanted to sometimes be able to keep the function definition expanded. 2023-09-12
  • emacsconf-el: Since I was writing about a lot of EmacsConf functions in preparation for my presentation, I wanted a quick way to link to the files in the web-based repository. 2023-09-12
  • defvar: Made sense to include variable definitions too.
  • web links: The emacsconf-el links were so useful, I wanted to be able to use that type of link for other projects as well. 2024-01-07
  • Embark: I wanted to be able to copy the final URL from a custom link at point, so I used Embark. 2024-01
  • QR code: I started livestreaming again, so I wanted a quick way for viewers to get the URL of something without waiting for stream notes. 2024-01-10
  • :comments both: While scanning Reddit to find links for Emacs News, I learned about :comments both and how that includes references to the Babel file that tangled the code. 2024-01-07
  • context: Now that it was easy to link to the web version of an Emacs Lisp file, I thought it might be fun to be able to automatically include a context link by passing link=1. I also wanted to be able to navigate to the Org source code for a tangled function. 2024-01-11
  • my-include:...?name=...: I wanted to be able to refer to Org Babel source blocks by name.
I promise it's all connected. (Pepe Silva meme)

In the course of writing this blog post, I learned how to use URLs in Graphviz, learned how to include inline HTML for export with @@html:...@@, used position: sticky, figured out how to highlight the SVG using JS, used CSS to make a note that should only show up in RSS feeds, and submitted a pull request for meme.el that was merged. And now I want to figure out sidenotes or at least footnotes that don't assume they're the only footnotes on the page… This is just how my brain likes to do things. (Oooh, shiny!)

4. What kinds of notes can I leave for myself or others?

I might take years before revisiting the same topic, so good notes can pay off a lot. Also, when I share what I've been working on, sometimes people e-mail me or comment suggesting other things that are nearby, which is a lot of fun. The ideas I come up with are probably too weird to exactly line up with other people's interests, but who knows, maybe they're close enough to what other people work on that they can save people time or spark more ideas.

Inspired by Mats Lidell's EmacsConf 2023 talk on writing test cases, I've been working on writing occasional tests, too, especially when I'm writing a small, function to calculate or format something. That's a good way of sketching out how I want a function to behave so that I can see examples of it when I revisit the code. Tests also mean that if I change things, I don't have to worry too much about breaking important behaviours.

Ideas for next steps

How can I get even better at this?

  • Popping the stack (untangling interruptions and ideas): When I let myself get distracted by a cool sub-idea, I sometimes have a hard time backing up. I can get back into the habit of clocking time and practise using my org-capture template for interrupting task so that I can use C-u with C-c j (my binding for org-clock-goto) to jump to a recently-clocked task.
  • Braindumps can help me use non-computer time to flesh out notes for things I'm working on or ideas for next steps.
  • If I skim the descriptions of new packages in Emacs News (maybe even the READMEs instead of just the one-liners), I'll probably retain a brief sense of what's out there and what things are called.
  • Vector search across package descriptions and function docstrings could be an even more powerful way to discover things that are close to something I want to do.
  • Using elisp-demos to add more examples to functions can help me look up things I frequently use but don't remember.
  • Figuring out more modern IDE features like refactoring support, on-the-fly error checking, and code navigation could help me code faster.

So that's how I tinker with Emacs for fun: start with something that mostly works, keep an eye out for opportunities to make things better, use tinkering as a way to make doing things more fun, look for things that are nearby, and

Footnotes:

1

I used to think this was the Zeigarnik effect, but it turns out the Zeigarnik effect is about remembering incomplete tasks versus completed tasks, while the Ovsiankina effect is more about intrusive thoughts and wanting to get back to that incomplete task.

2

At the moment, she likes Eyecraftmc and J Perm.

View org source for this post

Org Mode custom link: copy to clipboard

| emacs, org

I have a tiny corporation for my consulting. I do all of my own paperwork. I have lots of notes in Org Mode for infrequent tasks like the tax-related paperwork I do once a year. My notes include checklists, links, and Org Babel blocks for calculations. I often need to copy standard text (ex: the name of the company) or parts of the output of my Org Babel blocks (ex: tax collected) so that I can fill in web forms on the Canada Revenue Agency website.

This little snippet makes it easy to copy text for pasting. It defines a custom Org link that starts with copy:. When I follow the link by clicking on it or using C-c C-o (org-open-at-point), it copies the text to the kill ring (which is what Emacs calls the clipboard) so that I can paste it anywhere. For example, [[copy:Hello world]] becomes a link to copy "Hello world". Copying means never having to worry about typos or accidentally selecting only part of the text.

(use-package org
  :config
  (org-link-set-parameters
   "copy"
   :follow (lambda (link) (kill-new link))
   :export (lambda (_ desc &rest _) desc)))

I can use these links as part of my checklist so that I can quickly fill in things like my business name and other details. I can put sensitive information like my social insurance number in a GPG-encrypted file. (Just set up your GPG keys and end a filename with .gpg, and Emacs will take care of transparently encrypting and decrypting the file.)

I can also export those links as part of my Org Babel output. For example, the following code calculates the numbers I need to fill in a T5 form for the other-than-eligible dividends that I issue myself according to the T5 instructions from the CRA.

(let* ((box-10 1234) ; fake number for demo
       (box-11 (* 1.15 box-10))
       (box-12 (* 0.090301 box-11)))
  `((box-10 ,(format "[[copy:%.2f][%.2f]]" box-10 box-10))
    (box-11 ,(format "[[copy:%.2f][%.2f]]" box-11 box-11))
    (box-12 ,(format "[[copy:%.2f][%.2f]]" box-12 box-12))))
box-10 1234.00
box-11 1419.10
box-12 128.15

On my computer, the numbers become links that I can click and copy. Another little shortcut thanks to Emacs and Org Mode!

View org source for this post
This is part of my Emacs configuration.

2024-01-15 Emacs news

| emacs, emacs-news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, lobste.rs, kbin, programming.dev, 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

Yay Emacs 2024-01-12: EmacsConf 2023 report, SVG animation, Embark, Org Mode links

| yay-emacs, emacs

For this livestream, I experimented with scheduling it for 8:00 AM EST instead of just starting it whenever I could squeeze in the time.1 People dropped by! And asked questions! And suggested interesting things! Wow. This could be fun.

I wrote a bunch of blog posts throughout the week and added lots of little videos to them. It was easy to walk through my recent posts and demonstrate things without worrying about (a) accidentally leaking personal information or (b) flubbing things on camera, since apparently my multitasking abilities are on the way down.2 It felt good to go through them and add some more commentary and highlights while knowing that all the details are there in case people want to do a deeper dive.

Here are the links:

I roughly edited the transcript from Deepgram and I uploaded it to YouTube, fixing some bugs in my Deepgram VTT conversion along the way. I think I like having proper transcripts even for ephemeral stuff like this, since it costs roughly USD 0.21 for the 43-minute video and I can probably figure out how to make editing even faster..

New projects are easier to keep working on when they have immediate personal benefits. It's easy for me to keep doing Emacs News every week because I have so much fun learning about the cool things people are doing with Emacs. I think it'll be easy for me to keep doing Yay Emacs livestreams because not only do I get to capture some workflows and ideas in videos, but other people might even tell me about interesting things that could save me time or open up new possibilities. Also, it's worth building up things I love.

I'm going to try scheduling another stream for next Sunday (Jan 21) at 7:30 AM EST. Maybe I can experiment with sharing my screen with the Surface Book or the W530 and then using that computer to stream. We'll see what that's like!

Footnotes:

1

Thanks to the unpredictability of life with a kiddo, scheduling things has been one of my life goals for a while! <laugh> When I created the event, the kiddo was still in her winter-break habit of sleeping in until 10 or 11, so I figured that I had a little time before I needed to call in for her virtual school at 8:45 AM. Of course, that week she decided to start setting her alarm for 7:59 AM so that she could wake up early and have watching time, and she actually started waking up around that time. So for Friday, I woke up earlier (well, the cat woke got me up even earlier) and packed a little breakfast she could have in the living room (since my computer's on a kitchen cabinet)… and that was the one day she snoozed her alarm clock and sleep in. I've scheduled the next stream for 7:30 AM… and she has announced that she wants to set her alarm for 7:30ish. Hmm.

2

I notice that it can be a little challenging for me to talk and do things at the same time. This is particularly obvious when I'm cubing (brain hiccup at the last step, gotta solve the whole Rubik's cube all over again). It's also why I prefer to record the audio for my presentations separately instead of winging it. =) It could be verbal interference, (very mild, totally expected) age-related cognitive decline (which is a topic I've been meaning to write up my notes on), or my squirrel brain could just have been pretty bad at this all along. Anyway, words or code, sometimes I just gotta pick one. Never mind my laptop's CPU not handling ffmpeg well, my brain's CPU gets high utilization too. That's good, though!

View org source for this post