How to define an alternate command for emacs eshell - emacs

I'm trying to start using eshell in place of bash within emacs, but I rely heavily on bash functions that I have written over the years. I'd like to configure eshell to invoke bash whenever a "command not found" condition occurs, in case the command in question is implemented as a bash function.
There is a variable tantalizingly named eshell-alternate-command-hook that sounds like it is made to order, but my lack of elisp skill is interfering with my success I think.
This is my best effort:
(add-hook 'eshell-alternate-command-hook 'invoke-bash t t)
(defun invoke-bash (command args)
(throw 'eshell-replace-command
(list "bash -c" command args)))
But when I test it, it doesn't work:
c:/temp $ lsd
Wrong number of arguments: (lambda (command args) (throw (quote eshell-replace-command) (list "bash -c" command args))), 1
c:/temp $

This is what I eventually came up with:
(defun invoke-bash (command)
(progn
(setq invoke-bash-cmd (concat "bash -c \"" command " " (mapconcat 'identity eshell-last-arguments " ") "\""))
(message invoke-bash-cmd)
(throw 'eshell-replace-command
(eshell-parse-command invoke-bash-cmd))))

I'm not eshell guru, but in the place where this hook is used, I see that it receives only one argument - command, that you trying to execute, so your code could look like
(add-hook 'eshell-alternate-command-hook 'invoke-bash)
(defun invoke-bash (command)
(throw 'eshell-replace-command
(list "bash -c" command)))
but it doesn't work, because you need to return elisp function, not name of command (according to documentation). If you want to run bash, then you need to return string with full path to it, but I hadn't found how to pass additional arguments to bash. Maybe you can find more in corresponding section on Emacs Wiki?

Related

Emacs: run shell command with regex and Lisp function to start gud

I am tring to write some Emacs Lisp function to be able to simply compile and debug Java classes within Spacemacs using its Java layer (don't want to go into complex packages like jdee). The compile function is defined beblow and works as expected
(defun my-java-compile (command)
(interactive (list (read-string
"Command: "
(concat
"javac"
" -d "
java-dest-path
" -cp "
java-class-path
" "
(file-name-nondirectory buffer-file-name)))))
(unless (file-exists-p java-dest-path)
(make-directory java-dest-path))
(compilation-start command nil)
)
The java-dest-path and java-class-path are local variables set in the .dir-locals.el in the root directory of the project.
The debug function is defined as:
;; get fully qualified class name
(defun get-fqcn ()
(setq get-package-name-command
(concat
"gsed -n 's/^package\s\+\([^;\s]\+\);\s*$/\1/p' "
buffer-file-name))
(setq fqpn (shell-command-to-string get-package-name-command))
(if (equal "" fqpn) (file-name-base buffer-file-name)
(concat fqpn "." (file-name-base buffer-file-name)))
)
(defun my-jdb (command)
(interactive (list (read-string
"Command: "
(concat
"jdb"
" -classpath "
java-class-path
" "
(get-fqcn)))))
(helm-M-x nil jdb command)
)
I am still trying to make it work. Here are the two issues:
when running shell-command-to-string function, the gsed -n
's/^package\s\+\([^;\s]\+\);\s*$/\1/p' java_file command returns an empty
string "", while it returns the fully qualified package name as
expected when running in a terminal. If I change it to gsed -n '/^package/p', the emacs function return the package line OK. So
it seems shell-command-to-string could not handle the regular
expression in gsed. Any work around?
I could not find the function to trigger the gud or jdb . What would be the gud equivalent of compilation-start function?
The problem is that you want to include backslashes in your regexp, but backslashes are already used as escape characters in Emacs Lisp strings. Let's try displaying the string in the echo area:
(message "%s" "gsed -n 's/^package\s\+\([^;\s]\+\);\s*$/\1/p' ")
This displays:
gsed -n 's/^package +([^; ]+); *$/^A/p'
So as you can see, the backslashes were "eaten" by the Emacs Lisp parser. We need to double the backslashes in order for them to appear literally in the string we send to gsed:
"gsed -n 's/^package\\s\\+\\([^;\\s]\\+\\);\\s*$/\\1/p' "
Alternatively, implement the search in Emacs Lisp:
(save-excursion
(goto-char (point-min))
(search-forward-regexp "^package[[:blank:]]+\\([^;[:blank:]]+\\);")
(match-string 1))
The function for running jdb inside gud is jdb:
(jdb command)

Launching shell command in Eshell without prompt interfence

I'm trying to customize my eshell to intercept python to do two things:
Open a new window and run python if no arguments are given (e.g., $ python)
Run the command "per usual" if arguments are given (e.g., $ python foobar.py)
So far I have something like this
(defun eshell/python (&rest cmd-args)
(if (not cmd-args)
(progn (run-python "python")
(select-window (split-window-below))
(switch-to-buffer "*Python*")
(balance-windows)
nil)
(message "Use '*python' to call python directly")))
I've tried replacing (message ...) with a few different things:
Based on the ouput of eshell-parse-command "python test.py" I tried
(progn (eshell-trap-errors
(eshell-named-command "python" cmd-args)))
but it hits a recursion limit.
Since *python test.py does what I want, I then tried
(progn (eshell-trap-errors
(eshell-named-command (concat "*" "python") cmd-args)))
but that puts the python process in the background and interrupts stdout with the output of my eshell-prompt-function.
Finally, I've fiddled with shell-command but I can't get it to write to the eshell buffer. In particular,
(progn (eshell-trap-errors
(shell-command (mapconcat (lambda(x) x) cmd-args " ")
(get-buffer "*eshell*") (get-buffer "*eshell*"))))
gives me a Text is read only message and moves point to start of the eshell buffer.
Is what I'm looking for possible?
Edit 1
Running without eshell/python defined, I've instead tried to avoid alias problems:
(defun eshell/gvr (&rest cmd-args)
(if (not cmd-args)
(progn (run-python "python")
(select-window (split-window-below))
(switch-to-buffer "*Python*")
(balance-windows)
nil)
(progn (eshell-trap-errors
(eshell-named-command "python" cmd-args)))))
If test.py is
print "Hello World"
x = raw_input("What should I repeat? ")
print x
running gvr test.py in eshell fails when I reply to the prompt because eshell tries to execute the input instead of handing it to python, but running python test.py goes off without a hitch.
How can I get run my own subprocesses in eshell the same way that they happen by default?

One-shot command to knit and latexmk under Emacs + AUCtex

I want to knit AND latexmk Knitr documents using one AUCtex command.
I don't know how to code in lisp, and web search didn't turn up anything like this.
I have something close. The extension of the file needs to be changed for latexmk.
Any help will be appreciated.
Following line is for my .emacs file.
(add-hook 'LaTeX-mode-hook (lambda () (push '
("KnitrLaTeX" "Rscript -e \"library(knitr)\; knit('%s')\" && latexmk -pdf %s"
TeX-run-TeX nil t :help "Run knitr and latexmk on file")
TeX-command-list)))
When I run C-c C-c (KnitrLaTeX), emacs runs the following command:
Running `KnitrLaTeX' on `slides.Rnw' with ``Rscript -e "library(knitr); knit('slides.Rnw')" && latexmk -pdf slides.Rnw''
Which is wrong. It should read "... && latexmk -pdf slides.tex"
Thanks in advance.
It appears that you are having trouble with how the second usage of %s is being interpreted at the tail end of your compile command -- i.e., you want the second usage of %s to mean slides.tex instead of slides.Rnw.
Although I am not familiar with knit, I am familiar with creating custom variables for use with AUCTeX. Set forth below are some examples of how to create custom variables and add them to the TeX-expand-list.
Rather than of using %s for a second time (i.e., at the tail end of your compilation command), perhaps consider using %(tex-file-name) instead. This assumes that your *.tex file is open in the buffer with focus when you begin your compilation command -- i.e., the full file name will be inserted into your compilation command.
If you have a file with a different extension that is open in the buffer with focus when you run your compilation command, and if you want the base name to be the same (but with a different extension), then you would do something similar to the example of %(pdf-file-name) -- i.e., remove whatever extension is there and replace it with the new one.
(eval-after-load "tex" '(progn
(add-to-list 'TeX-expand-list '("%(tex-file-name)" (lambda ()
(concat "\"" (buffer-file-name) "\""))))
(add-to-list 'TeX-expand-list '("%(pdf-file-name)" (lambda ()
(concat
"\"" (car (split-string (buffer-file-name) "\\.tex"))
".pdf" "\""))))
(add-to-list 'TeX-expand-list '("%(line-number)" (lambda ()
(format "%d" (line-number-at-pos))))) ))

Update multi-term buffer name based on PWD

If I use konsole or other terminal, the terminal tag name can change based on PWD. But in multi-term, the buffer name is *terminal<number>*. This is not very nice. Because when I switch between them, the name is not very informative. So I want to rename it based on PWD.
I find that the Enter key is bind to term-send-raw, so I write a function
(defadvice term-send-raw (around rename-term-name activate)
(progn
(rename-buffer
(concat "⇒ "
(shell-command-to-string "pwd | xargs basename | tr -d '\n'")
(format-time-string " [%M ∞ %S]")))
ad-do-it))
But the problem is pwd command return the PWD of the terminal buffer, while it is not the PWD of the SHELL in that terminal.
The PWD of the terminal buffer is set by defcustom multi-term-default-dir. And it does not change when the PWD change in the SHELL.
(defcustom multi-term-default-dir "~/"
"The default directory for terms if current directory doesn't exist."
:type 'string
:group 'multi-term)
How can I get the PWD of the SHELL in the terminal?
Regards.
AFAIK there is no easy way to retrieve information from a running process.
But if you want to get the current directory you could:
ask the shell to print it
parse and trace the command-line for functions like cd, pushd, popd…
poll /proc/PID/cwd
The first method is described in the header of term.el (M-xfind-libraryRETtermRET).
And now, thank you for your question, you gave me the opportunity to do this:
(defadvice term-send-input (after update-current-directory)
(let* ((pid (process-id (get-buffer-process (current-buffer))))
(cwd (file-truename (format "/proc/%d/cwd" pid))))
(cd cwd)))
(ad-activate 'term-send-input)
It's a naive implementation of the third method and it doesn't work if the user uses su or ssh. However, I don't know if it's possible withouth using the first or the second method.
In your case, you can just replace the cd command with whatever you want.
Building off of Daimrod's answer for polling /proc/PID/cwd, I found a way get around the problem that Reed pointed out where the advice doesn't pick up the updated CWD immediately and you have to hit Enter twice.
If you move the CWD update code to its own function and use run-at-time to call it from the advice at a later time, it will pick up the updated CWD correctly. Unfortunately I don't know enough about Emacs' scheduling to explain why this works (any enlightenment would be appreciated).
Here's my code based on Daimrod's. Note I advised term-send-input for line-mode and term-send-return for char-mode. I tested this using multi-term on Emacs 24.3.1:
(defadvice term-send-input (after update-current-directory)
(run-at-time "0.1 sec" nil 'term-update-dir)
)
(ad-activate 'term-send-input)
(defadvice term-send-return (after update-current-directory)
(run-at-time "0.1 sec" nil 'term-update-dir)
)
(ad-activate 'term-send-return)
(defun term-update-dir ()
(let* ((pid (process-id (get-buffer-process (current-buffer))))
(cwd (file-truename (format "/proc/%d/cwd" pid))))
(unless (equal (file-name-as-directory cwd) default-directory)
(message (concat "Switching dir to " cwd))
(cd cwd)))
)
Most terminals get their window name from the command echo -en. In zsh you can put this in your ~/.zshenv
precmd() { echo -en "\e]0;`basename ${PWD}`\a" }
and that will get the basename of your PWD environment variable. Ideally multi-term would do something similar and put it in multi-term-buffer-name, which is the variable which holds its buffer name.
Yes, this is not a complete solution. I'm hoping for one too!
Try this:
(defun open-or-jump-to-multi-term ()
(interactive)
(if (string-prefix-p "*terminal<" (buffer-name))
(delete-window)
(progn
(setq bufname (concat "*terminal<" (directory-file-name (file-name-directory (buffer-file-name))) ">"))
(if (get-buffer-process bufname)
(switch-to-buffer-other-window bufname)
(progn
(split-window-right)
(other-window 1)
(multi-term)
(rename-buffer bufname)
)
)))
)
(global-set-key (kbd "C-`") 'open-or-jump-to-multi-term)

how to set name for an interactive buffer in elisp

I have the following function defined in emacs -
(defun web2py-server ()
(interactive)
(shell-command "cd /opt/web2py; python /opt/web2py/web2py.py &"))
The above creates a new buffer in emacs, how do set a name "abc" for the window.
Thanks,
Murtaza
Use shell-command's second parameter, OUPTUT-BUFFER, for that:
(defun web2py-server ()
(interactive)
(shell-command "cd /opt/web2py; python /opt/web2py/web2py.py &"
(get-buffer-create "abc")))
You can do it interactively by calling M-x rename-buffer, or you can add the name to your function:
(defun web2py-server ()
(interactive)
(shell-command "cd /opt/web2py; python /opt/web2py/web2py.py &")
(rename-buffer "abc"))
Edit:
If you want an ongoing, interactive process, it might be better to use start-process instead of shell-command. This allows you to designate the output buffer when you start the process. Otherwise, Moritz' answer looks better than my original. If you're interested in start-process, you could start by replacing your shell-command line with the following:
(let ((default-directory "/opt/web2py"))
(start-process "my-server" "abc" "python" "/opt/web2py/web2py.py")