Enhance crux-rename-file-and-buffer to accept new-name as an argument
Hi there!
I really like the crux-rename-file-and-buffer function and have been using it interactively via M-x. Today, I thought about calling it from Elisp and noticed that I can't pass in a new-name argument.
Would it be possible to change the function so I can provide a filename directly when calling it from Elisp? That would make it even more useful!
Thanks for considering this!
Hey everyone!
I wanted to share my use case for the crux-rename-file-and-buffer function. I've patched it to accept a new-name argument, which has been super helpful for me when renaming Ruby files based on the class/module inside.
Here's the function I came up with, my/projectile-rails-rename-file-to-class. It automatically generates snake_case filenames:
(defun my/projectile-rails-rename-file-to-class ()
"Rename the buffer and file based on the class/module name, converting CamelCase to snake_case."
(interactive)
(require 'string-inflection)
(require 'crux)
(let* ((class-name (save-excursion
(goto-char (point-min))
(if (re-search-forward "class \\([A-Za-z0-9_]+\\)" nil t)
(match-string 1)
(if (re-search-forward "module \\([A-Za-z0-9_]+\\)" nil t)
(match-string 1)
nil))))
(snake-case (string-inflection-underscore-function class-name))
(new-name (format "%s.rb" snake-case))
(new-path (expand-file-name new-name (file-name-directory buffer-file-name))))
(cond
((null class-name)
(message "No class or module found"))
((string= (buffer-name) new-name)
(message "Buffer is already named: %s" new-name))
((file-exists-p new-path)
(error "A file named '%s' already exists." new-path))
(t
(message "New filename should be: %s" new-path)
(crux-rename-file-and-buffer new-path)))))
I'd love to hear your thoughts or any feedback! Thanks!
Here’s the patched version of crux-rename-file-and-buffer that lets you provide a new-name argument directly:
(defun crux-rename-file-and-buffer (&optional new-name)
"Rename current buffer and if the buffer is visiting a file, rename it too.
If NEW-NAME is provided, use it as the new filename. Otherwise, prompt for it."
(interactive (list (read-file-name "New name: " (file-name-directory (buffer-file-name)) nil 'confirm)))
(let ((filename (buffer-file-name)))
(when filename
(let ((containing-dir (file-name-directory new-name)))
;; Make sure the current buffer is saved and backed by some file
(when (or (buffer-modified-p) (not (file-exists-p filename)))
(if (y-or-n-p "Can't move file before saving it. Would you like to save it now?")
(save-buffer)))
(if (get-file-buffer new-name)
(message "There already exists a buffer named %s" new-name)
(progn
(make-directory containing-dir t)
(cond
((vc-backend filename)
;; vc-rename-file seems not able to cope with remote filenames?
(let ((vc-filename (if (tramp-tramp-file-p filename) (tramp-file-local-name filename) filename))
(vc-new-name (if (tramp-tramp-file-p new-name) (tramp-file-local-name filename) new-name)))
(vc-rename-file vc-filename vc-new-name)))
(t
(rename-file filename new-name t)
(set-visited-file-name new-name t t)))))))))
I think this makes it a lot more flexible! Should I go ahead and submit a PR for this?
Let me know what you think!