Blog

For the English part of the blog, see Content AND Presentation.

2019-02-17 Inserting the current file name at point

It is not uncommon to issue some command to Emacs and find oneself in the minibuffer with the need to insert the name of the current file. A typical example would be firing M-! (shell-command) and realizing that we need to gice the name of the currently visited file as the argument to some command.

One way (admittedly, a roundabout one) to accomplish this is to fire Dired (if you use Dired-X, and I don’t see whay you shouldn’t, pressing C-x C-j (dired-jump) when visiting a file is a good way) and then press w (dired-copy-filename-as-kill). It comes in three variants: plain w, which just puts the filename at point into the kill ring, then C-u w, which does the same, but uses the relative path from the “main” directory of the Dired buffer (this makes a difference if you have a Dired buffer with subdirectories, which is one of the killer features of Dired as a file manager!). Lastly, you can say C-u 0 w (or M-0 w, or C-0 w), which requires a bit of finger acrobatics, but gives you the full path (which is sometimes exactly what you want).

That way, however, is very inconvenient. Imagine pressing M-!, typing half of the command line, and only then remembering that you need the filename. Then, you will probably quit (possibly killing the stuff typed so far), launch dired, use w, get back to M-! again, yank the filename and yank the rest of the command. Quite a mouthful. (Notice that you can go to another buffer without quitting the minibuffer, but this requires recursive minibuffers, and involves only a bit less keystrokes.)

So, imagine you could just press some key combo while in minibuffer (or anywhere else, for that matter) to get the name of the currently visited file inserted at point.

The main difficulty in coding this was how to get the name of the previous buffer when I’m in the minibuffer (which is an important use-case). With the help of the Emacs mailing list, I learned about minibuffer-selected-window, and getting a buffer when you have a window is easy.

(defun insert-current-file-name-at-point (&optional full-path)
  "Insert the current filename at point.
With prefix argument, use full path."
  (interactive "P")
  (let* ((buffer
	  (if (minibufferp)
	      (window-buffer
	       (minibuffer-selected-window))
	    (current-buffer)))
	 (filename (buffer-file-name buffer)))
    (if filename
	(insert (if full-path filename (file-name-nondirectory filename)))
      (error (format "Buffer %s is not visiting a file" (buffer-name buffer))))))

(global-set-key (kbd "M-o") #'insert-current-file-name-at-point)

Notice how I bind this to M-o, which is pretty useless in stock Emacs anyway. (Of course, feel free to bind to to whatever you like. Just be aware that you can use it with M-x, but if you want to use it in the minibuffer, you’ll need to set enable-recursive-minibuffers, which see, to a non-nil value.)

(Note: while writing this post, I noticed that user Alexander Klimov put an almost identical function in his message to the Emacs devel list. To make the situation even more embarassing, I actively participated in that thread, remembered the idea which inspired this post, and promptly forgot his code. Meh. I still decided to post it, if only because it is a useful function and I presume not everyone following my blog reads emacs-devel. Also, my code behaves slightly better in an edge case of a buffer which is not visiting any file.)

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2019-02-10 Making recover-this-file messages more prominent

When I start my Emacs, I visit all my agenda files automatically (of course, I set up the org-agenda-files variable first).

This has an annoying side effect. When I visit a file interactively, and Emacs detects that an autosave file exists for the file I’m visiting, it suggests using M-x recover-this-file. When I visit files during my init.el execution, these messages get lost in the *Messages* buffer, and it is easy to forget to check the autosave data. (This normally should not happen, since Emacs asks about saving all unsaved files before exiting. Sometimes, though, my Emacs gets killed for some reasons, and then it’s a problem.)

Of course, I could call recover-file for all the offending files automatically. Too much automation without informing the user is not necessarily the best option, however. I decided that I want to make the warnings much more prominent, and the simple method I used may be general enough to be potentially useful for other people as well. Here’s the code I have at the end of my init.el.

(defun show-initial-important-messages (regex)
  "Show all lines in *Messages* matching REGEX."
  (let* ((messages (with-current-buffer "*Messages*"
		     (buffer-string)))
	 (important
	  (with-temp-buffer
	    (insert messages)
	    (delete-non-matching-lines regex (point-min) (point-max))
	    (buffer-string))))
    (when (> (length important) 0)
      (with-output-to-temp-buffer " *Important messages from the init file*"
	(princ important)))))
(show-initial-important-messages "recover-this-file")

I guess that the code is pretty much self-explanatory; if not, check out the docstrings of all the relevant functions.

Now I can see all the messages about unsaved buffers when I start Emacs. (I can easily add other messages, too – time will tell if this is going to be useful.)

CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode

Comments on this page

2019-02-04 A simple template mechanism in Elisp

A long time ago I asked on the Emacs mailing list about a templating mechanism for Emacs Lisp. Of course, there is format. However, I don’t like it as a template engine, since the entries are identified by their order and not names. Then, there is YASnippet and skeleton.el, but they are (probably) better suited for interactive use. (At least Yasnippet can be used programmatically, but it seemed to be too complicated for my needs anyway back then.) Some people suggested other solutions, none of which really appealed to me.

So, I set out to write my own. Notice how we compute pos for the next iteration in string-expand-template (this simple trick is due to Pascal J. Bourguignon) – we can’t just use match-end, since this doesn’t take into account the possible difference in lengths of the replaced string and the replacement.

(defun string-expand-template (template values)
  "Expand TEMPLATE, replacing occurrences of \"{{{beep}}}\" using
the alist VALUES.  The text to use as the replacement is the cdr
of the cons with car equal to the string \"beep\".  If no such
string is present in VALUES, use the empty string as the
replacement.  To insert literal \"{{{\" in the template, escape
the first one with a backslash."
  (let ((pos 0) (result template) prev-result)
    (while (string-match
	    "\\(?:^\\|[^\\]\\)\\({{{\\([a-z0-9-]+\\)}}}\\)"
	    result pos)
      (setq prev-result result)
      (setq result (replace-match
		    (or (cdr (assoc (match-string 2 result) values)) "")
		    t t result 1))
      (setq pos (+ (match-beginning 2)
		   (- (length result) (length prev-result)))))
    result))

(defun perform-template-expansion-here (values)
  "Perform template expansion, using the VALUES alist, from the point
to the end of (visible part of) the buffer.  This is used by both
`expand-template' and `insert-and-expand-template' functions."
  (while (re-search-forward "\\(?:^\\|[^\\]\\)\\({{{\\([a-z0-9-]+\\)\\(?:,\\(.*?\\)\\)?}}}\\)" nil t)
    (replace-match (or (cdr (assoc (match-string 2)
				   values))
		       (match-string 3)
		       "")
		   t t nil 1)))

(defun expand-template (template values)
  "Expand TEMPLATE, replacing occurrences of \"{{{beep}}}\" using
the alist VALUES.  The text to use as the replacement is the cdr
of the cons with car equal to the string \"beep\".  If no such
string is present in VALUES, use the empty string as the
replacement.  To insert literal \"{{{\" in the template, escape
the first one with a backslash."
  (with-temp-buffer
    (insert template)
    (goto-char 0)
    (perform-template-expansion-here values)
    (buffer-string)))

(defun insert-and-expand-template (template values)
  "Expand TEMPLATE, replacing occurrences of \"{{{beep}}}\" using
the alist VALUES.  The text to use as the replacement is the cdr
of the cons with car equal to the string \"beep\".  If no such
string is present in VALUES, use the empty string as the
replacement.  To insert literal \"{{{\" in the template, escape
the first one with a backslash."
  (save-restriction
    (narrow-to-region (point) (point))
    (insert template)
    (goto-char (point-min))
    (perform-template-expansion-here values)
    (goto-char (point-max))))

It turned out to be easier than I suspected – not completely without pitfalls, though. I decided to base it on regexen (“A man had a problem, and he decided to use reular expressions…;-)”), which is a bit restrictive, but not too much for my needs.

So, let me start with a short howto. The main entry point is the function expand-template. It receives two arguments: the template itself and an alist specifying the substitutions. In the template (which is an ordinary string), anything consisting of letters, digits and/or hyphens between triple braces is the key searched for in the alist. If none is found, an empty string is inserted. As a bonus, you can write a default value after a comma; it may contain anything except a newline or three closing braces. Finally, a backslash before the opening triple braces escapes the whole thing. And that’s it.

Let us look at a few examples.

(expand-template
 "This is {{{beep}}} and that is {{{cling}}}."
 '(("beep" . "an apple")
   ("cling" . "an orange")))
(expand-template
 "{{{beep}}} is tasty, and I like how the glasses go \\{{{cling}}}."
 '(("beep" . "Wine")
   ("cling" . "an orange")))
(expand-template
 "This is a {{{beep}}} and that is {{{nothing}}}."
 '(("beep" . "beep")
   ("cling" . "cling")))
(expand-template
 "This is a {{{beep}}} and that is a {{{cling,CLING}}}."
 '(("beep" . "beep")))

Since very often the templates are expanded only to be then inserted into a buffer, and expand-template uses a (with-temp-buffer ... (buffer-string)) approach, using it to generate a string only to be inserted somewhere is a waste, so there is also the insert-and-expand-template. It takes exactly the same arguments, but instead of returning an (expanded) string, it just expands the template in place.

I mentioned some pitfalls. I wanted to implement expand-template using replace-regexp-in-string at first. It’s rep argument (specifying the replacement text) can be a function, which seemed to suit my needs. However, this function receives only the text that matched, and while you can refer to the match data in it, they are not very useful: match-beginning and match-end, for some strange reason, refer not to the searched string, but certain its substring! (Admittedly, this is documented, but it’s not something I like.) Finally, I decided that doing the replacements in a buffer (as opposed to a string) will be both easier to code (and the code will be more readable) and (probably) more effective.

It turned out that things were a bit more complicated – but this will wait for another post.

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

More...