How can I check if a file exists using Emacs Lisp? - emacs

I would like emacs to mark files that are generated as read-only when they're opened. The part of the puzzle that I'm missing is how to check if a file "exists". I currently have the following:
;;
;; get file extension
;;
(defun get-ext (file-name)
(car (cdr (split-string file-name "\\."))))
;;
;; get the base name of the file
;;
(defun base-name (file-name)
(car (split-string file-name "\\.")))
;;
;; if an 'lzz' file exists for this header, mark it as read only
;;
(defun mark-read-only ()
(if (string= (get-ext (cur-file)) "h")
(if ( ??file-exists??? (concat (base-name (cur-file)) ".lzz") )
(toggle-read-only))))
What can I use for "???file-exists???"?
Once I find this, I'll add "mark-read-only" to the appropriate hook (which I think is the find-file-hook).
BACKGROUND
We use lzz as a code generator to simplify our C/C++ development process. Briefly, lzz takes a single input file (which looks very like C/C++) and generates header and source files as appropriate.
By default, lzz includes #line directives so that the debugger points to the original source and not the generated source, however, to reduce compilation dependencies we normally disable these directives in header files. The result is that when debugging templates or inline functions, the debugger normally points to the generated header file and not the original source file.
This is not a big deal, however, recently I've found that when debugging I'll make a quick modification to the displayed file and then I'll rebuild. Of course this normally means that the change I made disappears because the file I edited is generated and so the changes are "blown away" during the library rebuild.
SOLUTION
Thanks to everyone for their help and comments. A special thanks to cobbal for pointing out the correct function to use.
Here's the resulting code (with updates based on the other comments here too):
(defun cur-file ()
"Return the filename (without directory) of the current buffer"
(file-name-nondirectory (buffer-file-name (current-buffer)))
)
(defun mark-generated-as-read-only ()
"Mark generated source files as read only.
Mark generated files (lzz or gz) read only to avoid accidental updates."
(if
(or (string= (file-name-extension (cur-file)) "h")
(string= (file-name-extension (cur-file)) "cpp"))
(cond
(
(file-exists-p (concat (file-name-sans-extension (cur-file)) ".lzz"))
(toggle-read-only))
(
(file-exists-p (concat (file-name-sans-extension (cur-file)) ".gz") )
(toggle-read-only))
)
)
)

try file-exists-p
"Return t if file filename exists (whether or not you can read it.)".
Note that it's not spesific to files and works for directories too.

Depending on what you need, you might want file-readable-p instead of file-exists-p.
Apropos will only get you so far. Icicles provides apropos completion and progressive completion which let you find help easily for command, function, variable, etc. names that match subparts in an arbitrary order (is it file-exists-p or exists-file-p?).

Use f.el, modern library for file and directory manipulation. You can use f-exists?, f-file?, f-directory? and many other predicates. The library is better than standard functions, because it's every file related function you'll ever need under one namespace.

Related

How to log spelling replacements in emacs?

I want to log to a file the ispell replacements that get made (whether manual r, or from a list 0...)
Every time a "correction" is made, there are two words that are relevant:
The word that ispell identifies as incorrect.
The word that ends up in its place. [maybe "" when its skipped]
I just want to log these pairs to a file for "analysis" (and possibly flashcards)
I am still browsing code to see if there is a place to wedge this in. I see ispell-update-post-hook used in ispell-command-loop but I'm not sure if that's what I want. I also am not sure how I'd both get the above pair of words and write them to a file, as the hook doesn't (elisp ignorance?) seem to provide access.
This code does what you've requested. It does not check for duplicates in the file that's generated, it simply appends to the existing file.
(defvar save-ispell-words-file "~/spell_check.txt")
(defadvice ispell-command-loop (after save-ispell-words activate)
"Save the misspelled words and their replacements"
(when (or (null ad-return-value)
(stringp ad-return-value))
(save-excursion
(set-buffer (find-file-noselect save-ispell-words-file))
(goto-char (point-max))
(insert (format "%s %s\n" (ad-get-arg 2) (if (null ad-return-value) "\"\"" ad-return-value)))
(save-buffer))))
Tested with Emacs 27.2.

Adding hook on elisp function

I'm using Emacs.
Is there any way to add hook on a function?
Assume that there is a markdown-export function.
It is designed to export HTML file into current directory where current working 'markdown file' exsits.
But, I want to export HTML file into another directory. How can I do that without modification on Emacs markdown plugin (markdown-mode.el)?
This is markdown-mode.el's export function:
(defun markdown-export (&optional output-file)
"Run Markdown on the current buffer, save to file, and return the filename.
If OUTPUT-FILE is given, use that as the filename. Otherwise, use the filename
generated by `markdown-export-file-name', which will be constructed using the
current filename, but with the extension removed and replaced with .html."
(interactive)
(unless output-file
(setq output-file (markdown-export-file-name ".html")))
(when output-file
(let* ((init-buf (current-buffer))
(init-point (point))
(init-buf-string (buffer-string))
(output-buffer (find-file-noselect output-file))
(output-buffer-name (buffer-name output-buffer)))
(run-hooks 'markdown-before-export-hook)
(markdown-standalone output-buffer-name)
(with-current-buffer output-buffer
(run-hooks 'markdown-after-export-hook)
(save-buffer))
;; if modified, restore initial buffer
(when (buffer-modified-p init-buf)
(erase-buffer)
(insert init-buf-string)
(save-buffer)
(goto-char init-point))
output-file)))
=====================================================================
I have made an advice to save exported HTML at temp directory
Here is the code.
(defadvice markdown-export (around set-temp-path-for-exported-file activate)
(ad-set-arg 0 (format "%s/%s" "~/.emacs.d/temp-dir" (file-name-nondirectory buffer-file-name)))
ad-do-it)
Thanks!!!!!!!!!!!!!!
In this case you do not need to hook on this function since it already accepts the filename as an argument, unfortunately it does not accept the filename when called interactively. As a workaround you can define a simple wrapper around the function like follows
(defun my-markdown-export (&optional file)
(interactive (list (ido-read-file-name "Export as: ")))
(markdown-export file))
The advice mechanism is a bit like having hooks for any arbitrary function, but here you have actual hooks you can use, as well as a function argument which addresses your requirement directly.
So you can:
(a) Pass the function any arbitrary output filename.
(b) Use the provided markdown-before-export-hook to setq whichever variables you need to (which at a glance looks like output-file, output-buffer, and output-buffer-name).

How to make all org-files under a folder added in agenda-list automatically?

I am using org-mode to write notes and org-agenda to organize all notes, especially to search some info. by keyword or tag.
C-c a m can search some files by tag inputed, C-c a s by keyword ,those functions from org-agenda are well to utilize, however, I need to add org-file into the agenda-list by hand.
I added some codes into .emacs, such as
(setq org-agenda-files (list "path/folder/*.org"))
or
(setq org-agenda-files (file-expand-wildcards "path/folder/*.org"))
but, both failed to add files under the folder specified into agenda-list automatically, so I can't search keyword or tag among those org-files, unless that I open a org-file and type C-c [ to add it into agenda-list.
How can I make all org-files under a folder automatically added in agenda?
Just naming the directory should be enough. For example this works for me very well:
(setq org-agenda-files '("~/org"))
Also take a look at org-agenda-text-search-extra-files; it lets you
add extra files included only in text searches. A typical value might
be,
(setq org-agenda-text-search-extra-files
'(agenda-archives
"~/org/subdir/textfile1.txt"
"~/org/subdir/textfile1.txt"))
Caveat: If you add a file to the directory after you have started
Emacs, it will not be included.
Edit: (2018) To include all files with a certain extension in the extra files list you can try the following function I wrote sometime back (a more recent version might be available here).
;; recursively find .org files in provided directory
;; modified from an Emacs Lisp Intro example
(defun sa-find-org-file-recursively (&optional directory filext)
"Return .org and .org_archive files recursively from DIRECTORY.
If FILEXT is provided, return files with extension FILEXT instead."
(interactive "DDirectory: ")
(let* (org-file-list
(case-fold-search t) ; filesystems are case sensitive
(file-name-regex "^[^.#].*") ; exclude dot, autosave, and backupfiles
(filext (or filext "org$\\\|org_archive"))
(fileregex (format "%s\\.\\(%s$\\)" file-name-regex filext))
(cur-dir-list (directory-files directory t file-name-regex)))
;; loop over directory listing
(dolist (file-or-dir cur-dir-list org-file-list) ; returns org-file-list
(cond
((file-regular-p file-or-dir) ; regular files
(if (string-match fileregex file-or-dir) ; org files
(add-to-list 'org-file-list file-or-dir)))
((file-directory-p file-or-dir)
(dolist (org-file (sa-find-org-file-recursively file-or-dir filext)
org-file-list) ; add files found to result
(add-to-list 'org-file-list org-file)))))))
You can use it like this:
(setq org-agenda-text-search-extra-files
(append (sa-find-org-file-recursively "~/org/dir1/" "txt")
(sa-find-org-file-recursively "~/org/dir2/" "tex")))
Edit: (2019) As mentioned in the answer by #mingwei-zhang and the comment by #xiaobing, find-lisp-find-files from find-lisp and directory-files-recursively also provides this functionality. However, please note in these cases the file name argument is a (greedy) regex. So something like (directory-files-recursively "~/my-dir" "org") will give you all Org files including backup files (*.org~). To include only *.org files, you may use (directory-files-recursively "~/my-dir" "org$").
There is a simpler way of doing recursive search of org files (courtesy #xiaobing):
(setq org-agenda-files (directory-files-recursively "~/org/" "\\.org$"))
EDIT: You can also filter out certain directory from lookup by adding a array filter. Example, filtering out all org files in xxxx/xxx/daily/ directory:
(setq org-agenda-files
(seq-filter (lambda(x) (not (string-match "/daily/"(file-name-directory x))))
(directory-files-recursively "~/Notes/roam" "\\.org$")
))
For Emacs <25, you can use find-lisp-find-files:
(load-library "find-lisp")
(setq org-agenda-files
(find-lisp-find-files "FOLDERNAME" "\.org$"))

Emacs - intercept file before it is open with external program and modify it, then open modified verison

I need to process a file of certain types with external command line program accepting single argument (filename) and then use file modified by this program either open modified file or accept output of command line program as data source for file.
Any way to do this?
Where I used to work there were some binary files that I wanted to view in emacs. The way I did this was to add to jka-compr-compression-info-list like the following for editing applescripts:
(add-to-list 'jka-compr-compression-info-list
["\\.scpt\\'"
"Compiling" "osacompile-helper.sh" nil
"Decompiling" "osacompile-helper.sh" ("-d")
nil nil "Fasd"])
(jka-compr-update)
Here osacompile-helper.sh is just a little shell wrapper around osacompile and osadecompile that reads from stdin and writes to stdout (which is required). You also need to turn on auto-compression-mode, although I think that's the default. If you use the customize interface to change jka-compr-compression-info-list, instead of setting it directly, then you don't have to call jka-compr-update.
If you just want this to work when you open the file with C-x C-f, then you can probably just attach your behaviour to find-file, but deeper down I believe insert-file-contents is what eventually reads files in.
A cursory look doesn't seem to show any appropriate hook, so you could look at doing this with before advice.
(defadvice insert-file-contents
(before my-before-insert-file-contents-advice)
"Process files externally before reading them."
(let ((filename (expand-file-name (ad-get-arg 0))))
(message "About to read file %s" filename)
;; your code here.
;; ;; stupid unsafe example:
;; (let ((file (shell-quote-argument filename))
;; (tempfile (shell-quote-argument (make-temp-file "some-prefix-"))))
;; (shell-command (format "sort %s >%s" file tempfile))
;; (shell-command (format "mv %s %s" tempfile file)))
))
(ad-activate 'insert-file-contents)
You might like to elaborate on your requirements, in case you don't actually need to clobber the original file? (which I think is a horrendous idea, frankly; I certainly wouldn't use code like this!)
For example, you could read in the original file, process it within the buffer (maybe using shell-command-on-region with the replace flag), and set the buffer as unmodified. That way you are only likely to save the changes made by the shell command if you make other edits to the file, and the mere act of loading the file into an editor hasn't actually modified it.
In any case, I trust you'll implement sensible backup processes into your code, and will be plenty paranoid when testing!
You can call the external program with shell-command, with the output directed to a new buffer. A minimal working example is:
(defun my-find-and-process-file ()
(interactive)
(let* ((file (read-file-name "File name: "))
(buf (pop-to-buffer file)))
(shell-command (format "cat %s" file) buf)))
Replace cat with the name of your program. This will create a buffer and fill it with the output of your program. If a buffer with the name of your file already exists, it will over-write it. If that's a possibility, you will want to change the buffer name to something safe by adding a suffix or something. This code also doesn't trigger any of the find-file hooks, so you'll have to manually select the mode, or modify the code to do that for you.

Emacs incorrectly looking for .el instead of .elc

I recently started using django-html-mumamo-mode which is part of nXhtml in emacs and everything seems to work except that when I start writing javascript code in an html page, I get the warning/error
Can't find library /usr/share/emacs/23.2/lisp/progmodes/js.el
I checked in that folder and all of the files have the .elc extension including js.elc, which is probably why emacs can't find it. Can I change something to make emacs just load the .elc file?
Edit: This continues to occur if I run M-x load-library js or M-x load-library js.elc
Edit2: I have confirmed that load-suffixes is set to ("el" "elc"), and that js.elc is in the progmodes folder, which is in load-path and that all users have read permissions for that file. I am using emacs version 23.2.1, and when I set debug-on-error to t I got a traceback, and it looks like the following part contains the error:
error("Can't find library %s" "/usr/share/emacs/23.2/lisp/progmodes/js.el")
find-library-name("/usr/share/emacs/23.2/lisp/progmodes/js.el")
find-function-search-for-symbol(js-indent-line nil "/usr/share/emacs/23.2/lisp/progmodes/js.elc")
(let* ((lib ...) (where ...) (buf ...) (pos ...)) (with-current-buffer buf (let ... ... ... ...)) (put fun (quote mumamo-evaled) t))
(if (get fun (quote mumamo-evaled)) nil (let* (... ... ... ...) (with-current-buffer buf ...) (put fun ... t)))
(unless (get fun (quote mumamo-evaled)) (let* (... ... ... ...) (with-current-buffer buf ...) (put fun ... t)))
(progn (unless (get fun ...) (let* ... ... ...)))
(if mumamo-stop-widen (progn (unless ... ...)))
(when mumamo-stop-widen (unless (get fun ...) (let* ... ... ...)))
Notably, the third line contains a reference to the correct file, but it ends up trying to load the wrong one. Has anyone seen this kind of thing before or have any idea how to fix it?
If you read the section in the Emacs manual on "How Programs Do Loading, the js.elc file should be loaded if normal library -loading commands (e.g. - "require", "autoload", "load-file", etc) are being used. Some things to do to debug this:
Does your userid have system security permissions to access the js.el file in that location?
If you type M-x emacs-version, what version of Emacs are you running?
The "load-library" command searches for lisp files in the "load-path". When you examine the contents of your load-path, is the specified directory in it?
Set the variable "debug-on-error" to "t" and re-attempt to write javascript code in an html page - when the error occurs, check the source line where the error occurs and, if it's not apparent from that what is causing the problem, post an update to your question with a few lines of the source where the error occurred as well as the stack trace that was produced by Emacs.
EDIT: Ok, now that you've added the stack trace, it's possible to see why the error is occurring. Here are the key lines from the "find-function-search-for-symbol" function (which is the function where the error is occurring in):
(when (string-match "\\.el\\(c\\)\\'" library)
(setq library (substring library 0 (match-beginning 1))))
;; Strip extension from .emacs.el to make sure symbol is searched in
;; .emacs too.
(when (string-match "\\.emacs\\(.el\\)" library)
(setq library (substring library 0 (match-beginning 1))))
(let* ((filename (find-library-name library))
In line#2, the function is setting the library name equal to the "*.elc" library name minus the "c" (e.g. it's converting it from "/usr/share/emacs/23.2/lisp/progmodes/js.elc" to "/usr/share/emacs/23.2/lisp/progmodes/js.el". Then, in line#7 of the above code, it's trying to find that source member (and failing as it doesn't exist). Looking further at the stack trace, the key line is:
(if (get fun (quote mumamo-evaled)) nil (let* (... ... ... ...) (with-current-buffer buf ...) (put fun ... t)))
which is called in the nXhtml "mumamo-funcall-evaled" function. The author of nXhtml obviously has not considered that the ".elc" file may exist but that the ".el" is not in the same directory. It appears that he used to distribute js.el with nXhtml but stopped doing so since it is now shipped with most recent Emacs distributions. So, in his environment, he probably has the ".el" files in the same directory as the ".elc" files and hasn't encountered this problem.So, you should probably do 2 things:
Notify the author of the nXhtml library so that he can fix the bug in his code.
Copy the necessary ".el" source files to "/usr/share/emacs/23.2/lisp/progmodes/" so that you don't get the error. Alternatively, you may choose to re-install js.el (and possibly some other modules) in another directory and put that directory ahead of "/usr/share/emacs/23.2/lisp/progmodes/" in your "load-path".
Doing #1 will get the problem fixed in the long-term while doing #2 should let you use nXhtml in the short-term.
Check your value of load-suffixes
C-h v load-suffixes. You probably want this to be something like (".elc" ".el"). If it is make sure that your mode hasn't set it to something weird, or bound it dynamically.