Window scrolling after start-process and pop-to-buffer - emacs

I'm trying to write a function that compiles some code, and if the compilation succeeds, it spawns the process and opens its output in a new window. The compilation bit works fine, but there's something weird going on with the second part. I wrote some code that looks like this:
(defun test-function ()
(with-current-buffer (get-buffer-create "*output*")
(erase-buffer)
(start-process "echo" (current-buffer) "echo" "hi")
(pop-to-buffer (current-buffer))))
This code almost works, but when it runs, the top of the screen seems to sit right below the actual output of the program. So, once echo quits, the screen looks like
Process echo finished
And scrolling up one line gives (the expected)
blah
Process echo finished
Is there a way to get it to start at the actual top of the buffer? I've tried things like scroll-up and goto-char before and after starting the process, but they don't seem to affect anything. From some other sources, it seems like I could attach a sentinel to the process and have it scroll up when there's output, but that seems like overkill just to scroll up at the beginning.

A bit of a hack, but for me this works:
(insert "\n")
(start-process ...

It looks like there is an \n before Process built into process.c. Perhaps someone can suggest for you how to modify this after Emacs has already been built -- if not, a custom build seems like an extreme solution.
Of course, you could use elisp code to go up and delete the hard return after the fact.
Searching 5342 files for "\nprocess"
/Users/HOME/Desktop/emacs-trunk/src/process.c:
6405 tem = BVAR (current_buffer, read_only);
6406 bset_read_only (current_buffer, Qnil);
6407: insert_string ("\nProcess ");
6408 { /* FIXME: temporary kludge. */
6409 Lisp_Object tem2 = p->name; Finsert (1, &tem2); }
/Users/HOME/Desktop/emacs-trunk/lisp/man.el:
1304 (substring msg 0 eos) msg))))
1305 (goto-char (point-max))
1306: (insert (format "\nprocess %s" msg))))
1307 ))
1308 (if delete-buff
/Users/HOME/Desktop/emacs-trunk/lisp/progmodes/sql.el:
3944 (if (and (eq (current-buffer) sql-buffer)
3945 (not buffer-read-only))
3946: (insert (format "\nProcess %s %s\n" process event))
3947 (message "Process %s %s" process event)))
3948
/Users/HOME/Desktop/emacs-trunk/lisp/shell.el:
641 (when (buffer-live-p buf)
642 (with-current-buffer buf
643: (insert (format "\nProcess %s %s\n" process event))))))
644
645 ;;;###autoload
4 matches across 4 files

Fixing it with a process sentinel:
(defun recenter-temp-buffer (process event)
(pop-to-buffer (process-buffer process)) (recenter-top-bottom))
(with-output-to-temp-buffer "test-buffer"
(set-process-sentinel
(start-process "test-process" "test-buffer" "echo" "hello")
'recenter-temp-buffer))
Note that the sentinel will call (recenter-top-bottom) on every status change, which may not be what you want.

Related

Hook into man output

I want to hook into man output (from man.el) but the commands run asynchronously. Does anyone know how to set a process sentinel for the man command, or an alternative to hook the function?
For example, how can I replace the sit-for in the following snippet with a callback to run when man finishes its processing?
(defun get-some-help (cmd &optional num)
(let ((buf (man (concat (number-to-string (or num 3)) " " cmd))))
(sit-for 0.1) ;how to replace this?
(unless (buffer-live-p buf)
(message "Do something else instead"))))
(get-some-help "wait4")

Emacs: what is the conventional way of receiving output from a process?

My aim is to get the output from a process in Emacs.
For example, M-x run-python gives me a python shell *Python* that I can send python code to. If I send print "hello world" to *Python*, I hope Emacs can know the result once the execution is finished and echo it in the mini-buffer.
Is it possible to add something like a callback?
Thanks to the comments from #lawlist , I solved my problem by creating the following filter function and assigning it to the process (*MozRepl* in my case) with (set-process-filter (get-buffer-process "*MozRepl*") 'moz-controller-repl-filter)
(defun moz-controller-repl-filter (proc string)
"Filter function of *MozRepl*.
It gets the useful output of *MozRepl*, store it in `moz-controller-repl-output` and `kill-ring`"
(when (buffer-live-p (process-buffer proc))
(unless (string= string "repl> ") ; ignore empty output (page up, page down, etc)
(setq moz-controller-repl-output
(replace-regexp-in-string "\"\\(.+\\)\"\nrepl> " "\\1" string))
(kill-new moz-controller-repl-output) ; append to kill-ring
(message moz-controller-repl-output) ; show the copied content in echo area
)
(with-current-buffer (process-buffer proc)
(let ((moving (= (point) (process-mark proc))))
(save-excursion
;; Insert the text, advancing the process marker.
(goto-char (process-mark proc))
(insert string)
(set-marker (process-mark proc) (point)))
(if moving (goto-char (process-mark proc)))))))

Elisp sentinel on process waiting for input

I made a function, compiling the current latex file:
;close the *async pdflatex* window, when pdflatex finishes
(defun latex-sentinel (process event)
(message event)
(cond ((string-match-p "finished" event)
(progn
(kill-buffer "*async pdflatex*")
(message "pdflatex done")
(delete-other-windows)))))
(defun latex-compile ()
"Runs pdflatex on current file"
(interactive)
(let* ((file-name (shell-quote-argument (buffer-file-name)))
(process (start-process-shell-command
"pdflatex"
"*async pdflatex*"
(concat "pdflatex " file-name))))
(set-process-sentinel process 'latex-sentinel)
(setq new-window (split-window-below 40))
(set-window-buffer new-window "*async pdflatex*")
(other-window 1)))
(add-hook 'LaTeX-mode-hook (lambda ()
(define-key LaTeX-mode-map (kbd "<f2>") 'latex-compile)))
When there is an error, while compiling, pdflatex freezes, and I see this:
My current workflow:
Scroll up to see the error
kill-this-buffer - to kill pdflatex process
delete-window - to close *async pdflatex* window and get back to editing.
Is it possible to track, that the process has stopped and is waiting for user input? My current sentinel activates only on "finished" event. So I could automate my current workflow.
[ #Thomas: you don't need AUCTeX for that. The builtin latex-mode also provides a C-c C-c binding for that task. ]
In general, detecting that a process is "waiting for me" is difficult/impossible (at best you might monitor the process's CPU usage and if it's been 0% for a while you can decide that it's probably waiting for you, but even doing that is tricky since you need to find the proper OS-process to monitor and then use system-dependent operations to get the CPU usage). latex-modes usually "solve" this by passing \nonstopmode\input on pdflatex's command line.
I had the same difficulty. The following seems to be OK.
(defun latex-compile()
(interactive)
(save-buffer)
(set 'tex-to-pdf "pdflatex -interaction=nonstopmode")
(set 'file-name (shell-quote-argument (buffer-file-name)))
(set 'comm1 (concat tex-to-pdf " " file-name ))
(set-process-sentinel
(start-process-shell-command "latex-compile"
"*async latex-compile*"
comm1)
'tex-to-pdf-sentinel))
;;;
(defun tex-to-pdf-sentinel (process event)
(cond
( (string-match-p "finished" event)
(message "latex-compile complete"))
( (string-match-p "\\(exited\\|dumped\\)" event)
(message "error in latex-compile"))
))
I realize use of global variables with set is not advisable; this is just a minimal example.
You can set up a second sentilel when the first completes, e.g. so that a viewer opens to display the the .pdf output, or you want to ensure bibtex runs each time. This may save on repeated calls to C-c C-c in latex mode.

AUCTeX: Run Compile Command n-times

I'd like to have a function that asks for a number n and executes the default compile command n-times afterwards. That is to say unlike C-c C-c (i.e. TeX-command-master) I don't want to be asked which command to run, it should select the default compile command based on the AUCTeX settings. Naturally if any error occurs the execution should stop.
I know about TeX-texify, however, this doesn't statisfy my needs because sometimes I just want emacs to run pdflatex five times indepent of what the AUCTeX parser thinks is adequate.
Any help is much appreciated!
Edit: I have looked into this a little further and using code from the above reference I have started writing a function that does this. However, it has one major flaw. Let me first give you the code:
(defcustom TeX-MultiTeX-Command "LaTeX" "Default MultiTeX command" :type 'string :group 'TeX-command)
(defun TeX-MultiTeX (n)
"Run TeX-command n-times"
(interactive "nRun TeX/LaTeX how many times: ")
(while (> n 0)
(TeX-command TeX-MultiTeX-Command 'TeX-master-file)
(setq n (- n 1))))
As you can see, I have implemented a config variable for selecting the correct compilation command. Now let me present the problem:
The compilation of the LaTeX document takes some time, however, my function instantly calls the second (and following) executions of the compile command. Maybe someone can provide help in finding a solution that checks whether compilation has finished successfully prior to executing (TeX-command TeX-MultiTeX-Command 'TeX-master-file), then executes said function or prints some error message if compilation finished with an error.
With the help of the code of the TeX-texify function I have developed a function that does what I want, the code is given below.
I'd like to thank user4815162342; although this solution is not based on his suggestion, I think his solution might be of use for a different problem. Also I'd like to thank TN, the author of TeX-texify, I shamelessly took and adapted his code for my problem. ;)
(defcustom TeX-MultiTeX-Command "LaTeX"
"Default MultiTeX command"
:type 'string :group 'TeX-command)
(defun TeX-MultiTeX-sentinel (&optional proc sentinel)
"Non-interactive! Call the standard-sentinel of the current LaTeX-process.
If there is still something left do do start the next latex-command."
(set-buffer (process-buffer proc))
(funcall TeX-MultiTeX-sentinel proc sentinel)
(let ((case-fold-search nil))
(when (string-match "\\(finished\\|exited\\)" sentinel)
(set-buffer TeX-command-buffer)
(unless (plist-get TeX-error-report-switches (intern (TeX-master-file)))
(TeX-MultiTeX TeX-MultiTeX-num-left)))))
(defun TeX-MultiTeX (n)
"Run TeX-command n-times"
(interactive "nRun TeX/LaTeX how many times: ")
(when (or (called-interactively-p 'any)
(null (boundp 'TeX-MultiTeX-num-left)))
(setq TeX-MultiTeX-num-left n))
(if (>= TeX-MultiTeX-num-left 1)
(progn
(TeX-command TeX-MultiTeX-Command 'TeX-master-file)
(setq TeX-MultiTeX-num-left (- TeX-MultiTeX-num-left 1))
(setq proc (get-buffer-process (current-buffer)))
(setq TeX-MultiTeX-sentinel (process-sentinel proc))
(set-process-sentinel proc 'TeX-MultiTeX-sentinel))))
It seems that you need a synchronous way to run TeX-command. I haven't word with TeX-command, but if it uses the compilation API, it can be made to wait for the compilation to finish, although it's not exactly obvious how to do that. Here is an example that uses compilation-finish-functions to achieve the desired effect:
(require 'cl) ; for lexical-let
(defun compile-and-wait (compilefun)
(interactive)
(lexical-let ((done nil) finish-callback)
(setq finish-callback
;; when the compilation is done, remove the callback from
;; compilation-finish-functions and interrupt the wait
(lambda (buf msg)
(setq compilation-finish-functions
(delq finish-callback compilation-finish-functions))
(setq done t)))
(push finish-callback compilation-finish-functions)
(funcall compilefun)
(while (not done)
(sleep-for .1))))
EDIT
AUC TeX is not using compilation mode to spawn TeX, so the above cannot work. Since it's still useful for other compilation buffers, I'm leaving it in the answer. Another way to implement TeX-MultiTeX is by binding TeX-process-asynchronous to nil, which should ensure that AUC TeX waits for the command to finish.

Running command line commands in Emacs, and receiving their output

I want to run a set of command line commands in a loop within emacs, with the loop stopping when the user taps a key.
This is in order to see an ascii 'video' appear in Emacs, which stops when you press a key.
I thought it would be fun to have image-to-ascii bit of text as a comment (with the image coming from my mac's camera).
I use imagesnap to take the camera image, and jp2a to convert it to ascii. I think imagesnap is mac only. Here's the code I have so far:
(defun ascii-video-comment ()
"Makes video comment, requires imagemagick, jp2a, imagesnap"
(interactive)
(shell-command "imagesnap -q ~/Desktop/emacs-snap.jpg")
(insert (shell-command-to-string "jp2a --width=48 ~/Desktop/emacs-snap.jpg"))
(shell-command "rm ~/Desktop/emacs-snap.jpg")
)
This just takes a snap from the camera, converts to ascii, inserts it into my file, and places the cursor after.
Like I say, I would like it to keep looping, giving the appearance of a slow ascii video, until i tap a key to select the current 'frame'.
Is this even possible?
EDIT
This is my current code, which I'm fairly happy with. It loops 20 times, and you can choose the current image by cancelling (C-g). Things seem to go wrong when you do it a second time though.
(defun ascii-video-comment ()
"Makes video comment, requires imagemagick, jp2a, imagesnap"
(interactive)
(cl-loop repeat 20 do
(shell-command "imagesnap -q ~/Desktop/ascii-video-comment.jpg")
(cua-set-mark)
(insert (shell-command-to-string "jp2a --width=120 ~/Desktop/ascii-video-comment.jpg"))
(shell-command "rm ~/Desktop/ascii-video-comment.jpg")
(comment-region (mark) (point))
(cua-set-mark)
(pop-global-mark)
(sit-for 0.1)
(undo)
)
)
On your EDIT "final code": don't use interactive commands in a lisp program: you code becomes brittle and inefficient.
E.g., the doc string for shell-command (C-h f shell-command RET) explicitly states:
In Elisp, you will often be better served by calling call-process or
start-process directly, since it offers more control and does not
impose the use of a shell (with its need to quote arguments)
Also, use delete-file instead of (shell-command "rm ...").
Do not use cua-set-mark, pop-global-mark, and, especially, undo in programs.
Bind a variable instead:
(let ((beg (point)))
(call-process "jp2a" nil t t "--width=120" "~/Desktop/ascii-video-comment.jpg")
(comment-region beg (point))
(sit-for 0.1)
(delete-region beg (point)))
AFAIR, Emacs doesn't provide API to poll for pending events, so there are two options. UPD: disregard that, should've read the manual beforehand, Emacs does provide API to poll for pending events:
(defun start-printing-messages-2 ()
(interactive)
(while (not (input-pending-p))
(loop-body-function)
(redisplay 'force)))
If you want a delay between command execution, there's sit-for for you:
(defun start-printing-messages-3 ()
(interactive)
(while (sit-for 0.05)
(loop-body-function)))
If you want that delay to start counting from beginning of loop body rather than its end (if your loop body might take significant time), you need to set up full-fledged timer execution: you basically run a function in a timer and add a post-command-hook that will kill that timer:
(defvar loop-run-count 0)
(defvar loop-timer-object nil)
(defun loop-body-function ()
(setq loop-run-count (1+ loop-run-count))
(message "The function was run %s times" loop-run-count))
(defun stop-printing-messages ()
(when loop-timer-object
(cancel-timer loop-timer-object)
(remove-hook 'post-command-hook 'stop-printing-messages)))
(defun start-printing-messages ()
(interactive)
(setq loop-run-count 0)
;; post-command-hook is added via timer too, because otherwise it
;; might get called right after this function completes and this
;; would kill the timer that didn't even start yet.
;; get killed right after creation.
(run-with-timer
0.01 nil (lambda ()
(add-hook 'post-command-hook 'stop-printing-messages)))
(setq loop-timer-object
(run-with-timer nil 0.01 'loop-body-function)))
You can wait for input with while-no-input and sit-for.
For the use case you describe, sit-for is what you need.
Note that you should combine the two shell calls with && or make a shell
script.
Here is a toy command that does what you need, but only flashing random numbers.
(defun flash-me ()
"flash random number until you press a key"
(interactive)
(let ((beg (copy-marker (point)))
(end (copy-marker (point) t)))
(loop do (progn
(delete-region beg end)
(insert (shell-command-to-string "echo $RANDOM")))
while (sit-for 1))
(set-marker beg nil)
(set-marker end nil)))