How to throw a value when exiting recursive edit (without using setq) - emacs

Is it possible to throw a value when exiting recursive edit without using setq?
The functions below work correctly with setq, however, my goal is to eliminate unnecessary global variables (if possible) -- especially file names -- and use let bound variables instead. In the context of throwing a value when exiting recursive edit, however, I have been unable to devise a method that does not use a global variable for the file name.
In this example, I am using lawlist-save-as in conjunction with dired-read-file-name to enter dired-mode and select a file name or a path. Pressing the enter key on a file name selects a file. Pressing the enter key on a directory name or the two (2) dots moves up one directory. Pressing the enter key on the line with just one (1) dot, means select only the path in the current directory. The value of the file name or path is passed back to the function lawlist-save-as through the last line of dired-read-file-name.
[Although not necessary for this example (but just in case anyone is curious), getting marked files with (dired-get-marked-files) is used for a different situation, such as attaching multiple files to an e-mail using Wanderlust -- in which case, I use the condition ((listp lawlist-filename) (throw 'exit nil)). And, of course, opening a file is self-explanatory -- used for a situation when dired-mode is entered regularly and then just pressing the return key on a file to be opened.]
(require 'dired)
(defvar lawlist-filename nil)
(defvar save-as-buffer-filename nil)
(defvar save-as-variable nil)
(defvar dired-buffer-name nil)
(defun dired-read-file-name (&optional directory)
(let ((working-buffer (buffer-name)))
(if directory
(dired directory)
(dired nil))
(let ((dired-buffer-name (buffer-name)))
(if save-as-buffer-filename
(progn
(goto-char (point-min))
(re-search-forward (file-name-nondirectory save-as-buffer-filename) nil t)))
(recursive-edit)
(kill-buffer dired-buffer-name)
(switch-to-buffer working-buffer)
lawlist-filename)))
;; select file or directory.
(define-key dired-mode-map (kbd "<return>") (lambda () (interactive)
(setq lawlist-filename
(if (or (re-search-backward "^*" nil t)
(re-search-forward "^*" nil t))
(dired-get-marked-files)
(dired-get-file-for-visit)))
(cond
((listp lawlist-filename)
(throw 'exit nil))
;; open file
((and (not (file-directory-p lawlist-filename))
(file-exists-p lawlist-filename)
(not (equal lawlist-filename (concat (file-name-directory lawlist-filename) ".")))
(not save-as-variable))
(find-file lawlist-filename))
;; save-as
((and (not (file-directory-p lawlist-filename))
(file-exists-p lawlist-filename)
(not (equal lawlist-filename (concat (file-name-directory lawlist-filename) "."))))
(throw 'exit nil))
;; go up one directory
((and (file-directory-p lawlist-filename)
(not (equal lawlist-filename (concat (file-name-directory lawlist-filename) "."))))
(setq dired-buffer-name (buffer-name))
(dired-find-file)
(goto-char (point-min))
(re-search-forward " \\.\\.$" nil t)
(kill-buffer dired-buffer-name)
(setq dired-buffer-name (buffer-name)))
;; only use current path for save-as situation.
((and (equal lawlist-filename (concat (file-name-directory lawlist-filename) "."))
save-as-variable)
(setq lawlist-filename (expand-file-name default-directory))
(throw 'exit nil)) )))
(defun lawlist-save-as ()
(interactive)
(setq save-as-variable t)
(if (buffer-file-name)
(setq save-as-buffer-filename (buffer-file-name)))
(let ((proposed-filename (dired-read-file-name)))
(when proposed-filename ;; needed if aborting recursive-edit
(setq save-as-variable nil)
(let ((save-as-filename (read-string "Save-As: "
(concat proposed-filename (when (file-directory-p proposed-filename) (buffer-name))))))
(setq save-as-buffer-filename nil)
(when (and save-as-filename (file-exists-p save-as-filename))
(or (y-or-n-p (format "File `%s' exists; overwrite? " save-as-filename))
(error "Canceled")))
(set-visited-file-name save-as-filename)
(set-buffer-modified-p t)
(and (buffer-file-name)
(file-writable-p buffer-file-name)
(setq buffer-read-only nil))
(save-buffer)))))

recursive-edit is just a function that runs a command loop. All stuff that is valid for lisp applies.
So if you encapsulate recursive-edit into a let which locally binds some variable, say test, and if you setq this variable during the recursive edit session this variable is just set locally in the scope of the let.
Example:
Run the following with C-x C-e:
(let (test)
(recursive-edit)
(message "test=%S" test))
You will be in the command loop of the recursive edit. Then set test via M-: (setq test "That is my test.").
Afterwards press M-C-c to exit the recursive edit session.
The message test=\"That is my test.\" is be printed out but the symbol test is still unbound.

Related

Emacs -- modify `dired-mark` to prevent moving cursor to an empty line at eob

I have a custom function that gets dired marked file names (i.e., the names of every marked file) if more than one has been marked, or if only one, it gets the file name at point. A problem occurs whenever the cursor is in no-man's-land because dired-mark (or me, because I'm the pilot) got overzealous on repetition and moves the cursor to a blank line at the end of the buffer where there is no file. In that situation, the error is as follows:
Debugger entered--Lisp error: (error "No file on this line")
signal(error ("No file on this line"))
error("No file on this line")
dired-get-file-for-visit()
(file-directory-p (dired-get-file-for-visit))
(if (file-directory-p (dired-get-file-for-visit)) nil (let ((lawlist-filename (if (or (re-search-backward "^*" nil t) (re-search-forward "^*" nil t)) (dired-get-marked-files) (dired-get-file-for-visit)))) (wl-draft-mailto) (attach-multiple-files lawlist-filename)))
(lambda nil (interactive) (if (file-directory-p (dired-get-file-for-visit)) nil (let ((lawlist-filename (if (or (re-search-backward "^*" nil t) (re-search-forward "^*" nil t)) (dired-get-marked-files) (dired-get-file-for-visit)))) (wl-draft-mailto) (attach-multiple-files lawlist-filename))))()
funcall-interactively((lambda nil (interactive) (if (file-directory-p (dired-get-file-for-visit)) nil (let ((lawlist-filename (if (or (re-search-backward "^*" nil t) (re-search-forward "^*" nil t)) (dired-get-marked-files) (dired-get-file-for-visit)))) (wl-draft-mailto) (attach-multiple-files lawlist-filename)))))
call-interactively((lambda nil (interactive) (if (file-directory-p (dired-get-file-for-visit)) nil (let ((lawlist-filename (if (or (re-search-backward "^*" nil t) (re-search-forward "^*" nil t)) (dired-get-marked-files) (dired-get-file-for-visit)))) (wl-draft-mailto) (attach-multiple-files lawlist-filename)))) nil nil)
command-execute((lambda nil (interactive) (if (file-directory-p (dired-get-file-for-visit)) nil (let ((lawlist-filename (if (or (re-search-backward "^*" nil t) (re-search-forward "^*" nil t)) (dired-get-marked-files) (dired-get-file-for-visit)))) (wl-draft-mailto) (attach-multiple-files lawlist-filename)))))
I like the ability to hold the dired-mark key down for repetition, but I would like it to stop at the last line with a file in the dired-mode buffer. Any ideas on how to accomplish that goal would be greatly appreciated. Moving past that point, performing a test, and then returning to the previous point seems to not be a very efficient means of handling it. It might be better to terminate the dired buffer at the end of the last line with a file, so that there is no blank line -- i.e., if there is no blank line at the end of the dired-mode buffer, then dired-mark can't go there (so that would be good?).
EDIT:  The debugger message has been updated with a complete backtrace. The following is the custom function that may be, as #Drew suggests, the root cause of the error message that I am receiving:
;; E-mail attachments using Wanderlust.
(define-key dired-mode-map (kbd "C-c e") (lambda () (interactive)
;; if hovering over a directory, then do nothing
(unless (file-directory-p (dired-get-file-for-visit))
(let ((lawlist-filename
(if (or (re-search-backward "^*" nil t)
(re-search-forward "^*" nil t))
(dired-get-marked-files)
(dired-get-file-for-visit))))
(wl-draft-mailto)
(attach-multiple-files lawlist-filename)))))
(defun attach-multiple-files (&optional lawlist-filename)
"Either (dired-get-marked-files) or (dired-get-file-for-visit) when exiting recursive edit."
(interactive)
(let* (
new-dir
(beg (point))
(dir "/Users/HOME/.0.data"))
(goto-char (point-min))
(when (and (re-search-forward "username#hostname.com" nil t) (not (re-search-forward "username#hostname.com\n" nil t)))
(goto-char (point-max))
(newline 2))
(catch 'done
(while t
(goto-char (point-max))
(let* (
(multi-attach-variable t)
(next-file
(if lawlist-filename
lawlist-filename
(dired-read-file-name
(if new-dir
new-dir
dir)))) )
(setq new-dir
(cond
((and next-file (stringp next-file))
(file-name-directory next-file))
((and next-file (listp next-file))
(file-name-directory (car next-file)))))
(cond
((stringp next-file)
(mime-edit-insert-file next-file t))
((listp next-file)
(mapcar (lambda (x) (mime-edit-insert-file x t)) next-file)))
(setq lawlist-filename nil)
(if (not (lawlist-y-or-n-p "Attach another? "))
(progn
(goto-char beg)
(throw 'done nil))))))))
Try defadvice on dired-mark to avoid landing in no-man's-land:
(defadvice dired-mark (after stop-at-last-file activate)
(when (eobp)
(previous-line 1)))
The "move forward after marking" functionality is baked into dired-mark (actually, if you want to get into the guts of it, into dired-repeat-over-lines), so this advice checks if it put you on that blank line and moves you back if so. Your last paragraph suggests you might not be super-keen on this option, but it strikes me as less troublesome than altering the dired internals.
Previous Answer (October 17, 2014):  The following 3 lines of code will halt the function if the cursor is not on a file or directory and if nothing has been marked:
(when (null (dired-get-marked-files))
(let ((debug-on-quit nil))
(signal 'quit `("You are not on a line containing a valid file or directory."))))
Revised Answer (January 20, 2015):  The function dired-insert-directory may be modified to delete trailing new lines and/or whitespace at the end of the buffer as follows:
(goto-char (point-max))
(let ((trailing-whitespace+newlines (abs (skip-chars-backward "\s\r\n\t"))))
(when (> trailing-whitespace+newlines 0)
(delete-char trailing-whitespace+newlines)))

Asking emacs for default directory path "once"

I want to have a variable that keeps the default directory a user enters and keep using it throughout the run of emacs.
Basically, when the user executes a custom command, the prompt will ask for a default directory path to execute the command (only once) and whenever the user calls the same command emacs uses the same path onward.
How can I program that snippet of code in lisp?
I basically want this code in the igrep library to accept the input from user once and not ask again:
(defvar default-files-string-new "*.[sch]")
(defun igrep-read-files (&optional prompt-prefix)
"Read and return a file name pattern from the minibuffer.
If `current-prefix-arg' is '(16) or '(64), read multiple file name
patterns and return them in a list. Optional PROMPT-PREFIX is
prepended to the \"File(s): \" prompt."
(let* ((default-files (igrep-default-files))
(default-files-string (mapconcat 'identity default-files " "))
(insert-default-directory igrep-insert-default-directory)
(file (igrep-read-file-name
(igrep-prefix prompt-prefix
(if default-files
(format "File(s) [default: %s]: "
default-files-string)
"File(s): "))
nil (if default-files default-files-string "") nil nil
'igrep-files-history))
(files (list file)))
(if (or igrep-read-multiple-files
(and (consp current-prefix-arg)
(memq (prefix-numeric-value current-prefix-arg)
'(16 64))))
(let* ((key (igrep-default-key 'exit-minibuffer
minibuffer-local-completion-map
"\r"))
(prompt
(igrep-prefix prompt-prefix
(if igrep-verbose-prompts
(format "File(s): [Type `%s' when done] "
(key-description key))
"File(s): "))))
(while (and (setq file
(igrep-read-file-name prompt
nil "" nil nil
'igrep-files-history))
(not (equal file "")))
(setq files (cons file files)))))
(mapcar (lambda (file)
(if (file-directory-p file)
;; really should map expand-file-name over default-files:
(expand-file-name (if default-files default-files-string-new "*")
file)
file))
(nreverse files))))
You could use advices to do that:
(defvar wd-alist nil)
(mapc
(lambda (function)
(eval
`(defadvice ,function (around ,(intern (format "%s-wd" function)) activate)
(let ((wd (cdr (assoc ',function wd-alist))))
(unless wd
(setq wd (read-file-name "Default directory: "))
(push (cons ',function wd) wd-alist))
(let ((default-directory wd))
ad-do-it)))))
'(grep-find))
The variable wd-list stores the association (FUNCTION . PATH). The list mapc iterate over are the advised functions. Now, when calling find-grep, it asks for the working directory (after interactive arguments, so you first have to type the pattern and enter...) and stores it in wd-list for further use. Now your find-grep are always done in that directory.
You could have a custom variable for the sane default, and then have the user enter the path or accept the default on the first call.
(defcustom default-path "/tmp/foo" "Path")
(setq current-path nil)
(defun foo ()
(interactive)
(unless current-path
(setq current-path
(read-from-minibuffer
(format "Path [%s]" default-path) nil nil t nil default-path)))
(message "Path is: %s" current-path))
The first time you do M-x foo, it prompts for the path. A common idiom is to allow the user to specify a prefix argument when they want to change the value (after the first time.) This code will have the desired effect:
(defun foo (choose)
(interactive "P")
(when (or choose (not current-path))
(setq current-path
(read-from-minibuffer
(format "Path [%s]" default-path) nil nil t nil default-path)))
(message "Path is: %s" current-path))
Now doing M-x foo is the same as before, but C-0 M-x foo will prompt for a new value.
In your example, something like this will work.
(defun igrep-read-files (&optional prompt-prefix)
(interactive "P")
(when (or prompt-prefix (not current-path ))
(setq current-path
(read-file-name "Dir: " default-path nil t)))
(message (expand-file-name default-files-string-new current-path)))
Have a look at the code of sendmail-query-once.
Although it's not very fashionable to do this sort of thing.
Usually package writers pick a sane default and let the user
customize it as they want.

Save buffer as a *.pdf with `ns-write-file-using-panel` or similar option

The existing code written by Rupert Swarbrick, later modified by Rory Yorke, still leaves open the need to specify the file location with a save-as function (e.g. on OSX, this would be ns-write-file-using-panel). Does anyone have a suggestion, please, that adds an option similar to ns-write-file-using-panel and/or perhaps modifies the /tmp directory option written in the script?
Word wrap for Emacs print buffer to PDF
Formatting a header in an Emacs function to print a buffer to PDF w/ line wrapping
(defun harden-newlines ()
(interactive)
"Make all the newlines in the buffer hard."
(save-excursion
(goto-char (point-min))
(while (search-forward "\n" nil t)
(backward-char)
(put-text-property (point) (1+ (point)) 'hard t)
(forward-char))))
;; (defun spool-buffer-given-name (name)
;; (load "ps-print")
;; (let ((tmp ps-left-header))
;; (unwind-protect
;; (progn
;; (setq ps-left-header
;; (list (lambda () name) 'ps-header-dirpart))
;; (ps-spool-buffer-with-faces))
;; (setf ps-left-header tmp))))
(defun spool-buffer-given-name (name)
(let ((ps-left-header (list (format "(%s)" name))))
(ps-spool-buffer-with-faces)))
(defun print-to-pdf ()
"Print the current file to /tmp/print.pdf"
(interactive)
(let ((wbuf (generate-new-buffer "*Wrapped*"))
(sbuf (current-buffer)))
(jit-lock-fontify-now)
(save-current-buffer
(set-buffer wbuf)
(insert-buffer sbuf)
;; (longlines-mode t)
(visual-line-mode t)
(harden-newlines)
(spool-buffer-given-name (buffer-name sbuf))
(kill-buffer wbuf)
(switch-to-buffer "*PostScript*")
(write-file "/tmp/print.ps")
(kill-buffer (current-buffer)))
(call-process "ps2pdf14" nil nil nil
"/tmp/print.ps" "/tmp/print.pdf")
(delete-file "/tmp/print.ps")
(message "PDF saved to /tmp/print.pdf")))
You can modify the last function to take a filename as a parameter:
(defun print-to-pdf (pdf-file-name)
"Print the current file to the given file."
(interactive "FWrite PDF file: ")
(let ((ps-file-name (concat (file-name-sans-extension pdf-file-name) ".ps"))
(wbuf (generate-new-buffer "*Wrapped*"))
(sbuf (current-buffer)))
(jit-lock-fontify-now)
(save-current-buffer
(set-buffer wbuf)
(insert-buffer sbuf)
(setq fill-column 95)
(longlines-mode t)
(harden-newlines)
(message (buffer-name sbuf))
(spool-buffer-given-name (buffer-name sbuf))
(kill-buffer wbuf)
(switch-to-buffer "*PostScript*")
(write-file ps-file-name t)
(kill-buffer (current-buffer)))
(call-process "ps2pdf14" nil nil nil ps-file-name pdf-file-name)
(delete-file ps-file-name)
(message "PDF saved to %s" pdf-file-name)))
You might want to add some code that tests if the PDF file already exist though, to avoid overwriting anything.

Word wrap for Emacs print buffer to PDF

I use this function for printing a buffer's content to PDF
(from my .emacs file:)
(defun print-to-pdf ()
(interactive)
(ps-spool-buffer-with-faces)
(switch-to-buffer "*PostScript*")
(write-file "/tmp/tmp.ps")
(kill-buffer "tmp.ps")
(setq cmd (concat "ps2pdf14 /tmp/tmp.ps /home/user/" (buffer-name) ".pdf"))
(shell-command cmd)
(shell-command "rm /tmp/tmp.ps")
(message (concat "Saved to: /home/user/" (buffer-name) ".pdf"))
)
I cannot, however, find a way to enable or apply the visual-line minor mode to the PostScript buffer before it gets written to disk so to enable word wrap in the output.
The problem with getting visual line mode to be respected is that it inserts "soft newlines" (which get ignored by the PS renderer). A solution is to replace these with hard newlines. The code below does what you want, I think. Note that we call harden-newlines in a temporary buffer so as not to mess up the current document. Also, I've changed the output destination to always land in /tmp/print.pdf. It seems... unwise to overwrite documents in your /home without any sort of warning! You can always move the PDF afterwards.
Anyway, here you go. Is this what you wanted?
(defun harden-newlines ()
(interactive)
"Make all the newlines in the buffer hard."
(save-excursion
(goto-char (point-min))
(while (search-forward "\n" nil t)
(backward-char)
(put-text-property (point) (1+ (point)) 'hard t)
(forward-char))))
(defun spool-buffer-given-name (name)
(load "ps-print")
(let ((tmp ps-left-header))
(unwind-protect
(progn
(setq ps-left-header
(list (lambda () name) 'ps-header-dirpart))
(ps-spool-buffer-with-faces))
(setf ps-left-header tmp))))
(defun print-to-pdf ()
"Print the current file to /tmp/print.pdf"
(interactive)
(let ((wbuf (generate-new-buffer "*Wrapped*"))
(sbuf (current-buffer)))
(jit-lock-fontify-now)
(save-current-buffer
(set-buffer wbuf)
(insert-buffer sbuf)
(longlines-mode t)
(harden-newlines)
(spool-buffer-given-name (buffer-name sbuf))
(kill-buffer wbuf)
(switch-to-buffer "*PostScript*")
(write-file "/tmp/print.ps")
(kill-buffer (current-buffer)))
(call-process "ps2pdf14" nil nil nil
"/tmp/print.ps" "/tmp/print.pdf")
(delete-file "/tmp/print.ps")
(message "PDF saved to /tmp/print.pdf")))

In emacs, can I set up the *Messages* buffer so that it tails?

Basically I want the *Messages* buffer to always scroll to the bottom when a new message arrives.
Can I do that?
I found auto-revert-tail-mode but that works for buffers that are visiting files.
When I tried it in the Messages buffer, it popped an error:
auto-revert-tail-mode: This buffer is not visiting a file
For multiple frames you probably want:
(defadvice message (after message-tail activate)
"goto point max after a message"
(with-current-buffer "*Messages*"
(goto-char (point-max))
(walk-windows (lambda (window)
(if (string-equal (buffer-name (window-buffer window)) "*Messages*")
(set-window-point window (point-max))))
nil
t)))
Just put point at the end of the buffer M->. If you don't manually move it it will stay there -- IOW, you will always see the tail.
This code seems a bit overkill, but a the simple (goto-char (point-max)) wasn't working for me:
(defadvice message (after message-tail activate)
"goto point max after a message"
(with-current-buffer "*Messages*"
(goto-char (point-max))
(let ((windows (get-buffer-window-list (current-buffer) nil t)))
(while windows
(set-window-point (car windows) (point-max))
(setq windows (cdr windows))))))
Here's an implementation that uses the new advice style.
(defun message-buffer-goto-end-of-buffer (&rest args)
(let* ((win (get-buffer-window "*Messages*"))
(buf (and win (window-buffer win))))
(and win (not (equal (current-buffer) buf))
(set-window-point
win (with-current-buffer buf (point-max))))))
(advice-add 'message :after 'message-buffer-goto-end-of-buffer)
i run 23.3 and there were still way too many occasions where the built-in 'solution' and the orginal defadvice on the message function just didn't cut it, so i wrapped that code in a list / toggle / timer set up and it's working beautifully - no more frustration when debugging!
it's generic, so works on any buffer, although i only really use it for..
(toggle-buffer-tail "*Messages*" "on")
..hope it's useful to someone.
;alist of 'buffer-name / timer' items
(defvar buffer-tail-alist nil)
(defun buffer-tail (name)
"follow buffer tails"
(cond ((or (equal (buffer-name (current-buffer)) name)
(string-match "^ \\*Minibuf.*?\\*$" (buffer-name (current-buffer)))))
((get-buffer name)
(with-current-buffer (get-buffer name)
(goto-char (point-max))
(let ((windows (get-buffer-window-list (current-buffer) nil t)))
(while windows (set-window-point (car windows) (point-max))
(with-selected-window (car windows) (recenter -3)) (setq windows (cdr windows))))))))
(defun toggle-buffer-tail (name &optional force)
"toggle tailing of buffer NAME. when called non-interactively, a FORCE arg of 'on' or 'off' can be used to to ensure a given state for buffer NAME"
(interactive (list (cond ((if name name) (read-from-minibuffer
(concat "buffer name to tail"
(if buffer-tail-alist (concat " (" (caar buffer-tail-alist) ")") "") ": ")
(if buffer-tail-alist (caar buffer-tail-alist)) nil nil
(mapcar '(lambda (x) (car x)) buffer-tail-alist)
(if buffer-tail-alist (caar buffer-tail-alist)))) nil)))
(let ((toggle (cond (force force) ((assoc name buffer-tail-alist) "off") (t "on")) ))
(if (not (or (equal toggle "on") (equal toggle "off")))
(error "invalid 'force' arg. required 'on'/'off'")
(progn
(while (assoc name buffer-tail-alist)
(cancel-timer (cdr (assoc name buffer-tail-alist)))
(setq buffer-tail-alist (remove* name buffer-tail-alist :key 'car :test 'equal)))
(if (equal toggle "on")
(add-to-list 'buffer-tail-alist (cons name (run-at-time t 1 'buffer-tail name))))
(message "toggled 'tail buffer' for '%s' %s" name toggle)))))
edit: changed functionality to display tail at the bottom of the window
Here's an amendment over Peter's / Trey's solutions
(defun modi/messages-auto-tail (&rest _)
"Make *Messages* buffer auto-scroll to the end after each message."
(let* ((buf-name "*Messages*")
;; Create *Messages* buffer if it does not exist
(buf (get-buffer-create buf-name)))
;; Activate this advice only if the point is _not_ in the *Messages* buffer
;; to begin with. This condition is required; otherwise you will not be
;; able to use `isearch' and other stuff within the *Messages* buffer as
;; the point will keep moving to the end of buffer :P
(when (not (string= buf-name (buffer-name)))
;; Go to the end of buffer in all *Messages* buffer windows that are
;; *live* (`get-buffer-window-list' returns a list of only live windows).
(dolist (win (get-buffer-window-list buf-name nil :all-frames))
(with-selected-window win
(goto-char (point-max))))
;; Go to the end of the *Messages* buffer even if it is not in one of
;; the live windows.
(with-current-buffer buf
(goto-char (point-max))))))
(advice-add 'message :after #'modi/messages-auto-tail)