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)))
Related
As the documentation says that RET will comint-send-input anywhere in a shell mode. The issue is that if you by mistake hit enter on any line and you are not at a prompt it will execute the whole random text until the next prompt. How can I prevent this from happening? It would be nice if hitting Enter anywhere out of the prompt will send you to a new prompt at the bottom.
Something like this?
(defun my-comint-send-input-maybe ()
"Only `comint-send-input' when point is after the latest prompt.
Otherwise move to the end of the buffer."
(interactive)
(let ((proc (get-buffer-process (current-buffer))))
(if (and proc (>= (point) (marker-position (process-mark proc))))
(comint-send-input)
(goto-char (point-max)))))
(with-eval-after-load "comint"
(define-key shell-mode-map [remap comint-send-input] 'my-comint-send-input-maybe))
You could replace (goto-char (point-max)) with (comint-copy-old-input) to insert but not send the old input at the new prompt; but that's still liable to cause problems when the inserted input looks like output.
However, also note the comments and link in C-hf comint-send-input regarding comint-get-old-input -- this can be used to implement custom logic for establishing what the "old input" should be when comint-send-input is invoked with point before the process mark.
Bulletproof:
(defun comint-send-input-or-insert-previous-input ()
"Call `comint-send-input' if point is after the process output marker.
Otherwise, move point to the process mark and try to insert a previous input
from `comint-input-ring' (if any) returned by `comint-previous-input-string'
and affected by the current value of `comint-input-ring-index'.
Implementation is synthesized from and inspired by the `comint-after-pmark-p',
`comint-goto-process-mark', and `comint-copy-old-input' functions."
(interactive)
(let ((process (get-buffer-process (current-buffer))))
(if (not process)
(user-error "Current buffer has no process")
(let ((pmark (process-mark process)))
(if (<= (marker-position pmark) (point))
(comint-send-input)
(goto-char pmark)
(when (and (eolp) comint-input-ring)
(let ((input (comint-previous-input-string 0)))
(when (char-or-string-p input)
(insert input)))))))))
I dont like default ggtags interface around ggtags-find-definition/reference/file. I managed to get ggtags-find-file work with IDO, but ggtags-find-definition is a lot harder problem.
Is there some package which can do it? If not, how to make one?
NOTE: I want to be able to browse found definitions/references in fuzzy minibuffer, not whole new buffer (and window).
Normally, I would suggest ido-completing-read-plus (formerly ido-ubiquitous). It makes almost every command that uses completing-read use ido-completing-read instead. However, there is one major case where it doesn't work well: when the completion candidates are generated by a function. This is the case for ggtags-completion-table, which is how ggtags generates its completion candidates.
The solution is to define your own ggtags-completing-read-function that expands the candidates before passing to ido-completing-read. Note: the reason why ido-completing-read (and in turn ido-completing-read-plus) doesn't allow this is because it might use a ton of memory and CPU, and may freeze the UI. You will probably need to tune this to make it acceptable if you have a large number of completion candidates. Also note that most ggtags commands work with the symbol at point; it seems like you generally just give a prefix arg (C-u) to make it prompt for the symbol.
(defun jpk/ggtags-completing-read (&rest args)
(apply #'ido-completing-read
(car args)
(all-completions "" ggtags-completion-table)
(cddr args)))
(setq ggtags-completing-read-function #'jpk/ggtags-completing-read)
Assuming that you ran ggtags-find-definition and it found results and put them in the buffer *ggtags-global*, this function will extract the filenames and line numbers and let you use IDO to pick the one you want.
(defun ido-goto-grep-results (grep-buffer prompt)
(interactive)
(let (candidates result filename dirname line)
(with-current-buffer grep-buffer
(setq dirname default-directory)
(save-excursion
(save-match-data
(goto-char (point-min))
(forward-line 4)
(while (re-search-forward "^\\(.+\\):\\([0-9]+\\):" nil 'noerror)
(push (concat (match-string 1) ":" (match-string 2))
candidates)))))
(setq result (split-string (ido-completing-read prompt candidates nil t) ":"))
(setq filename (car result))
(setq line (string-to-number (cadr result)))
(find-file-other-window (expand-file-name filename dirname))
(goto-char (point-min))
(forward-line (1- line))))
(ido-goto-grep-results "*ggtags-global*" "Jump to definition: ")
This is pretty rough. You probably want to make your own find-definition command that runs ggtags-find-definition and ido-goto-grep-results at once. Somehow you need to only get the actual results and not the "Global found N definitions..." line (better regexp, narrow the ggtags buffer, or some other way).
I'm fairly new to elisp, but one thing that I really want to figure out is either how to wait for ace-jump to end before executing instructions or how get a position from ace-jump instead of moving my cursor. My goal is to be able to select a line with ace-jump, copy it, then paste it right above my current line. I started by first trying to go to a line with ace-jump then duplicate it in place, but that hasn't worked. Here is what I have for that:
(defun ace-jump-yank-line-above ()
(interactive)
(ace-jump-line-mode)
(kill-ring-save (line-beginning-position) (line-beginning-position 2) )
(yank)
)
But this gives me strange behavior
You can have a look at the source of my project lispy.el.
It's got several functions that use ace-jump-mode and do something after.
For instance lispy-ace-symbol will ace-jump to symbol and mark it.
Here's the implementation detail - the key is setting ace-jump-mode-hook:
(defun lispy--ace-do (x bnd &optional filter func no-narrow)
"Use `ace-jump-do' to X within BND when FILTER return t.
When FUNC is not nil, call it after a successful move.
When NO-NARROW is not nil, don't narrow to BND."
(require 'ace-jump-mode)
(lispy--recenter-bounds bnd)
(unless no-narrow
(narrow-to-region (car bnd) (cdr bnd)))
(when func
(setq ace-jump-mode-end-hook
(list `(lambda()
(setq ace-jump-mode-end-hook)
(,func)))))
(let ((ace-jump-mode-scope 'window)
(ace-jump-search-filter filter))
(ace-jump-do x))
(widen))
I use something similar to ace-jump rather than ace-jump itself, but something like this should work (can't be sure about the call to ace-jump-line-mode):
(defun ace-jump-yank-line-above ()
(interactive)
(let ((loc (point-at-bol))
(line nil))
(save-excursion
(ace-jump-line-mode)
(setq line (buffer-substring-no-properties
(point-at-bol) (point-at-eol)))
(goto-char (1- loc))
(if (bobp)
(insert (concat line "\n"))
(insert (concat "\n" line))))))
Okay, none of these worked for me, but I used these answers to create a script that works. Here is the code that I used:
;; The base function for the line-based ones
(defun ace-jump-end-do (dfunc afunc)
;; Save where to return to as a marker
(setq ace-jump-do-retpos (make-marker))
(set-marker ace-jump-do-retpos (point))
;; Add our during function to the hook
(setq ace-jump-mode-end-hook
(list `(lambda()
(progn
(setq ace-jump-mode-end-hook)
(,dfunc)
(goto-char ace-jump-do-retpos)
(set-marker ace-jump-do-retpos nil)
(,afunc)
))))
(ace-jump-line-mode)
)
;; Copy the line above the current line
(defun ace-jump-yank-line-above ()
(interactive)
(ace-jump-end-do
;; At the line
(lambda ()
;; Store the line in a variable
(setq line (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
)
;; Upon returning
(lambda ()
(save-excursion
(goto-char (point-at-bol))
(insert (concat line "\n"))
)
(when (bolp) (goto-char (point-at-bol 2)))
)))
Unfortunately, this resets the end hook of ace-jump every time it's called. It works for me though since I don't have anything else hooked to it. If I run into issues, I'll need to figure something else out.
ace-jump-mode is really silly... calling it just goes into some useless minor-mode where you pick the hints, but it is non-blocking: any code afterwards is executed immediately.
There is so much potential for this kind of interaction and ace-jump-mode completely wastes it away with crazy implementation. It also doesn't work at all with save-excursion and you wound need to hack around that with various hooks and state-saving variables.
I've written a new package addressing all these issues, you can find it at https://github.com/Fuco1/better-jump Hopefully people will pick it up, but it serves me well at least. Took me about 2 hours to write the basic working prototype and it already covers all the functionality of packages like ace-link, ace-window and ace-whatever-else-you-can-find (also ace-jump, obviously :))
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.
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.