0

Often there is a function which then asks for another keystroke to determine how to act. Examples for this are the cdlatex functions cdlatex-math-modify, cdlatex-environment, or the reftex function reftex-reference. (I think it reads the keystroke with the readchar.)

I want to write an elisp-expression, which calls the function and then "presses the key", such that the function does its behavior in the special case.

e.g. I want to write a function called vec which, when evaluated, does the same thing, as cdlatex-math-modify when I press > next.

.

Edit: If this helps, here is the code for two functions, in which this is used. I think the important thing is that the char is read by readchar or read-char-exclusive. Because a person in a comment pointed out it is good to see the implementation in elisp code, here is the full code.

First for the function reftex-select-with-char:

(defun reftex-select-with-char (prompt help-string &optional delay-time scroll)
;; Offer to select something with PROMPT and, after DELAY-TIME seconds,
;; also with HELP-STRING.
;; When SCROLL is non-nil, use SPC and DEL to scroll help window.
(let ((char ?\?))
(save-window-excursion
  (catch 'exit
    (message "%s   (?=Help)" prompt)
    (when (or (sit-for (or delay-time 0))
              (= ?\? (setq char (read-char-exclusive))))
      (reftex-kill-buffer "*RefTeX Select*")
      (switch-to-buffer-other-window "*RefTeX Select*")
      (insert help-string)
      (goto-char 1)
      (unless (and (pos-visible-in-window-p (point-min))
                   (pos-visible-in-window-p (point-max)))
        (enlarge-window (1+ (- (count-lines (point-min) (point-max))
                               (reftex-window-height)))))
      (setq truncate-lines t))
    (if (and (pos-visible-in-window-p (point-min))
             (pos-visible-in-window-p (point-max)))
        nil
      (setq prompt (concat prompt (if scroll "   (SPC/DEL=Scroll)" ""))))
    (message "%s" prompt)
    (and (equal char ?\?) (setq char (read-char-exclusive)))
    (while t
      (cond ((equal char ?\C-g) (keyboard-quit))
            ((equal char ?\?))
            ((and scroll (equal char ?\ ))
             (condition-case nil (scroll-up) (error nil))
             (message "%s" prompt))
            ((and scroll (equal char ?\C-? ))
             (condition-case nil (scroll-down) (error nil))
             (message "%s" prompt))
            (t (message "")
       (reftex-kill-buffer "*RefTeX Select*")
               (throw 'exit char)))
      (setq char (read-char-exclusive)))))))

For the Function cdlatex-math-modify we have: (They key is read by readchar. The source is https://github.com/cdominik/cdlatex/blob/master/cdlatex.el)

defun cdlatex-math-modify (arg)
"Modify previous char/group/macro with math accent/style.
This macro modifies the character or TeX macro or TeX group BEFORE point
with a math accent or a style.
If the character before point is white space, an empty modifying form
is inserted and the cursor positioned properly.
If the object before point looks like word, this macro modifies the last
character of it.
All this happens only, when the cursor is actually inside a LaTeX math
environment.  In normal text, it does just a self-insert.
The accent and style commands and their properties are defined in   the
constant `cdlatex-math-modify-alist'."

(interactive "P")
(catch 'exit

(let ((inside-math (cdlatex--texmathp))
      (win (selected-window))
      char (help-is-on nil) ass acc rmdot it cmd extrabrac)
  (catch 'exit1
    (save-window-excursion
      (while t
        (if help-is-on
            (progn
              (cdlatex-turn-on-help
               "AVAILABLE MODIFIERS. (?=SCROLL)"
               (if inside-math 1 2)
               cdlatex-math-modify-alist-comb help-is-on t)
              (message "Math modify: "))
          (message "Math modify: (?=HELP)"))

        (if (and (not help-is-on)
                 (sit-for cdlatex-auto-help-delay))
            (setq char ?\?)
          (setq char (read-char)))
(cond
         ((= char ?\C-g)
          (keyboard-quit))
         ((= char ?\?)
          (if help-is-on
              (progn
                (setq help-is-on (+ help-is-on (- (window-height) 1)))
                (if (> help-is-on (count-lines (point-min) (point-max)))
                    (setq help-is-on 1)))
            (setq help-is-on 1)))
         ((equal char cdlatex-math-modify-prefix)
          (select-window win)
          (insert cdlatex-math-modify-prefix)
          (message "")
          (throw 'exit t))
         (t (throw 'exit1 t))))))
  (message "")
  (setq ass (assoc char cdlatex-math-modify-alist-comb))
  (if (not ass)
      (progn
        (insert cdlatex-math-modify-prefix char)
        (throw 'exit t)))
  (setq ass    (cdr ass))
  (setq cmd    (nth (if inside-math 0 1) ass))
  (setq acc    (nth 2 ass))
  (setq rmdot  (nth 3 ass))
  (setq it     (nth 4 ass))
  (if (not cmd) (error "No such modifier `%c' %s math mode" char
                       (if inside-math "inside" "outside")))
  (cond
   ((cdlatex-region-active-p)
    (let ((beg (min (region-beginning) (region-end)))
          (end (max (region-beginning) (region-end))))
      (goto-char end)
      (point-to-register ?x)
      (goto-char beg)
      (insert "{")
      (if acc (forward-char -1))
      (insert cmd)
      (if (not acc) (insert " "))
      (register-to-point ?x)
      (insert "}")))
   (arg
    (point-to-register ?x)
    (backward-word arg)
    (insert "{")
    (if acc (forward-char -1))
    (insert cmd)
    (if (not acc) (insert " "))
    (register-to-point ?x)
    (insert "}"))
   ((or (bolp)
        (not cdlatex-modify-backwards)
        (memq (preceding-char) '(?\  ?$ ?- ?{ ?\( )))
    ;; Just insert empty form and position cursor
    (if acc
        (insert cmd "{?")
      (insert "{" cmd " ?"))
    (if it (insert "\\/"))
    (insert "}")
    (search-backward "?")
    (delete-char 1))
   (t
    ;; Modify preceding character or word
    (point-to-register ?x)
    (if (= (preceding-char) ?\})
        ;; its a group
        (progn (setq extrabrac nil)
               (backward-list 1)
               (if (not acc) (forward-char 1)))
      ;; not a group
      (forward-char -1)
      (if (looking-at "[a-zA-Z]")
          ;; a character: look if word or macro
          (progn
            (setq extrabrac t)
            (re-search-backward "[^a-zA-Z]")
            (cond
             ((= (following-char) ?\\))
             ((not inside-math) (forward-char 1))
             (t (register-to-point ?x)
                (forward-char -1)
                (if (and rmdot (looking-at "[ij]"))
                    (progn (insert "\\")
                           (forward-char 1)
                           (insert "math")
                           (point-to-register ?x)
                           (forward-char -6))))))
        (setq extrabrac t)))
    (if extrabrac (progn (insert "{")
                         (if acc (forward-char -1))))
    (insert cmd)
    (if (not acc) (insert " "))
    (register-to-point ?x)
    (if extrabrac (insert "}")))))))
3
  • 1
    Your best bet is to read the elisp code for those functions and see how they implement that keystroke dispatch. Aug 18, 2022 at 14:59
  • I thought that knowing it is the function readchar was enough. Now I have added the full source code of the relevant functions. Thank you for answering!
    – Sinthoras
    Aug 18, 2022 at 15:42
  • 1
    What @PhilHudson said. Look at the code, and see where it reads the char (likely in the interactive spec), and then see where the result of reading is used. Typically, when used interactively a command reads something in the interactive spec and passes what was read as an argument. If that's the case then you can just pass the value you want as that arg to the function.
    – Drew
    Aug 18, 2022 at 17:41

1 Answer 1

3

You can use unread-command-events to "push back a character onto the input", so the next time a read-char happens, that character will be returned, rather than having read-char wait for "actual" input.

Here is a simple illustration: the function foo does a read-char and returns the character read:

(defun foo ()
   (read-char "Enter a char: "))

Evaluating the function call (foo) will issue a prompt and wait for you to type a character. It then returns that character (remember characters are represented by integers in Emacs Lisp).

You can now push something onto unread-command-events and evaluate the function call. The read-char will read whatever you pushed and return that value:

(setq c (progn (push ?A unread-command-events) (foo)))

c  --> 65 ;; the ASCII value of the character A

(setq c (progn (push ?a unread-command-events) (foo)))

c --> 97 ;; the ASCII value of the character a

Obvious care needs to be exercised of course, and there may be unforeseen complications. There is no warranty: if it breaks, you get to keep the pieces.

1
  • Thank you! This was what I searched for!
    – Sinthoras
    Aug 19, 2022 at 18:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.