emacs extension to do system call, sleep, then reload buffer - emacs

I want to write an emacs extension such that when I do M-x, b, k, e the following things occur:
a system/shell call is executed where the last argument is the full path to the file of the buffer that is currently selected.
wait for the command to terminate or sleep for 3 seconds if it isn't possible to block for the return of a system call.
reload/restore the current buffer
How is this done?

This should be close:
(defun bke ()
(interactive)
(let ((buf-name (buffer-file-name)))
(with-temp-buffer
(shell-command (concat "your-command-here " buf-name) t))
(revert-buffer t t t))

Here you go (at least two of three)
1) Shell call with last argument
(defun b ()
(interactive)
(shell-command (concat (read-string "$ ") " " buffer-file-name)))
2) I do not know, sorry.
3) Reload file.
(defun e ()
(interactive)
(revert-buffer t t t))

Other than the key binding portion, this question is the same as: elisp: call command on current file.

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 function to create new terminal

Each time I open a new terminal in Emacs "M-x term" I get the currently open one, to get around this I need to rename the buffer where the terminal is running upon and then start a new one through M-x term.
I would like to write a function which holds a global counter and uses it to start a new terminal using it to generate the buffer name; once done I could map this function to a key binding of my preference.
I am having issues in running the terminal in a new created buffer, I am not an experienced ELisp programmer and this code might look quite naive to some, nonetheless this where I am at the moment :
(defvar counter 0)
(defun mine/open-terminal ()
"Open a new terminal and rename the buffer"
(setq counter (+ counter 1))
(setq title (concat "Terminal-" (number-to-string counter)))
(setq terminal (get-buffer-create title))
That function creates a new buffer with the correct name - although it does not show up it immediately as I would like it to do, the rub is that if I add at the end of the function the line:
(term "/bin/bash")
A new buffer called terminal is created, I have the feeling I am missing a bit here, is there a way to start a new terminal giving to it a buffer name ?
Thanks a lot.
The easiest way to do it I have found so far was it to literally copy the source code of the original term function from :
term.el source
And modify it into:
;; My terminal stuff
(defvar counter 0)
(defun my/open-terminal ()
"Open a new terminal and rename the buffer"
(interactive)
(setq counter (+ counter 1))
(setq title (concat "Terminal-" (number-to-string counter)))
(setq buf-title (concat "*" title "*"))
(message buf-title)
(set-buffer (make-term title "/bin/bash"))
(term-mode)
(term-char-mode)
(switch-to-buffer buf-title)
)
Maybe you can have a look at sane-term package. It has sane-term-create (create new term) and sane-term (loop through terms or create one if none.)
Here is an alternative approach that does not use a counter attached to a global or buffer-local variable -- i.e., the counter is only let-bound for the duration of the function.
(require 'term)
(defun my-term (program)
"Start a terminal-emulator in a new buffer.
The buffer is in Term mode; see `term-mode' for the
commands to use in that buffer.
\\<term-raw-map>Type \\[switch-to-buffer] to switch to another buffer."
(interactive (list (read-from-minibuffer "Run program: "
(or explicit-shell-file-name
(getenv "ESHELL")
(getenv "SHELL")
"/bin/sh"))))
(let* ((n 0))
(catch 'done
(while t
(let* (
bufname
buffer
(basename "term"))
(setq basename (concat basename (if (= n 0) "" (int-to-string n))))
(setq bufname (concat "*" basename "*"))
(setq n (1+ n))
(when (not (get-buffer bufname))
(setq buffer (set-buffer (make-term basename program)))
(term-mode)
(term-char-mode)
(throw 'done (switch-to-buffer buffer))) )))))

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)))

File path to clipboard in Emacs

What is the most simple way to send current full file name with file path to clipboard?
What I am using now is messages buffer: I copy file name that appears there after saving a file. But, I suppose, there should be much more simple way.
Why no one tell the simple solution.
Just go to your dired buffer then press 0 w or C-u 0 w.
This will call dired-copy-filename-as-kill which gives you full path of a file. If you want current dir, just delete the file at the end of it or you can use the function below, then bind it to any key you like.
(defun my/dired-copy-dirname-as-kill ()
"Copy the current directory into the kill ring."
(interactive)
(kill-new default-directory))
PS: personally I go to current directory from file buffer using dired-jump
I use this:
(defun my-put-file-name-on-clipboard ()
"Put the current file name on the clipboard"
(interactive)
(let ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name))))
(when filename
(with-temp-buffer
(insert filename)
(clipboard-kill-region (point-min) (point-max)))
(message filename))))
In Emacs Prelude I use:
(defun prelude-copy-file-name-to-clipboard ()
"Copy the current buffer file name to the clipboard."
(interactive)
(let ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name))))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the clipboard." filename))))
If you want to write the name/path of the current buffer you can type C-u M-: and then either (buffer-file-name) - for the full path - or (buffer-name) for the buffer name.
That is:
M-: + ellisp expression evaluates an ellisp expression in the mini-buffer
C-u write the output to the current buffer
Does not exactly answer to the question but could be useful if someone use this or other function sporadically, and prefers to not initialize the function at every startup.
In the Spacemacs distribution, you can press Spacefyy to display the buffer name in the minibuffer and copy it to the kill ring.
The function spacemacs/show-and-copy-buffer-filename seems to originate from this blog post: Emacs: Show Buffer File Name.
(defun camdez/show-buffer-file-name ()
"Show the full path to the current file in the minibuffer."
(interactive)
(let ((file-name (buffer-file-name)))
(if file-name
(progn
(message file-name)
(kill-new file-name))
(error "Buffer not visiting a file"))))
There's a buffer-extension - and it has copy-buffer-file-name-as-kill function. It even asks You what to copy: name, full name or a directory name.
Edit:
I use modified version of copy-buffer-file-name-as-kill from buffer-extension.el:
(defun copy-buffer-file-name-as-kill (choice)
"Copyies the buffer {name/mode}, file {name/full path/directory} to the kill-ring."
(interactive "cCopy (b) buffer name, (m) buffer major mode, (f) full buffer-file path, (d) buffer-file directory, (n) buffer-file basename")
(let ((new-kill-string)
(name (if (eq major-mode 'dired-mode)
(dired-get-filename)
(or (buffer-file-name) ""))))
(cond ((eq choice ?f)
(setq new-kill-string name))
((eq choice ?d)
(setq new-kill-string (file-name-directory name)))
((eq choice ?n)
(setq new-kill-string (file-name-nondirectory name)))
((eq choice ?b)
(setq new-kill-string (buffer-name)))
((eq choice ?m)
(setq new-kill-string (format "%s" major-mode)))
(t (message "Quit")))
(when new-kill-string
(message "%s copied" new-kill-string)
(kill-new new-kill-string))))
If you use Doom Emacs, it can be done with SPC f y.
To paste the current file path in the buffer, the most simple way I see is to do: C-u M-! pwd (this might not work on Windows systems though).
Alternatively, you can use C-x C-b to show the file paths of all opened buffers.
This is what has worked for me on MacOS 10.15.7, GNU Emacs 27.1
(defun copy-current-buffer-file-name ()
(interactive)
(shell-command (concat "echo " (buffer-file-name) " | pbcopy")))
set keybinding to "C-x M-f":
(global-set-key (kbd "C-x M-f") 'copy-current-buffer-file-name)
FYI: For a real beginner reading this, you need to add those lines to your init.el file.
Lots of good answers here, though I think for the "most simple way", as described in the question, there's room for improvement. Here's what I came up with (with thanks to other answers for the bits and pieces):
M-: (kill-new (buffer-file-name)) RET
This does precisely what you asked for -- takes the filename of the current buffer, and puts it in the "kill ring" and, depending on your settings, also the system clipboard. (See emacswiki/CopyAndPaste for more details on that part.)
If you want to do this regularly, then setting up a function like listed in the other answers, and binding it to an available key sequence, would make it easier to do frequently. But the above works with no prior setup, which I'm interpreting to be more "simple".

Emacs - Can't get buffer-offer-save working

I would like to have Emacs ask me whether I want to save a modified buffer, when that buffer is not associated with a file. To open a new buffer (not visiting a file) I have the following function in my .emacs file:
;; Creates a new empty buffer
(defun new-empty-buffer ()
"Opens a new empty buffer."
(interactive)
(let ((buf (generate-new-buffer "untitled")))
(switch-to-buffer buf)
(funcall (and default-major-mode))
(setq buffer-offer-save t)))
I thought setting "buffer-offer-save" to something not nil would made the trick. But whenever I kill the buffer with "kill-this-buffer", it gets instantly killed without asking anything.
This happens on GNU Emacs 23.1.1
Any ideas?
Thanks,
W
Edited to add use of buffers-offer-save. Note: the variable buffer-offer-save is only used upon exiting Emacs.
You can start with this code and customize it to what you want:
(add-to-list 'kill-buffer-query-functions 'ask-me-first)
(defun ask-me-first ()
"prompt when killing a buffer"
(if (or buffer-offer-save
(eq this-command 'kill-this-buffer)
(and (buffer-modified-p) (not (buffer-file-name))))
(y-or-n-p (format "Do you want to kill %s without saving? " (buffer-name)))
t))
Upon further reflection, that is a bit heavy-handed because you get prompted for all buffers that get killed, and there are often lots of temporary buffers that Emacs uses. If you just want to be prompted when you try to interactively kill a buffer (that isn't associated with a file).
You can use this advice which only prompts you when you're interactively trying to kill a buffer:
(defadvice kill-buffer (around kill-buffer-ask-first activate)
"if called interactively, prompt before killing"
(if (and (or buffer-offer-save (interactive-p))
(buffer-modified-p)
(not (buffer-file-name)))
(let ((answ (completing-read
(format "Buffer '%s' modified and not associated with a file, what do you want to do? (k)ill (s)ave (a)bort? " (buffer-name))
'("k" "s" "a")
nil
t)))
(when (cond ((string-match answ "k")
;; kill
t)
((string-match answ "s")
;; write then kill
(call-interactively 'write-file)
t)
(nil))
ad-do-it)
t)
;; not prompting, just do it
ad-do-it))
Modifying 'new-empty-buffer seems to make it work as I intended with Trey's defadvice.
;; Creates a new empty buffer
(defun new-empty-buffer ()
"Opens a new empty buffer."
(interactive)
(let ((buf (generate-new-buffer "untitled")))
(switch-to-buffer buf)
(funcall (and default-major-mode))
(put 'buffer-offer-save 'permanent-local t)
(setq buffer-offer-save t)))
This makes buffer-offer-save permanent local in our new buffer, so it won't get killed with the rest of the local variables when switching major modes.
buffer-offer-save asking on exiting Emacs but not on closing a buffer manually doesn't make sense, so why not “enlarge” its responsibilities?
(defadvice kill-buffer (around kill-buffer-ask activate)
"If `buffer-offer-save' is non-nil and a buffer is modified,
prompt before closing."
(if (and buffer-offer-save (buffer-modified-p))
(when (yes-or-no-p "The document isn't saved. Quit? ")
ad-do-it)
ad-do-it))
It will not prompt if untitled buffer is newly created. It will not prompt if you use kill-buffer from Elisp. It will not prompt on Emacs system buffers like *Messages*. But it will prompt if you created an empty buffer and wrote something in it.
See also my answer on creating an empty buffer.