Alright; for those who read dev.to regularly, it may already be obvious that my title is derived from a particular other post (not to mention it being listed as one of the top 7 posts of the last week).
But it reminded me of a problem I'd run into oh so often when using Emacs (probably for anyone who uses a text editor), one that I'd recently found a very satisfying solution to.
For those who hadn't read the article, you may be doing some maintenance on some files; you'll probably notice immediately since you'll find that the file is read-only but, even if you give this no second thought and make it so you can edit the file, you'll be greeted, when you go to save, with
File apg.conf is write-protected; try to save anyway? (yes or no)
And – if you think to yourself, "Yes! Of course I do!" and slam out a
yes – you'll be greeted with
Doing chmod: Operation not permitted
sudo but who wants to remember to do that every time?
Now, there's been a widespread, instant solution for, at least, as long as I've looked for a solution – it involves the incredible Tramp package and instantly invokes
sudo when trying to save a file Emacs doesn't have permissions for.
Recently, I found that, once saved, subsequent saves warn the user that the file has recently been edited (from your past save) and are you sure you want to save?
Needless to say, this can become irritating quite quickly.
Enter a Second Solution!
Thankfully, there's a really simple solution and it gets invoked the second you turn off read-only mode instead of waiting all the way until you save: one single action indicating a desire to take responsibility for this file when you indicate you want to.
First, define some advice for
read-only-mode (code gotten from this blog with slight tweaks from me):
(defadvice read-only-mode (after read-only-mode-sudo activate) "Find file as root if necessary." (when (and (buffer-file-name) (not (file-writable-p (buffer-file-name)))) (let ((p (point))) (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name)) (goto-char p))))
This simply tells
read-only-mode that, when activated, check that the buffer has a name and, if so, is the file not able to be written to.
If so, grab the point of the cursor, open a new file (really, the same file but with
sudo via Tramp), and then go the point we saved since, reopening the file, the cursor would be at the beginning of the file, otherwise.
However, I quickly found that I ran into a new problem – attempting to open a new file assumed that we should stay using
sudo: definitely not an assumption I wanted made, by default, for other files.
So I whipped up a function to replace the
find-file function (while using it to avoid departing from standard behavior too terribly):
(defun find-file-no-sudo () (interactive) (find-file (read-file-name "Find file: " (if (string-match "^/sudo:root@localhost:" default-directory) (substring default-directory 21) default-directory))))
Basically, – if there's the text necessary to use
sudo via Tramp at the beginning – strip it out!
Now, to make sure we use this where we used the old function…
(global-set-key (kbd "C-x C-f") 'find-file-no-sudo)
And that's it!
Throw all of the above in your init file and you're good to go.
Next time you find yourself with a file you wanted to edit but didn't open with
sudo, just do
C-x C-q and you'll prompted for your password; then edit to your heart's content.