Content AND Presentation

2024-04-27 Emacs everywhere

A few years ago I mentioned that I use Textern so that I can easily edit input boxes and text areas in Emacs. While I still use it, it seems less and less great. More and more web apps do crazy JavaScript things with their input​s and textarea​s, and they sometimes interfere with Textern in unpredictable ways.

Recently, I found something similar but possibly better: Emacs everywhere. One of its advantages is that it works, well, everywhere in X.org, not only in Firefox. (I’m not sure how, but it supposedly also works on Macs. Maybe they use X.org, too? I don’t know.) At first it seemed to me that it doesn’t matter much – I hardly ever use anything but Emacs and Firefox. Then it dawned on me: Emacs everywhere works in the terminal, too! (You may be surprised that I don’t use Eshell or at least M-x term. Well, I don’t. Old habits die slowly, plus I am a tad paranoid and I’m slightly afraid of my Emacs (or more probably, some rogue package) doing something unpleasant like saving sessions or my keypresses to the disk. Since I often type various passwords in the terminal, I’d prefer this not to happen.

The way Emacs everywhere does its job is (at least conceptually) pretty simple. It spawns an Emacs frame, creates an empty buffer, inserts the primary selection into it if it exists, and lets me edit the buffer. When I’m done, I can press C-c C-c and Emacs everywhere pastes what I’ve written into the program that was running before I started it. (In fact, there is a bit more to that. You can also press C-c C-k to cancel, and DEL (that is, backspace) as the first keypress deletes everything.)

I made a few quick experiments with various nasty apps like Jira and Discord, and Emacs everywhere seems to work very well! I also wrote a short script to launch it, emacs-everywhere.sh:

#!/bin/bash
emacsclient --eval "(emacs-everywhere)"

and bound it to s-e (that is, Super-e) in my window manager of choice:

globalkeys = gears.table.join(
   -- Emacs everywhere
   awful.key({ modkey, }, "e", function() awful.util.spawn("emacs-everywhere.sh") end),
   -- Emacs everywhere ends here
   -- ...
)

Now, the only thing left is that I need to remember to press s-e to edit anything in Emacs – unlike Textern, Emacs everywhere does not (and cannot) put the “current” content of whatever I’m editing in the Emacs buffer it creates. (Technically, it could simulate pressing C-a C-x which in many applications means “mark everything and kill” (or “select all and paste”, as the youth of today calls it;-)), but that would break e.g. in a terminal.) In fact, even if I forget about it, I can for example select everything with C-a (in Firefox at least), and then the selected text will get copied to the Emacs everywhere buffer – and when it gets pasted again, the selection will still be active (unless I deactivated it manually), so what I wrote in Emacs will effectively replace what I had earlier. Very nice!

That’s it for today, see you next week!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-04-22 Next-Error-Follow mode

A few weeks ago I wrote about Emacs’ Follow mode. It turns out that searching for follow-mode on my blog reveals an old post about Ibuffer which is very short and unfortunately a bit misleading. It seems that the mention of Follow mode there was really meant to mean Next-Error-Follow mode. It is a completely unrelated, but also useful concept. When you open a buffer with references to various places (like an Occur or Diff buffer), you can say M-x next-error-follow-minor-mode, and moving around in that buffer will automatically jump to the relevant position in another window. It’s a great way to quickly see the results of a search, differences between files, compilation errors etc. in context.

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-04-15 Improving recenter-top-bottom and reposition-window

If one can be a fan of an Emacs command, then I am a huge fan of recenter-top-bottom (C-l) and reposition-window (C-M-l). I use them all the time to see the context of what I’m editing at the moment. However, they are not always that useful. They are rather crude – recenter-top-bottom only has three “settings”, as the name suggests, and reposition-window has only two (it either puts the first line of a function, or the first line of a comment preceding the function at the top). As I mentioned a few weeks ago, I sometimes work with rather long functions – and sometimes I am in the second of two shorter ones, but I want to see the first one, too. Also, I don’t only edit code – I edit prose, too, where paragraph play the role of functions, and Org files, where there are even other structural elements – headlines, tables and source blocks in addition to paragraphs, for example.

I decided to write a variation on the theme of reposition-window, which – instead of putting the first line of the function I’m in at the top, it tries to put the first line of a “section” I’m in at the top. What a “section” is can be a bit vague. For example, in the simplest case, a “section” is just a fragment of the buffer separated from the rest by one or more blank lines. This may mean a paragraph (in prose), or a logical part of a function in code (assuming that the programmer puts such blank lines in suitable places, which is a very good practice anyway – putting them between functions is a minimum, and putting a few of them in longer and more complicated functions is what we do at my company anyway). For a more complex scenario, consider an Org mode headline or block which also start a new section.

Of course, this is still pretty imprecise, and there are quite a few ways it could work, especially in corner cases. One possible implementation I came up with is the following code.

(defconst reposition-window-section-re "\\`\\|\n\\s-*\n\\|^\\*+[ ]\\|#\\+begin"
  "Regular expression matching a \"section\" beginning.
For `reposition-window-section' to work properly, this must match
the beginning of the buffer, too.")

(defun reposition-window-section ()
  "Make the current section visible.
Here, \"section\" means a fragment starting with something that
matches `reposition-window-section-re'.  If the current section
is already visible, scroll up so that one more section becomes
visible, and if that is not possible, scroll down so that only
the current section is.  If the current section is larger than
the screen, fall back to `recenter-top-bottom'."
  (interactive)
  (save-excursion
    (let ((orig (point))
          (at-top (pos-visible-in-window-p (point-min))))
      (unless at-top
        (goto-char (window-start))
        (while (progn (re-search-backward reposition-window-section-re nil t)
                      (or (invisible-p (point))
                          (pos-visible-in-window-p (match-end 0)))))
        (goto-char (match-end 0))
        (forward-line 0)
        (set-window-start (selected-window) (point)))
      ;; if going to one section up moves point out of the window, go to the section right before point
      (when (or (not (pos-visible-in-window-p orig))
                ;; do the same if we started at the top of the buffer
                at-top)
        (goto-char orig)
        (re-search-backward reposition-window-section-re nil t)
        (goto-char (match-end 0))
        (forward-line 0)
        (set-window-start (selected-window) (point))
        ;; if that also moves point out of the window, just (recenter-top-bottom)
        (unless (pos-visible-in-window-p orig)
          (goto-char orig)
          (recenter-top-bottom))))))

(global-set-key (kbd "C-S-l") #'reposition-window-section)

It was my third attempt (not counting a lot of minor changes). The first one was needlessly complicated, and depended on (eq last-command #'reposition-window-section) so that pressing C-S-l twice in a row worked differently from pressing it once, doing anything else (e.g. small-scale point motion like C-f) and pressing it again. I generally consider commands which rely on external state like that inferior design, and while sometimes it is exactly what is called for, I decided that I’d better avoid it. (Interestingly, recenter-top-bottom, which is sort of a grandfather of my command, does exactly that. That leads to a subtle issue when the point is already in the middle line. In that case, it does nothing, which is potentially confusing for the user.) The second one was simply buggy (and I really do hope this one is not).

I am still not 100% satisfied with this design, which seems not very elegant with all the unless and when clauses. I like to comfort myself that elegance in Elisp commands is sometimes very difficult to achieve. Text editing is a surprisingly complicated subject (as evidenced for example by an excellent series of articles about tree-sitter and Combobulate by Mickey Petersen), and making your editor behave in an intuitive and useful way means dealing with quite a few non-obvious edge cases. This is exactly the situation here. The main idea is simple – to have a regex matching a “section beginning”, and to move the text in the window so that more and more “sections” are fully visible. Here are the possible special cases I could imagine.

  1. The section beginning regex could match a fragment of one line or could span several lines.
  2. Showing “one more section” could move the point out of the window.
  3. The section the point is in could be larger than the screen.
  4. We could have started at the top of the buffer (that is, (point-min) could be already visible).
  5. There could be one no section beginnings before the point.
  6. The section above the screen could be folded.

The first problem deserves an explanation. As I mentioned, one possible notion of a “section beginning” is one or more blank lines. In that case, that beginning itself should not be visible – there is no point in showing the user empty lines, especially in a command whose main purpose is to make the best use of limited screen real estate. However, an equally valid notion of a “section beginning” (for Org mode files at least) is a line beginning with one or more stars or a #+begin_whatever block. In that case we definitely want it to be visible. My solution, covering both cases, is a bit hackish, but I like to think that it’s clever: the first line visible will be the line where the end of the “section beginning” falls. This covers both cases described above.

Also, this is the reason why it is not enough to go to (window-start) and search for reposition-window-section-re backwards. When looking for the “one or more blank lines” type of section beginning, and assuming that these blank lines fall exactly above what is visible on the screen, when we go to (match-end 0) we end up exactly where we started. This means that in this situation we need to search backwards twice. The most elegant way of expressing this in code is a repeat-until-type loop – which in Elisp is implemented as a while loop with the whole body packed into the while condition – and repeating the search until (match-end 0) is no longer visible on the screen.

The second problem is pretty easy to solve. After we move (window-start) to the beginning of the section we’ve found, we need to check if the starting point is still visible. If not, we need to go back to where we started and look for the nearest section beginning above. This way, our command will “cycle” – show more and more sections until it’s impossible without losing the point from view, and then it will show just the section the point is in and as much as possible below.

However, it is also possible that we couldn’t do even that (item 3). In that case, we just fall back to (recenter-top-bottom). It’s not ideal, but at least we do something, so that the user clearly sees that something is happening. (I wanted to avoid the situation where pressing C-S-l does nothing at all. It is still possible – calling recenter-top-bottom when exactly in the middle of the screen indeed appears to do nothing, as I mentioned before – but working around an extremely rare special case where just pressing C-S-l again solves the issue anyway didn’t seem worth it.)

If all that were not enough, it is also possible that we start at the top of the buffer (that is, with (point-min) already visible). If that is the case, we cannot show more above the point, so we should do the same thing as in item 2 – move the section the point is in to the top.

Similarly, it may happen that there is no section beginning before point - in other words, we are in the first section in the buffer and our regex does not really match a section beginning per se, but a section separator. I’m on the fence with respect to this one – there is a very simple way to avoid this altogether, and that is to include \` as one of the variants in reposition-window-section-re. This regex matches the beginning of the buffer, so including it ensures that it’s always treated as a beginning of a section. On the other hand, if a user forgets to include it when customizing reposition-window-section-re, reposition-window-section will work incorrectly in this situation. After a short consideration I decided that I’ll go for the simpler code and mention in the docstring that this must be included manually. The assumption here is that most users (if my code ever has any user besides me, that is;-)) won’t even customize that option, and if they do, they have the docstring, and if they don’t read it, reposition-window-section will still work in most cirumstances. (Of course, I could add \` to the regex in my function each time it is run, or – if I decided to get really fancy – I could also add some code ensuring \`\| is added to the variable value in the {{{:set}}} parameter of {{{defcustom}}}.)

Last but not least, the section(s) above the portion visible on the screen could be invisible (this happens all the time with Org mode structure folding). That is pretty easy to solve – we can just keep looking for sections until the one we’ve found is visible.

Ok, that was quite a trip. The end result is still not ideal. For instance, I can imagine that a prefix argument could cause this command to scroll down instead of up. This way more and more context below the point would become visible. It is not obvious to me whether it would be better to keep showing whole sections at the top or at the bottom of the screen then – I guess bottom would make more sense. To achieve this, I would revisit the idea of checking last-command – it would be very inconvenient to have to have to type C-u C-S-l C-u C-S-l instead of just C-u C-S-l C-S-l, for example. Another way to make using a prefix argument with a command which tends to be repeated many times in a row would be to bind it to some key sequence, say C-x C-S-l, and use the mechanism of transient keymaps. I have to admit that I never used that and I’m not sure if this would even work, that is, if subsequent invocations would receive the same prefix argument as the first one – I’m afraid not, but it’s something worth investigating. I’ll look into it some day, I think. Still, the command is pretty useful even without that feature, and I have to say that it was useful even before I fleshed it out, while it was still buggy!

The last thing I’d like to say in this already rather long post is that if you’d like to learn how to code various convenience commands like this, I always recommend two sources – Introduction to programming in Emacs Lisp by the late Robert J. Chassell first, and my book, Hacking your way around in Emacs, if you want to dive a bit deeper. In fact, I am very much tempted to add this exact command as a topic of another chapter in that book – it is neither too simplistic nor too complex, and it is potentially useful to everyone (it does not depend on whether you use Emacs for coding or for prose). But that is more like a long-term plan.

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

More...

CategoryEnglish, CategoryBlog