8
\$\begingroup\$

As a disclaimer, this is the first function I have EVER written in Lisp or ELisp. Please, bash it as much as you would any other piece, but don't judge me for it please! :)

So, I realized I wanted a way to rename files without all the hassle of reopening the file etc. I googled and found a function at https://stackoverflow.com/questions/384284/can-i-rename-an-open-file-in-emacs (credit Steve Yegge).

However, it did not seem to work well with uniquify. Which was a problem. Having two files open and trying to rename the first one to the same name as the second one (for example CMakeLists.txt, which there might be many of) caused it to bail since it only checked the name of the buffer (which wasn't unique yet, as uniquify doesn't do it's thing before something needs uniquifying).

The only way I found to force a "re-uniquification" was to close the previous buffer and open the new file. This seems very inelegant...

(defun is-unique-name-for-buffer (new-name)
  (let ((file-name (buffer-file-name))
    (dir-name (file-name-directory buffer-file-name)))
  (let ((new-complete-name (concat dir-name new-name)))
  (progn (not (string-equal file-name new-complete-name))))))

(defun rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive "sNew name: ")
  (let ((name (buffer-name))
    (filename (buffer-file-name))
    (dir-name (file-name-directory (buffer-file-name))))
    (if (not filename)
    (message "Buffer '%s' is not visiting a file!" name)
      (if (not (is-unique-name-for-buffer new-name))
      (message "A buffer named '%s' already exists in that location!" new-name)
    (progn
      (rename-file (file-name-nondirectory filename) new-name 1)
      (kill-buffer)
      (set-buffer (find-file (concat dir-name new-name))))))))
\$\endgroup\$
  • \$\begingroup\$ Do not use concat to build absolute file names. Use expand-file-name: (expand-file-name dir-name new-name). That way, Emacs handles file names for different systems automatically. \$\endgroup\$ – Drew Oct 31 '13 at 4:53
  • \$\begingroup\$ Not sure what you intend here, but I'm guessing you really want find-file-noselect, not find-file. \$\endgroup\$ – Drew Oct 31 '13 at 4:54
8
\$\begingroup\$

Judgment circuits deactivated; you have nothing to fear.

I was going to suggest that there must already be a function that you can call to force uniquify a buffer, but M-x apropos RET ^uniq doesn't show any documented functions that would do that for you (take a look and try a few out if you like, you may find a more elegant solution).


Lisp (actually, Elisp and Common Lisp; Scheme does its own thing) convention is to end predicate functions with -p or p rather than begin them with is-.

(defun unique-name-for-buffer-p (new-name)
  (let ((file-name (buffer-file-name))
        (dir-name (file-name-directory buffer-file-name)))
    (let ((new-complete-name (concat dir-name new-name)))
      (progn (not (string-equal file-name new-complete-name))))))

Your predicate seems to be nesting lets only because you need new-complete-name to refer to dir-name. When you're in a situation like that, you can instead use let*. It's the same as let except that its clauses are guaranteed to be evaluated in order (it's still good style to use let when you can, but one let* is better than many nested lets).

(let* ((file-name (buffer-file-name))
       (dir-name (file-name-directory buffer-file-name))
       (new-complete-name (concat dir-name new-name)))
    (progn (not (string-equal file-name new-complete-name))))

You only need explicit progn when you want to evaluate multiple expressions as one (you use it correctly in your nested ifs, but read on).

(defun unique-name-for-buffer-p (new-name)
  (let* ((file-name (buffer-file-name))
         (dir-name (file-name-directory buffer-file-name))
         (new-complete-name (concat dir-name new-name)))
    (not (string-equal file-name new-complete-name))))

The nested ifs in your second function can be expressed as a single cond (which also gets rid of that otherwise legitimate progn).

(defun rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive "sNew name: ")
  (let ((name (buffer-name))
        (filename (buffer-file-name))
        (dir-name (file-name-directory (buffer-file-name))))
     (cond ((not filename) 
            (message "Buffer '%s' is not visiting a file!" name))
           ((not (unique-name-for-buffer-p new-name)) 
            (message "A buffer named '%s' already exists in that location!" new-name))
           (t (rename-file (file-name-nondirectory filename) new-name 1)
              (kill-buffer)
              (set-buffer (find-file (concat dir-name new-name)))))))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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