1

I am trying to create a function that switches to the most recent buffer in major mode major-mode. An answer to a related question supplies a function that should require the smallest of tweaks to serve my purpose, but I can't make it work. Here is the original function in that answer:

(defun switch-to-most-recent-org-buffer ()
  (interactive)
  (let (found)
    (catch 'done
      (mapc (lambda (x)
              (when (with-current-buffer x (eq major-mode 'org-mode))
                (switch-to-buffer x)
                (setq found t)
                (throw 'done nil)))
            (buffer-list))
      (unless found (message "not found")))))

I thought I could just replace the first and sixth lines, respectively, with

(defun switch-to-most-recent-buffer-in-mode (major-mode)

and

              (when (with-current-buffer x (eq major-mode major-mode))

Which gives me this:

(defun switch-to-most-recent-buffer-in-mode (major-mode)
  (interactive)
  (let (found)
    (catch 'done
      (mapc (lambda (x)
              (when (with-current-buffer x (eq major-mode major-mode))
                (switch-to-buffer x)
                (setq found t)
                (throw 'done nil)))
            (buffer-list))
      (unless found (message "not found")))))

but it doesn't work: while the original function switches to my most recent org buffer, evaluating (switch-to-most-recent-buffer-in-mode org-mode) returns a "not found" message. It seems I am failing to grasp something very basic about Elisp, so please excuse my ignorance: I'm not a programmer and am new to Emacs.

--

[edited to add:] The amended code below works (taken from phils's answer).

(defun switch-to-most-recent-buffer-in-mode (mode)
  (interactive "C")
  (let (found)
    (catch 'done
      (mapc (lambda (x)
              (when (with-current-buffer x (eq major-mode mode))
                (switch-to-buffer x)
                (setq found t)
                (throw 'done nil)))
            (buffer-list))
      (unless found (message "not found")))))

1 Answer 1

1

Regarding your changes:

(defun switch-to-most-recent-buffer-in-mode (major-mode)
  (interactive)

The first problem is that you used the name of a standard variable for your argument, which means that the real major-mode is shadowed by whatever you pass to your function. You should rename that to something else (mode would be safe).

The second problem is with the interactive form, which tells Emacs how to obtain the function arguments interactively (e.g. if called with M-x or by a key binding). By adding a required argument, but not changing the interactive spec, this command now won't work interactively.

You could make that (interactive "C") to read a command symbol (which will include all modes).

Your next change is:

(when (with-current-buffer x (eq major-mode major-mode))

That (eq major-mode major-mode) will hopefully look pretty dodgy to you upon review. It is guaranteed to be true.

You have run headlong into the first problem here -- you are trying to compare the standard variable major-mode with the argument you added of the exact same name, which obviously can't work.

Take note that even if the function wasn't directly using the standard variable, name clashes are often unsafe (whether and how depends on the specifics, though). It's best to stick to un-hyphenated single word names for function arguments, as the vast majority of global variables are hyphenated.

Finally, you're not calling your function correctly.

(switch-to-most-recent-buffer-in-mode org-mode)

You are trying to evaluate the variable org-mode and pass its value to the function.

The value of major-mode (the standard variable, with which you will later be comparing the argument you've passed) is a symbol, so you need to quote the argument to prevent it from being evaluated, so that you are passing the symbol org-mode:

(switch-to-most-recent-buffer-in-mode 'org-mode)

returns a "not found" message.

I would actually have expected either an error (because org-mode isn't normally a valid variable to evaluate in your call), or for it to unconditionally select the first buffer (because (eq major-mode major-mode) is always true), so I'm not sure whether you were testing what you've described; but hopefully these pointers are sufficient in any case.

7
  • Thanks for the detailed answer! I amended the code to reflect your suggests, but I'm still getting a "not found" message. I've updated my question with the code I used.
    – Pablo
    Apr 23, 2021 at 0:44
  • Your new problem is (eq major-mode 'mode) which is another quoting problem -- this time you're quoting something which shouldn't be quoted. Here mode is a variable and you want it to be evaluated so that you are comparing the value of that variable with the value of the major-mode variable. I.e. If the value of major-mode is org-mode and the value of mode is also org-mode then your current code is false because the two symbols org-mode and mode are not the same thing.
    – phils
    Apr 23, 2021 at 0:53
  • Symbols and Quoting can be difficult ideas to get to grips with, and a full understanding requires you to know the difference between the read and eval phases of lisp execution, which is a bit of a deep-dive if you're new to the language. I recommend doing some research about these topics, though -- it might not all make sense, but even a basic understanding will help a lot.
    – phils
    Apr 23, 2021 at 1:00
  • Thanks again, and sorry for misreading your original answer, which was clear enough. Unfortunately it still returns "not found".
    – Pablo
    Apr 23, 2021 at 1:00
  • Mmh, I take back what I said. It does work. (I had accidentally closed the buffer, which was the only buffer in the particular major mode I tried the command with.) Thanks!
    – Pablo
    Apr 23, 2021 at 1:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.