0

I'm working on a plugin that allows avy to act from a distance and immediately return to its starting position, which mostly works. However, there is a problem in that an action, such as the deletion of a word, often leaves a superfluous whitespace, and sometimes an action deletes a whitespace it should have left. I have a solution for this: when avy acts on a position at a distance, I'd like to save this position as a variable, and after avy returned to its starting point I would like to set a transient keymap that allows for certain actions, like the insertion of a whitespace, to the part of the buffer that was acted on, and otherwise continues with point where it was. So, in code, something like this:

(defun avy-act-on-position (cmd func size)
  "This function asks for a command and an avy function as chosen
using the avy-function-map, then uses the chosen avy function to
choose a position, and then apply the input command to it. Input
commands are chosen through typing a key combination that is either in
the currently active maps or in the avy-position-command-map, which
overrides the others. Afterwards, spaces at the position that was acted
on can be added or removed through a command in avy-post-action-map.
Through this method, simple editing of areas before or after point
can be done without having to move point."
  (interactive "kCommand: \nkAvy function: \nkSelection size: ")
  (call-interactively (lookup-key avy-function-map func))
  (call-interactively (lookup-key avy-position-selection-map size))
  (call-interactively (lookup-key (make-composed-keymap avy-selection-command-map (make-composed-keymap (current-active-maps t)))  cmd))
  (let ((pos (point)))
    (call-interactively #'avy-pop-mark)
    (let ((pos2 (point)))
    (set-transient-map avy-post-action-map)
    (goto-char (point))))
  )

(defvar-keymap avy-post-action-map
  :doc "Map of actions that can be taken post avy action commands like avy-act-by-same-function and avy-act-on-position."
  "," (progn (goto-char pos)
             (call-interactively #'delete-char)))

The problem is that when I evaluate this keymap, Emacs returns an error because pos is undefined (as it would be set as a local variable in avy-act-on-position). Furthermore, I need all commands in avy-post-action-map to return to the position which was acted on, so I'd like all of them to be precomposed with something like (goto-char pos). Is there some way I can do this, for instance by adding a hook that executes goto-char to a command that is called from avy-post-action-map?

1 Answer 1

2

Did you check the documentation for defvar-keymap? You are supposed to pass it a function¹, but you are instead immediately evaluating some code that tries to use your position variable. Of course it is undefined!²

¹: The docstring for defvar-keymap says “See define-keymap for an explanation of […] KEY/DEFINITION.”. The docstring for define-keymap says “KEY/DEFINITION pairs are as KEY and DEF in keymap-set.”. Finally, keymap-set actually documents what the DEFINITION should be:

DEFINITION is anything that can be a key's definition:
 nil (means key is undefined in this keymap),
 a command (a Lisp function suitable for interactive calling),
 a string (treated as a keyboard macro),
 a keymap (to define a prefix key),
 a symbol (when the key is looked up, the symbol will stand for its
    function definition, which should at that time be one of the above,
    or another symbol whose function definition is used, etc.),
 a cons (STRING . DEFN), meaning that DEFN is the definition
    (DEFN should be a valid definition in its own right) and
    STRING is the menu item name (which is used only if the containing
    keymap has been created with a menu name, see make-keymap),
 or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP,
 or an extended menu item definition.
 (See info node (elisp)Extended Menu Items.)

You want to use either a lambda or a symbol.

²: Actually, it won’t be defined inside your lambda either. You should go back to the Elisp manual and reread chapter 12.10 Scoping Rules for Variable Bindings. There you will discover that let establishes a Lexical Scope, wherein variables are accessible to code that is defined inside of it. But your keymap is defined in a different top–level form, so that scope is inaccessible to the lambdas within it. You want to use a Dynamic Variable, defined using defvar.

Your Answer

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

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