Elisp interactive function with input history - emacs

there is a bunch of interactive functions which take string input as an argument:
(defun zb/run-cmd-X (arg1 argN)
(interactive "Marg1: Marg2: ")
;;; some logic
How to make each of such functions zb/run-cmd-1..zb/run-cmd-N have own independent history of input arguments arg1...argN? And it would be perfect if this history was persistent between Emacs launches (ideally somewhere in an external file; for sync).
Is there any ready solution for this?
Thanks

Basically you want to read the documentation for read-from-minibuffer and completing-read regarding the HIST argument which each of those functions accepts. There are other functions with history support of course, but these two are the standard/basic options.
Persistence is provided for by the savehist library, which writes to the file in savehist-file (which by default is ~/.emacs.d/history, but the old ~/.emacs-history will be used instead if that file exists -- in which case you might want to rename it to the modern preferred path).
Here's an example:
(defvar my-ssh-history nil)
(eval-after-load "savehist"
'(add-to-list 'savehist-additional-variables 'my-ssh-history))
(defun my-ssh (args)
"Connect to a remote host by SSH."
(interactive
(list (read-from-minibuffer "ssh " nil nil nil 'my-ssh-history)))
(let* ((switches (split-string-and-unquote args))
(name (concat "ssh " args))
(termbuf (apply 'make-term name "ssh" nil switches)))
(set-buffer termbuf)
(term-mode)
(term-char-mode)
(switch-to-buffer termbuf)))
(savehist-mode 1)

Related

Racket-Mode: Can I evaluate a single form within a given namespace at the REPL?

I'm working at the Racket REPL via racket-mode in Emacs, writing code in multiple modules.
Is there a way to execute a single form from a module I'm not currently 'in', in the context of its own module?
For instance:
web.rkt
#lang racket
(require "view.rkt")
(define (display-default-view)
(display (default-view)))
view.rkt
#lang racket
(provide default-view)
(define default-text "Hello")
(define (default-view)
(string-append default-text " world"))
If I call racket-run from web.rkt I get a prompt saying web.rkt>. If I then run (display-default-view) I get "Hello world".
If I then visit view.rkt and change the default-text definition to:
(define default-text "Hi")
and re-evaluate the default-text definition, it evaluates fine, and my prompt still says web.rkt>.
When I enter default-text at the REPL I get "Hi". But when I run (display-default-view) I still get "Hello world". I'm presuming this is because all I've done is define a new default-text in web.rkt.
I'd expect to see output change to "Hi world" --- i.e. the behaviour of the view.rkt module to be updated. Just like I'd see if default-text lived in the web.rkt module.
The idea of dynamically re-evaluating single forms at the repl to change program behaviour is terrific, but it seems to not quite work here.
Is there a way to get this behaving as I would expect in racket-mode? Or if not, a mechanism to just enter a module, without running it, so that I can build something myself to do an enter-execute-exit dance?
Updated, simpler answer:
We can evaluate forms in the REPL in the current file's namespace by entering that namespace in the REPL, evaluating these forms, and then re-entering our original namespace. The easiest way to do this seems to be wrapping these forms with functions to enter the current file's namespace (before) and re-entering the original namespace (after) and then sending all of this into the existing Racket-mode code for evaluating forms in the REPL.
We can do this by building a string of our wrapped commands, writing it to a temporary buffer, marking the whole buffer as our region, and then sending it to racket-send-region.
(defun my-racket-current-namespace-wrapped-commands (buffer-file-string commands)
"generate string containing commands wrapped with Racket functions to enter
the current-namespace and then exit it upon finishing"
(concat "(require (only-in racket/enter enter!))"
"(enter! (file "
buffer-file-string
"))"
commands
"(enter! #f)"))
(defun my-racket--send-wrapped-current-namespace (commands)
"sends wrapped form of commands to racket-send-region function via a temporary buffer"
(let ((buffer-file-string (prin1-to-string buffer-file-name)))
(with-temp-buffer
(insert
(my-racket-current-namespace-wrapped-commands buffer-file-string commands))
(mark-whole-buffer)
(racket-send-region (point-min) (point-max)))))
(defun my-racket-send-region-current-namespace (start end)
"send region to REPL in current namespace"
(interactive "r")
(unless (region-active-p)
(user-error "No region"))
(let ((commands (buffer-substring (region-beginning) (region-end))))
(my-racket--send-wrapped-current-namespace commands)))
(defun my-racket-send-last-sexp-current-namespace ()
"send last sexp to REPL in current namespace"
(interactive)
(let ((commands (buffer-substring (my-racket--repl-last-sexp-start)
(point))))
(my-racket--send-wrapped-current-namespace commands)))
(defun my-racket--repl-last-sexp-start ()
"get start point of last-sexp
permanent (and slightly simplified) copy of racket mode's last-sexp-start private function"
(save-excursion
(progn
(backward-sexp)
(if (save-match-data (looking-at "#;"))
(+ (point) 2)
(point)))))
These functions should mostly be version agnostic - they only depend on racket-send-buffer (which seems likely to remain in future versions).
Edit 1: (Note - this does not seem to work as is for newer versions of Racket-mode. This worked as of the April 01, 2018 release, but newer versions seem to have refactored some of the internals this relied on. In almost all cases, the code above is preferable.)
Sorry, I believe that I originally misunderstood the question. It looks like you mean executing the command straight from view.rkt without having to manually change the namespace in the REPL. I didn't see any built-in functionally in racket-mode that does this, but it's not too hard to write an Elisp wrapper around this process. The following imports in enter!, switches to the current buffer's file's namespace, sends the code in the region, and then switches back to the original namespace. The code used is very similar to what racket-mode uses for racket-send-region and racket-send-last-sexp.
(defun my-racket-send-region-current-namespace (start end)
"Send the current region to the Racket REPL as that namespace"
(interactive "r")
(when (and start end)
(racket-repl t)
(racket--repl-forget-errors)
(let ((proc (racket--get-repl-buffer-process)))
(with-racket-repl-buffer
(save-excursion
(goto-char (process-mark proc))
(insert ?\n)
(set-marker (process-mark proc) (point))))
(comint-send-string proc "(require (only-in racket/enter enter!))")
(comint-send-string proc
(concat "(enter! (file "
(prin1-to-string buffer-file-name)
"))"))
(comint-send-string proc "\n"))
(racket--repl-show-and-move-to-end)
(racket--send-region-to-repl start end)
(let ((proc (racket--get-repl-buffer-process)))
(with-racket-repl-buffer
(save-excursion
(goto-char (process-mark proc))
(insert ?\n)
(set-marker (process-mark proc) (point))))
(comint-send-string proc "(enter! #f)")
(comint-send-string proc "\n"))))
(defun my-racket-send-last-sexp-current-namespace ()
(interactive)
(my-racket-send-region-current-namespace
(save-excursion
(backward-sexp)
(if (save-match-data (looking-at "#;"))
(+ (point) 2)
(point)))
(point)))
Note that if you're using this frequently, this function could probably use more error checking (e.g. the import of require/enter will clobber any previous definition of enter!).
I've also kept the original text below about how to manually switch namespaces in the REPL, in case it helps.
You can use the function enter! in the racket/enter module to switch namespaces to modify definitions in the namespace of the other file.
After calling racket-run in web.rkt, you could do the following in the REPL:
(display-default-view) ;; output is "Hello world"
(require racket/enter)
(enter! "view.rkt") ;; change namespace to view.rkt
(define default-text "Hi")
(enter! #f) ;; return to original namespace
(display-default-view) ;; output is "Hi world"
See the Racket documentation for more details on interactive module loading.

Emacs AucTeX; How to set C-c C-c default command?

I have set this in my .emacs file:
(add-hook 'TeX-mode-hook
(lambda ()
(setq TeX-command-default "LaTeX"))
(add-hook 'LaTeX-mode-hook
(lambda ()
(setq TeX-command-default "LaTeX"))
I see that C-c C-c is bound to TeX-command-master, which calls TeX-command-query. But since my (TeX-master-file) is "<none>", I expect the default command to be called, but keeps wanting to invoke "View" instead of "LaTeX".
If you check the source for TeX-command-query you'll find that it checks the modification date of the tex (lines 4-9) and bbl (lines 10-19) files involved in your document. Unless those files are more recent than the output file and there is no known next command to be performed (lines 20-22) it will use the "View" command as default (line 23).
This behaviour is of course sensible because normally you don't want to recompile unless there are changes (modified tex files). Apart from "patching" the command [posted below, would not really recommend to use because it will not receive automatic updates ;-) ] there isn't really anything you can do.
If you decide to use the patched command, just put is somewhere in your init file after the original command has been loaded. You could for example wrap it into (replace ;; BODY by code)
(eval-after-load "tex-buf"
'(progn
;; BODY
))
Here comes the patched command:
(defun TeX-command-query (name)
"Query the user for what TeX command to use."
(let* ((default
(cond ((if (string-equal name TeX-region)
(TeX-check-files (concat name "." (TeX-output-extension))
(list name)
TeX-file-extensions)
(TeX-save-document (TeX-master-file)))
TeX-command-default)
((and (memq major-mode '(doctex-mode latex-mode))
;; Want to know if bib file is newer than .bbl
;; We don't care whether the bib files are open in emacs
(TeX-check-files (concat name ".bbl")
(mapcar 'car
(LaTeX-bibliography-list))
(append BibTeX-file-extensions
TeX-Biber-file-extensions)))
;; We should check for bst files here as well.
(if LaTeX-using-Biber TeX-command-Biber TeX-command-BibTeX))
((TeX-process-get-variable name
'TeX-command-next
;; HERE COMES THE PATCH
;; was TeX-command-View
TeX-command-default))
;; END OF PATCH
(TeX-command-Show)))
(completion-ignore-case t)
(answer (or TeX-command-force
(completing-read
(concat "Command: (default " default ") ")
(TeX-mode-specific-command-list major-mode) nil t
nil 'TeX-command-history))))
;; If the answer is "latex" it will not be expanded to "LaTeX"
(setq answer (car-safe (TeX-assoc answer TeX-command-list)))
(if (and answer
(not (string-equal answer "")))
answer
default)))

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 can I set environment variables to a buffer-local scope in emacs

In Emacs, I want to vary the values of my environment variables in different buffers.
My emacs environment depends on environment variables (flymake, compile etc), however I want to be able to be able to have multiple projects open at once in one emacs session but these projects might have conflicting environments.
For example something like different INCLUDE_PATH environment variables for flymake.
You can do this by making process-environment buffer-local:
(defun setup-some-mode-env ()
(make-local-variable 'process-environment)
;; inspect buffer-file-name and add stuff to process-environment as necessary
...)
(add-hook 'some-major-mode 'setup-some-mode-env)
A more elaborate example is this code that imports the Guile environment setup created by an external script. The script is designed to be "sourced" in the shell, but here its result gets imported into a single Emacs buffer:
(defun my-guile-setup ()
(make-local-variable 'process-environment)
(with-temp-buffer
(call-process "bash" nil t nil "-c"
"source ~/work/guileenv; env | egrep 'GUILE|LD_LIBRARY_PATH'")
(goto-char (point-min))
(while (not (eobp))
(setq process-environment
(cons (buffer-substring (point) (line-end-position))
process-environment))
(forward-line 1))))
(add-hook 'guile-hook 'my-guile-setup)
I put the following in .dir-locals.el at the root of the tree where I want to define some environment vars:
;; variables local to this directory and its children
((nil . ((eval . (setenv "SOME_VARIABLE" "TRUE")))))
This will warn the first time you open a file in that directory tree. After you accept, the given environment var will be defined for each buffer you open there.
One solution would be to temporary change the environment when you spawn an external command. The command will inherit the current environment. Remember that Emacs is a single-treaded application, so we don't have to worry about race conditions etc.
You can pick one of two ways of doing this:
1) Write you own functions like my-compile that changes the environment temporarily and calls the normal compile command.
2) Modify the low-level process functions and ensure that they modify the environment accordingly. Typically, you can do this with defadvice.
It may be possible to use dynamic binding for those variables.
Dynamic binding and Dynamic scoping are a bit hard to explain, for explanations see http://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding and
http://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping.
Here is an example where I create a local process-environment without necessarily making it buffer-local. The advantage is that the settings only affect the running process and they disappear once the process ends. In this example, I set the timzezone environmental variable and call the function with (funcall my-start-process ....) and everything else is just like start-process in terms of arguments and so forth.
(let* ((my-start-process
(lambda (name buffer program &rest program-args)
(unless (fboundp 'make-process)
(error "Emacs was compiled without subprocess support"))
(let* (
(temp (mapcar 'concat process-environment))
(newenv
(cond
((equal (car (cdr (current-time-zone))) "PDT")
(setenv-internal temp "TZ" "UTC+7" t))
((equal (car (cdr (current-time-zone))) "PST")
(setenv-internal temp "TZ" "UTC+8" t))))
(process-environment (or newenv temp)))
(apply #'make-process
(append (list :name name :buffer buffer)
(when program
(list :command (cons program program-args))))))))
(proc (funcall my-start-process ...))))

Passing Emacs variables to minibuffer shell commands

I can run a shell command quickly by hitting M-!. One thing I'd like to do is perform shell quick operations on the current file. An example would be checking the file out through perforce:
M-! p4 edit buffer-file-name RET
(Yes there are perforce integrations, but I'm more interested in the minishell/variable problem rather than a specific workflow)
Of course, the buffer-file-name variable is not evaluated before the command is sent to the shell.
Is there an easy on-the-fly way to do this? Or will I have to roll a custom elisp function?
It seems current Emacs has something built-in to achieve the desired result, after M-! (shell-command) press <down>, you will get the file name you are currently visiting on the prompt. Now you can edit it to add the command you want to run on it.
In dired-mode it will give you the file your cursor is currently on.
Indeed using C-u M-: is almost right. I'm not so sure about using shell-quote-argument in eval-to-shell-argument since it only works on strings making it impossible to use eval-to-shell-argument to insert a number or a symbol. You could try something like:
(defun sm-minibuffer-insert-val (exp)
(interactive
(list (let ((enable-recursive-minibuffers t))
(read-from-minibuffer "Insert: "
nil read-expression-map t
'read-expression-history))))
(let ((val (with-selected-window (minibuffer-selected-window)
(eval exp)))
(standard-output (current-buffer)))
(prin1 val)))
and then bind this function in your minibuffer with (define-key minibuffer-local-map [?\M-:] 'sm-minibuffer-insert-val).
Of course, if the only thing you ever want to insert is the buffer-file-name, then your execute-shell-command-on-buffer is simpler.
I did roll my own elisp function, and it looks like this:
(defun execute-shell-command-on-buffer (shell-command-text)
(interactive "MShell command:")
(shell-command (format shell-command-text (shell-quote-argument buffer-file-name)))
)
https://gist.github.com/2367513
I bound it to M-", so now my example can be completed with:
M-"p4 edit %sRET
I won't accept this as the answer, because I did ask for solutions that don't require a function.
You can use C-u M-: (eval-expression with a universal prefix argument) to evaluate any Lisp expression and insert its value at point in the current buffer (including minibuffers, as long as you have enable-recursive-minibuffers set to a non-nil value).
In your example: C-u M-: buffer-file-name RET.
Note that the result of the expression is printed in Lisp form: that is, quoted in such a way that a subsequent call to read would construct an equal Lisp value. For strings, this means enclosing in double quotes, which will probably be interpreted as you expect by the inferior shell. However, you may run into problems with strings that contain special characters, which need different escaping by Elisp and the shell.
The more correct way uses shell-quote-argument, as in phils' solution. Here's a quick defun that reads a Lisp expression and inserts its value at point as a properly quoted shell word:
(defun eval-to-shell-argument (form)
(interactive "XEval: ")
(insert (shell-quote-argument form)))
The read-and-evaluate step happens automatically by using an "X" as the argument to interactive.
Edited to add: As #tenpn notes, the above solution doesn't work for inserting buffer-local variables like buffer-file-name in a minibuffer like the one M-! pops up (more precisely, it inserts the buffer-local value of the minibuffer, which is unlikely to be useful). Here is a revised version which seems to work. If the minibuffer is active, it makes the buffer of the previously-selected window temporarily active while reading and evaluating an expression.
Final edit: From #Stefan's answer I see that I should have used (minibuffer-selected-window) to find the previously-selected window. I've also added a (format "%s" ..) to allow inserting non-string values, while still quoting special characters in strings. Here's the final version:
(defun eval-to-shell-argument ()
(interactive)
(let* ((buffer
(if (minibufferp)
(window-buffer (minibuffer-selected-window))
(current-buffer)))
(result
(with-current-buffer buffer
(eval-minibuffer "Eval: "))))
(insert (shell-quote-argument (format "%s" result)))))
You can't do that with M-!, but you can evaluate arbitrary elisp from the minibuffer, so writing a function isn't strictly necessary:
M-: (shell-command (format "p4 edit %s" (shell-quote-argument buffer-file-name))) RET
In this case however, I think eshell is what you want to use:
M-x eshell-command RET p4 edit (eval buffer-file-name) RET
Edit: Except unfortunately that doesn't work, as the *eshell cmd* buffer is selected when that is evaluated. One solution would be:
M-x eshell-command RET p4 edit (eval buffer-file-name (other-buffer nil t)) RET
(Not quite as elegant, sorry.)
Everyone seems to be rolling their own version, so here's mine -- it will substitue the current filename or marked dired-files or current dired file wherever a % is in the shell command. It follows the same conventions as M-! so I bind it to that.
(defun my-shell-command (command &optional output-buffer error-buffer)
"Run a shell command with the current file (or marked dired files).
In the shell command, the file(s) will be substituted wherever a '%' is."
(interactive (list (read-from-minibuffer "Shell command: "
nil nil nil 'shell-command-history)
current-prefix-arg
shell-command-default-error-buffer))
(cond ((buffer-file-name)
(setq command (replace-regexp-in-string "%" (buffer-file-name) command nil t)))
((and (equal major-mode 'dired-mode) (save-excursion (dired-move-to-filename)))
(setq command (replace-regexp-in-string "%" (mapconcat 'identity (dired-get-marked-files) " ") command nil t))))
(shell-command command output-buffer error-buffer))