Preferring certain file extensions with Emacs file name completion - emacs

I have lots of directories filled with a bunch of TeX documents. So, there's lots of files with the same base filename and different extensions. Only one of them, though, is editable. I'd like a way to convince Emacs that if I'm in a directory where I've got
document.tex
document.log
document.pdf
document.bbl
document.aux
...
and I'm in the minibuffer and do
~/Documents/.../doc<TAB>
it fills in 'document.tex', because that's the only really properly editable document in that directory. Anybody know of a good way to do that?

I've written some code that should do what you want. The basic idea is to set the variable 'completion-ignored-extensions to match the extensions you want to skip, but only when there are .tex files present. This code does that.
(defadvice find-file-read-args (around find-file-read-args-limit-choices activate)
"set some stuff up for controlling extensions when tab completing"
(let ((completion-ignored-extensions completion-ignored-extensions)
(find-file-limit-choices t))
ad-do-it))
(defadvice minibuffer-complete (around minibuffer-complete-limit-choices nil activate)
"When in find-file, check for files of extension .tex, and if they're found, ignore .log .pdf .bbl .aux"
(let ((add-or-remove
(if (and (boundp 'find-file-limit-choices) find-file-limit-choices
(save-excursion
(let ((b (progn (beginning-of-line) (point)))
(e (progn (end-of-line) (point))))
(directory-files (file-name-directory (buffer-substring-no-properties b e)) nil "\\.tex$"))))
'add-to-list
'remove)))
(mapc (lambda (e) (setq completion-ignored-extensions
(funcall add-or-remove 'completion-ignored-extensions e)))
'(".log" ".pdf" ".bbl" ".aux")))
ad-do-it)
Enjoy.

Probably the easiest way to do this in your case is just to customize the variable "completion-ignored-extensions".
However, this will mean that emacs always ignores things like ".log" and ".pdf" which may not be what you want. If you want it to be more selective, you may have to effectively re-implement the function file-name-completion.

If you are open to installing a large-ish library and reading some documentation, you could take a look at Icicles and define a sort function that meets your needs. An alternative is ido whose wiki page has an example of sorting by mtime, which should be easy to change to sort by a function of the filename extension.

Related

Emacs: fix 'changed on disk'/'Reread from disk' when file has not changed [duplicate]

How to disable Emacs from checking the buffer file was changed outside the editor?
Emacs is really trying to help you here. Read the info page on Protection against Simultaneous Editing.
But, if you still want to avoid that message/prompt, you can redefine the function that is doing the prompting:
(defun ask-user-about-supersession-threat (fn)
"blatantly ignore files that changed on disk"
)
(defun ask-user-about-lock (file opponent)
"always grab lock"
t)
The second function there is for when two people are using Emacs to edit the same file, and would provide a similar prompt (but not the one you seemed to refer to in the question).
I'd advise against overriding the two routines, but it's there if you want.
On the off chance global-auto-revert-mode is on, you could disable that. Add this to your .emacs:
(global-auto-revert-mode -1)
You can tell if the mode is on by looking at the variable of the same name:
C-h v global-auto-revert-mode RET
If the value is t, then the mode is on, otherwise it is off.
I have the following in my .emacs. It makes Emacs only ask about really changed files. If a file remains the same bytewise, just its timestamp is updated, as often happens when you switch branches in VCS, this "change" is ignored by Emacs.
;; Ignore modification-time-only changes in files, i.e. ones that
;; don't really change the contents. This happens often with
;; switching between different VC buffers.
(defun update-buffer-modtime-if-byte-identical ()
(let* ((size (buffer-size))
(byte-size (position-bytes size))
(filename buffer-file-name))
(when (and byte-size (<= size 1000000))
(let* ((attributes (file-attributes filename))
(file-size (nth 7 attributes)))
(when (and file-size
(= file-size byte-size)
(string= (buffer-substring-no-properties 1 (1+ size))
(with-temp-buffer
(insert-file-contents filename)
(buffer-string))))
(set-visited-file-modtime (nth 5 attributes))
t)))))
(defun verify-visited-file-modtime--ignore-byte-identical (original &optional buffer)
(or (funcall original buffer)
(with-current-buffer buffer
(update-buffer-modtime-if-byte-identical))))
(advice-add 'verify-visited-file-modtime :around #'verify-visited-file-modtime--ignore-byte-identical)
(defun ask-user-about-supersession-threat--ignore-byte-identical (original &rest arguments)
(unless (update-buffer-modtime-if-byte-identical)
(apply original arguments)))
(advice-add 'ask-user-about-supersession-threat :around #'ask-user-about-supersession-threat--ignore-byte-identical)
In my case I wanted:
(setq revert-without-query '(".*"))
Documentation for revert-without-query:
Specify which files should be reverted without query.
The value is a list of regular expressions.
If the file name matches one of these regular expressions,
then ‘revert-buffer’ reverts the file without querying
if the file has changed on disk and you have not edited the buffer.
I had annoyance with this because every time I switched branches in git, emacs thought all my files had changed.
Revbuffs helps you cope with the symptoms of this. It allows you to cause all your buffers to be reloaded.
You can also try (global-auto-revert-mode) which will automatically revert your files to what's on disk.

Allow dired-do-copy and dired-do-rename to create new dir on the fly

Does anyone have an emacs lisp hack that would allow the creation of a new directory on the fly during dired-do-copy or dired-do-rename? I understand that it can be created prior to running one of these two commands. Extra points for some type of "Are you sure..." prompt.
It look like a case of applying an advice. The question being: what to
advice. Looking at the dired code, it seem that the correct target is
dired-mark-read-file-name that is used to read the destination
file-name. This will work:
(defadvice dired-mark-read-file-name (after rv:dired-create-dir-when-needed (prompt dir op-symbol arg files &optional default) activate)
(when (member op-symbol '(copy move))
(let ((directory-name (if (< 1 (length files))
ad-return-value
(file-name-directory ad-return-value))))
(when (and (not (file-directory-p directory-name))
(y-or-n-p (format "directory %s doesn't exist, create it?" directory-name)))
(make-directory directory-name t)))))
Note that maybe the first when (when (member op-symbol '(copy move))) could be removed for this to apply to more case of file creation in dired. But I'm not sure of when dired-mark-read-file-name is called, So I let this test there to reduce potential unwanted side-effect

What is the canonical way to list numbered backup files emacs has created?

I know how to configure emacs to keep numbered backups. I don't know the most canonical way to find those numbered backups.
The emacs function "find-backup-file-name" seems like it is the closest. Its documentation states:
This function computes the file name for a new backup file for filename. It may also propose certain existing backup files for deletion. find-backup-file-name returns a list whose CAR is the name for the new backup file and whose CDR is a list of backup files whose deletion is proposed.
However, this is not what I am looking for. I'm looking for a list of ALL previously created backup files. Here's the code (paraphrased) I have written to accomplish this:
(defvar backup-directory "~/emacs.d/backups/")
(defun get-backup-pattern (file-name)
(concat "*" (replace-regexp-in-string "\/" "\\!" file-name t t) ".~*"))
(butlast
(split-string
(shell-command-to-string
(concat "find "
backup-directory
" -name \""
(get-backup-pattern (buffer-file-name))
"\""))
"\n"))
This method works fine. However, shelling out to "find" seems a like a hack to me; Especially since this method is platform specific.
Is there a built-in method I should use or at least something more idiomatic?
Personally, I don't save backup files in a central folder so I can't provide working code, but if you want to search the contents of a directory, use directory-files.
So here is the solution I've decided on. I went away from using the *nix find command and am using directory-files as suggested.
(defun get-filter-pattern (file-name)
(concat (replace-regexp-in-string "\/" "!" file-name t t)
".~[0-9]*~*$"))
(defun filter (condp lst)
(delq nil
(mapcar (lambda (x) (and (funcall condp x) x)) lst)))
(defun filter-files (backup-directory buffer-file-name)
(mapcar (lambda (backup-name) (concat backup-directory backup-name))
(filter (lambda (backup-name)
(string-match (get-filter-pattern buffer-file-name) backup-name))
(directory-files backup-directory))))
Perhaps this isn't quite as optimized as using find. However, it should be platform independent (ie can use on Windows).

How do I get emacs to write to read-only files automatically?

I am finding myself editing a lot of files that are read-only. I usually hit C-x C-q to call toggle-read-only. Then I hit C-x C-s to save and get,
File foo.txt is write-protected; try to save anyway? (y or n)
After hitting y, the file is saved and the permissions on the file remain read-only.
Is there a way to shorten this process and make it so that simply saving a file with C-x C-s does the whole thing without prompting? Should I look into inserting chmod in before-save-hook and after-save-hook or is there a better way?
Adding a call to chmod in before-save-hook would be clean way to accomplish this. There isn't any setting you can change to avoid the permissions check.
Based on the follow-up question, it sounds like you'd like the files to be changed to writable by you automatically upon opening. This code does the trick:
(defun change-file-permissions-to-writable ()
"to be run from find-file-hook, change write permissions"
(when (not (file-writable-p buffer-file-name))
(chmod buffer-file-name (file-modes-symbolic-to-number "u+w" (nth 8 (file-attributes buffer-file-name))))
(if (not (file-writable-p buffer-file-name))
(message "Unable to make file writable."))))
(add-hook 'find-file-hook 'change-file-permissions-to-writable)
Note: When I tested it on my Windows machine, the file permissions didn't show up until I tried to save the buffer, but it worked as expected. I personally feel uneasy about this customization, but it's your Emacs. :)
I agree with Trey that universally doing a chmod on write is risky -- read-only files are read-only for a reason, IMHO. Here's a way to specifically override things on a per-buffer basis. It's not ideal in that it overrides file-writable-p for the life of the buffer (or at least until you toggle my-override-mode-on-save back to nil), but it makes you make a conscious decision on a file-by-file basis (sort-of; it's really a buffer-by-buffer basis, which is fairly similar). Of course since you're looking to automatically toggle the read-only flag when the file is visited, you might not be interested in this distinction. Still, here it is; enjoy it or ignore it as you will.
(make-variable-buffer-local
(defvar my-override-mode-on-save nil
"Can be set to automatically ignore read-only mode of a file when saving."))
(defadvice file-writable-p (around my-overide-file-writeable-p act)
"override file-writable-p if `my-override-mode-on-save' is set."
(setq ad-return-value (or
my-override-mode-on-save
ad-do-it)))
(defun my-override-toggle-read-only ()
"Toggle buffer's read-only status, keeping `my-override-mode-on-save' in sync."
(interactive)
(setq my-override-mode-on-save (not my-override-mode-on-save))
(toggle-read-only))
P.S. Thanks to Trey for the ad-return-value pointer in the other SO question.
Since I find it useful to be constantly reminded that I am about to edit a file I do not have permissions to, when I open a file in a buffer I want to force myself to proactively make the buffer writable wit C-x q. Opening it with tramp by hand however is quite tedious so I advise save-buffer to prompt me for password if it fails to write. I totally recommend you put this snippet in your .emacs
(defadvice save-buffer (around save-buffer-as-root-around activate)
"Use sudo to save the current buffer."
(interactive "p")
(if (and (buffer-file-name) (not (file-writable-p (buffer-file-name))))
(let ((buffer-file-name (format "/sudo::%s" buffer-file-name)))
ad-do-it)
ad-do-it))
For a reason I could no determine the Trey Jackson solution does not work on my gnu emacs 25.2 under windows: the file-modes-rights-to-number called from file-modes-rights-to-number fails.
If someone is stuck with the same problem he could use a less elegant but working solution replace block :
(chmod buffer-file-name (file-modes-symbolic-to-number "u+w" (nth 8 (file-attributes buffer-file-name))))
with block :
(cond ((or (eq system-type 'ms-dos) (eq system-type 'windows-nt))
(progn
(shell-command-to-string (concat "attrib -R " (buffer-file-name (current-buffer))))
(message "Setting file and buffer to writeable (%s style)" system-type)
))
((eq system-type 'gnu/linux)
(progn
(shell-command-to-string (concat "chmod u+w " (buffer-file-name (current-buffer))))
(message "Setting file and buffer to writeable (%s style)" system-type)
))
(t (message "file permission change not handle for OS %s" system-type))
)

How to get equivalent of Vim's :Texplore in Emacs?

I know about M-x dire, but would like to customize it. I would like to hit one key (for example F2) and get dire buffer open. When I navigate across the directory hierarchy it shouldn't open new buffers.
And when I finally open the file it also shouldn't open new buffer for it (not strictly necessary, but strongly preferred).
Of course this behavior can be global, i.e. for all dire buffers/invocations.
Check out dired-single, which pretty much does what you want (except that last bit, where it reuses the dired buffer for the newly visted file).
Caveat Lector: I wrote it, so I'm biased towards its usefulness.
Some alternatives - EmacsWiki: DiredReuseDirectoryBuffer, and this short snippet from an awkwardly-formatted blog-entry.
caveat: haven't tried them, myself.
I know this is very old but All you have to do is press 'a' on a dir or file to get this functionality. It's already there.
Here's what I finally used:
(require 'dired)
(global-set-key [(f2)] 'my-dired)
(defun my-dired ()
(interactive)
(dired (file-name-directory (buffer-file-name))))
(defadvice dired-advertised-find-file (around dired-subst-directory activate)
"Replace current buffer if file is a directory."
(interactive)
(let ((orig (current-buffer)) (filename (dired-get-filename :no-error-if-not-filep t)))
ad-do-it
(when (not (eq (current-buffer) orig)) (kill-buffer orig))))