Demo of input.el
(Non-EXWM
)
Demo of input.el
(EXWM
)
input
phase: Inititate it with input-start
(= C-c '
)
post-input
phase: Finalize input with input-done
(= C-c '
)
do this
- Copy the above below snippet to a file
~/input.el
emacs -Q
M-x load-file RET ~/input.el
- In the
*scratch*
buffer, do C-c '
.
- A new window will pop up with
tamil-phonetic
input method activated.
- In the new input window, type something.
- Type
C-c '
to exit.
- Profit.
input.el
(require 'org-macs)
(require 'subr-x)
(defvar input-start-keys (kbd "C-c '"))
(global-set-key input-start-keys 'input-start)
(defvar input-buffer-preferred-input-method "tamil-phonetic"
"Input method active in `input-mode'.")
(defvar input-mode-hook nil)
(defvar-local input--sink-beg-marker nil)
(put 'input--sink-beg-marker 'permanent-local t)
(defvar-local input--sink-end-marker nil)
(put 'input--sink-end-marker 'permanent-local t)
(defvar-local sink--overlay nil)
(put 'sink--overlay 'permanent-local t)
(defvar-local input-mode-stashed-window-config nil)
(put 'input-mode-stashed-window-config 'permanent-local t)
(defun input-buffer-locate (beg end)
"Return buffer editing area between BEG and END.
Return nil if there is no such buffer."
(catch 'exit
(dolist (b (buffer-list))
(with-current-buffer b
(and (input-buffer-p)
(= beg input--sink-beg-marker)
(eq (marker-buffer beg) (marker-buffer input--sink-beg-marker))
(= end input--sink-end-marker)
(eq (marker-buffer end) (marker-buffer input--sink-end-marker))
(throw 'exit b))))))
(defun sink--make-overlay (beg end input-buffer)
"Create overlay between BEG and END positions and return it.
INPUT-BUFFER is the buffer currently editing area between BEG and
END."
(let ((overlay (make-overlay beg end)))
(overlay-put overlay 'face 'secondary-selection)
(overlay-put overlay 'input-buffer input-buffer)
(overlay-put overlay 'help-echo
"Click with mouse-1 to switch to buffer editing this segment")
(overlay-put overlay 'face 'secondary-selection)
(let ((read-only
(list
(lambda (&rest _)
(user-error
"Cannot modify an area being edited in a dedicated buffer")))))
(overlay-put overlay 'modification-hooks read-only)
(overlay-put overlay 'insert-in-front-hooks read-only)
(overlay-put overlay 'insert-behind-hooks read-only))
overlay))
(defun input--finalize ()
"Remove overlay from current source buffer."
(deactivate-transient-input-method)
(when (overlayp sink--overlay)
(delete-overlay sink--overlay)))
;;; Input minor mode
(defvar input-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c'" 'input-done)
(define-key map "\C-c\C-k" 'input-abort)
map))
(define-minor-mode input-mode
"Minor mode for inputting strings.
Input is initiated with `input-start` (=`\\[input-start]').
To change the key binding set `input-start-keys'.
The default `input-mode-hook' activates the transient input
method defined in `input-buffer-preferred-input-method'.
\\{input-mode-map}."
:lighter " Input"
(setq header-line-format
(substitute-command-keys
(propertize "Edit, then exit with `\\[input-done]' or abort with \ `\\[input-abort]'"
'face 'highlight))))
(defun my-activate-transient-input-method (input-method)
(setq default-transient-input-method input-method)
(setq previous-transient-input-method current-input-method)
(when previous-transient-input-method
(deactivate-input-method))
(activate-input-method default-transient-input-method)
(setq current-transient-input-method default-transient-input-method))
(defun input--configure ()
"Configure the input buffer."
(my-activate-transient-input-method input-buffer-preferred-input-method)
(add-hook 'kill-buffer-hook #'input--finalize nil 'local))
(add-hook 'input-mode-hook #'input--configure)
(defun input-buffer-p (&optional buffer)
"Non-nil when current buffer is a input buffer.
If BUFFER is non-nil, test it instead."
(let ((buffer (org-base-buffer (or buffer (current-buffer)))))
(and (buffer-live-p buffer)
(local-variable-p 'input--sink-beg-marker buffer)
(local-variable-p 'input--sink-end-marker buffer))))
(defun input-abort ()
"Abort input and return to the sink buffer."
(interactive)
(unless (input-buffer-p)
(error "Not in a sub-editing buffer"))
(let* ((beg input--sink-beg-marker)
(end input--sink-end-marker))
(set-buffer-modified-p nil)
;; Switch to source buffer. Kill sub-editing buffer.
(let ((edit-buffer (current-buffer))
(source-buffer (marker-buffer beg)))
(unless source-buffer
(error "Source buffer disappeared. Aborting"))
(org-switch-to-buffer-other-window source-buffer)
(delete-other-windows)
(kill-buffer edit-buffer))
(goto-char beg)
;; Clean up left-over markers and restore window configuration.
(set-marker beg nil)
(set-marker end nil)
(when input-mode-stashed-window-config
(unwind-protect
(set-window-configuration input-mode-stashed-window-config)
(setq input-mode-stashed-window-config nil)))))
(defun input-done ()
"Kill current sub-editing buffer and return to source buffer."
(interactive)
(unless (input-buffer-p)
(error "Not in a sub-editing buffer"))
(let* ((beg input--sink-beg-marker)
(end input--sink-end-marker)
(rel-pos (- (point) 1))
(contents (buffer-substring-no-properties (point-min) (point-max))))
(kill-new contents)
(message "Copied input text in to kill ring")
(set-buffer-modified-p nil)
;; Switch to source buffer. Kill sub-editing buffer.
(let ((input-buffer (current-buffer))
(sink-buffer (marker-buffer beg)))
(unless sink-buffer
(error "Source buffer disappeared. Aborting"))
(org-switch-to-buffer-other-window sink-buffer)
(delete-other-windows)
(kill-buffer input-buffer))
;; Insert modified code.
(cond
((derived-mode-p 'exwm-mode)
(exwm-input--fake-key ?\C-v))
(
(org-with-wide-buffer
(when (not (equal (buffer-substring beg end)
contents))
(undo-boundary)
(goto-char beg)
(save-restriction
(narrow-to-region beg end)
(replace-region-contents beg end (lambda () contents))
(goto-char (point-max)))))
;; If we are to return to source buffer, put point at an
;; appropriate location. In particular, if block is hidden, move
;; to the beginning of the block opening line.
(goto-char (+ beg rel-pos))))
;; Clean up left-over markers and restore window configuration.
(set-marker beg nil)
(set-marker end nil)
(when input-mode-stashed-window-config
(unwind-protect
(set-window-configuration input-mode-stashed-window-config)
(setq input-mode-stashed-window-config nil)))))
(defun input-start ()
"Call a special editor for the element at point."
(interactive)
(cond
((derived-mode-p 'exwm-mode)
(ignore))
(t
(barf-if-buffer-read-only)))
(setq input-mode-stashed-window-config (current-window-configuration))
(let* ((name (format "*Input %s*" (buffer-name)))
(beg (copy-marker (point)))
(end (copy-marker (point) t))
(old-edit-buffer (input-buffer-locate beg end))
(contents ""))
(if old-edit-buffer
;; Move to existing buffer.
(org-switch-to-buffer-other-window old-edit-buffer)
(let* (;; Generate a new input buffer.
(input-buffer (generate-new-buffer name))
;; Add an overlay on top of source.
(overlay (sink--make-overlay beg end input-buffer)))
;; Switch to input buffer.
(delete-other-windows)
(org-switch-to-buffer-other-window input-buffer)
(set-buffer-modified-p nil)
(setq buffer-file-name nil)
;; Initialize buffer.
(text-mode)
;; Transmit buffer-local variables for exit function. It must
;; be done after initializing major mode, as this operation
;; may reset them otherwise.
(setq input--sink-beg-marker beg)
(setq input--sink-end-marker end)
(setq sink--overlay overlay)
;; Start minor mode.
(input-mode)
;; Clear undo information so we cannot undo back to the
;; initial empty buffer.
(buffer-disable-undo (current-buffer))
(buffer-enable-undo)
(goto-char (point-max))))))
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
FWIW, the above snippet is derived from org-src.el
after stripping down all the org
-isms.
Btw, org-src.el
is the library that implements editing of Org Babel and other special blocks.
In org-src
case you are inputting n various programming (or structural programming) languages; in your case, you are inputting in different human
language.
I looked at org-src.el
, to see if I can repurpose the default library to suit your current needs; unfortunately org-src.el
was too soaked in org
-isms that I had to copy stuff over and strip it to its bare essentials. In programmers-speak, the code that you see here is an org-src-core.el
around which org-src.el
could be built. In practical terms, the code I have shared here "works" or "known to work" (modulo some bugs I may have introduced during "aggressive" stripping or my own "limited" interpretation of what some areas of code are doing .)
IOW, if you know how to edit org
babel blocks, you know how to use the above snippet.
So much of what Editing Source Code (The Org Manual)
(info "(org) Editing Source Code")
says holds good.
Three areas where I have done "aggressive" stripping:
- Edting a region / Pre-filling of
input
buffer: org-src.el
allows editing of a region a block of text. That is, it populates the input
buffer with current src
-block content. In the present case, the input
happens at point
. So, the input
buffer starts out empty.
- *Handling of
mark
-s
org-src-window-setup
: org-src.el
provides much wider set of options.
If my choice of window setup for inputting is not what you like, you need to borrow from org-src-window-setup
.
Any further enhancements or bug fixes are left as an exercise to the reader. I have already spent enough brain cycles on what I mistakenly assumed to be a "trivial" exercise.