Comandos casi idénticos en múltiples teclas

7

Visual Studio tiene esta característica agradable donde puedes poner el punto en algo, presionar C-f3y ambos encontrarán la próxima instancia de la cosa debajo del punto y lo recordarán. Luego puede presionar f3para encontrar la siguiente instancia y S-f3buscar esa misma cosa, pero al revés.

Puedo hacer lo mismo en Emacs usando algo de elisp que escribí. (Es voluminoso y estoy seguro de que es malo, así que lo omitiré).

Lo que me gustaría hacer es permitir una funcionalidad similar para, por ejemplo, f2, f3, f4, y f5. Por lo tanto, presionar C-f2busca la cosa debajo del punto al guardar esa cosa en una variable / matriz de asociación / símbolo en algún lugar, presionar C-f3una cosa diferente hace que emacs guarde esa segunda cosa para guardarla en una variable distinta, y así puedo buscar para lo primero presionando f2en el futuro, y puedo buscar la segunda presionando f3en el futuro.

Me encantaría crear una sola función para Control-X, otra para X simple y una tercera para Shift-X, pero no estoy seguro de cómo abordar esto.

¿Es mejor tener que hacer que la función solicite la tecla actual presionada (a la this-single-command-keys), o encontrar una manera de pasar un parámetro a la función interactiva ( sin preguntar al usuario, ya que ya presionó una tecla)?

¿Cómo se pasa información adicional a una función interactiva?

MikeTheTall
fuente
La pregunta no es muy clara, OMI. ¿Qué información y cómo quieres pasarla? Puede usar un argumento prefijo para pasar información de forma interactiva. Un comando puede solicitar información (no quieres eso, dijiste). Una función puede obtener información de variables globales o de un archivo o ... ¿Qué es lo que desea pasar, cuándo y cómo? Si tiene algún código, quizás lo muestre, es probable que sea más claro que su pregunta (hasta ahora), IMO.
Dibujó
44
@Drew Creo que una vez que superas el título general engañoso, hay un problema de diseño específico allí: múltiples comandos que hacen exactamente lo mismo, excepto que usan una variable diferente para almacenar el estado de una invocación a la siguiente.
Gilles 'SO- deja de ser malvado'
@Gilles: Por eso no voté para cerrarlo como poco claro. Todavía podría ser más claro. Por ejemplo, si las variables se van a usar de esa manera, ¿qué es lo que realmente está en cuestión?
Dibujó el
P : " ¿Cómo se pasa información adicional a una función interactiva? " R : Esto normalmente se hace con un prefijo de comando como C-u; o, al pedirle al usuario que ingrese una selección del minibúfer, o pedirle al usuario que elija una clave en particular; por ejemplo, presione 1 para foo, presione 2 para bar, presione 3 para baz. [Alrededor del 90% de toda la pregunta es interesante , pero no relevante para la última pregunta / respuesta (en mi opinión). La segunda a la última pregunta busca una opinión .]
lawlist

Respuestas:

4

Puede vincular un comando a varias secuencias de teclas y acceder a la secuencia de teclas de llamada dentro del comando con this-command-keys. Eso se demuestra con el siguiente código. Existe el mismo comando my-commandestá ligado indirectamente a las secuencias de teclas F9, C-c a, y C-c b. De este modo, "indirectamente" significa que hay una envoltura delgada my-command-wrapper. Ese contenedor se usa como comando interactivo y se mantiene muy simple. No debe ser instrumentado edebug-defunporque el valor de retorno de this-command-keysen la sesión de depuración no tiene sentido.

El código de ejemplo muestra cómo puede mantener historias separadas para las secuencias de teclas de llamada separadas.

Si llama a las secuencias de teclas la primera vez, o con el prefijo arg, o con shift-modifier, puede cambiar el valor del argumento para que, de lo my-commandcontrario, se use el último valor del argumento del historial.

(defvar my-command-history-alist nil
  "Association of key sequence")
(defvar my-command--history nil)

(defun my-command-wrapper (&rest args)
  "Thin wrapper for `my-command'.
Adds stringified `this-command-keys' as the first argument to ARGS.
Don't instrument this function with `edebug-defun' otherwise
`this-command-keys' gives the wrong answer!"
  (interactive)
  (apply #'my-command (format "%s" (this-command-keys))
     args))

(defun my-command (keys &optional what)
  "Depending on the call sequence KEYS and PREFIX for this command do something with argument WHAT."
  (unless what
   (let* ((key-history (assoc-string keys my-command-history-alist))
      (my-command--history (cdr key-history)))
     (if (or (null my-command--history)
         current-prefix-arg
         this-command-keys-shift-translated)
     (progn
       (setq what (read-string (format "Input string for key sequence \"%s\":" keys) (car my-command--history) 'my-command--history))
       (if key-history
           (setcdr key-history my-command--history)
         (push (cons keys my-command--history) my-command-history-alist)))
       (setq what (car my-command--history)))))
  (message "Doing something with \"%s\"" what))

(global-set-key (kbd "<f9>") #'my-command-wrapper)
(global-set-key (kbd "C-c a") #'my-command-wrapper)
(global-set-key (kbd "C-c b") #'my-command-wrapper)
Tobias
fuente
4

Miremos esto lógicamente: desea tener comandos casi idénticos vinculados a C-f2y C-f3. La única diferencia entre estos comandos es si almacenan la cosa en cuestión en la f2memoria o en la f3memoria. Entonces, o necesita construir diferentes comandos, o necesita tener un solo comando cuyo comportamiento depende de la clave a la que está vinculado.

Puede vincular una clave a un comando que se construye en el momento en que crea el enlace. El argumento de comando para define-keyy amigos no tiene que ser un nombre de comando en forma de símbolo, puede ser una expresión lambda.

(global-set-key [C-f3] (lambda ()
                         (interactive)
                         …))

Esto funciona, pero no es muy agradable. Por ejemplo, los comandos de ayuda y los historiales de comandos no le mostrarán un nombre de comando.

Puede colocar la mayor parte del código en una función y definir pequeñas funciones de contenedor. Para evitar repetir mucho código, haga que una función o macro genere las funciones del contenedor.

(defun repeat-search-thing-at-point-forward (memory)
  (search-forward (symbol-value memory)))
(defun repeat-search-thing-at-point-backward (memory)
  (search-backward (symbol-value memory)))
(defun search-thing-at-point (memory)
  "Search the thing at point.
Store the thing in MEMORY for a future search with
`repeat-search-thing-at-point-forward' and
`repeat-search-thing-at-point-backward'."
  (set memory (thing-at-point 'word))
  (repeat-search-thing-at-point-forward memory))
(defun define-search-thing-at-point (map key)
  "Define commands to search a thing at point "
  (let* ((memory-variable (intern (format "search-memory-%s" key)))
         (set-function (intern (format "search-thing-at-point-%s" key)))
         (forward-function (intern (format "repeat-search-thing-at-point-forward-%s" key)))
         (backward-function (intern (format "repeat-search-thing-at-point-backward-%s" key)))
         (forward-key (vector key))
         (backward-key (vector (list 'shift key)))
         (set-key (vector (list 'control key))))
    (eval `(progn
             (defvar ,memory-variable nil
               ,(format "The last thing searched with \\[%s]." set-function))
             (defun ,set-function ()
               ,(format "Search the thing at point.
Use \\[%s] and \\[%s] to repeat the search forward and backward
respectively." forward-function backward-function)
               (interactive "@")
               (search-thing-at-point ',memory-variable))
             (defun ,forward-function ()
               ,(format "Search forward for the last thing searched with \\[%s]." set-function)
               (interactive "@")
               (repeat-search-thing-at-point-forward ',memory-variable))
             (defun ,backward-function ()
               ,(format "Search backward for the last thing searched with \\[%s]." set-function)
               (interactive "@")
               (repeat-search-thing-at-point-backward ',memory-variable))
             (define-key map ',set-key #',set-function)
             (define-key map ',forward-key #',forward-function)
             (define-key map ',backward-key #',backward-function)
             t))))

(define-search-thing-at-point global-map 'f2)
(define-search-thing-at-point global-map 'f3)
(define-search-thing-at-point global-map 'f4)

Alternativamente, puede definir un solo comando para cada funcionalidad (primera búsqueda, repetir hacia adelante, repetir hacia atrás). Esto es un poco menos flexible (por ejemplo, no puede volver a vincular `search-thing-at-point-f2 a H-ssi lo desea), pero es mucho menos detallado.

Un comando puede encontrar qué tecla lo invocó. La forma más fácil para usted es usar la variable last-command-event.

(defvar search-thing-memory nil
  "History of things searched with `search-thing-at-point'.")
(defun search-thing-at-point (key)
  "Search the thing at point.
Store the thing in MEMORY for a future search with
`repeat-search-thing-at-point-forward' and
`repeat-search-thing-at-point-backward'."
  (interactive (list (event-basic-type last-command-event)))
  (let ((thing (thing-at-point 'word))
    (memory (assq key search-thing-memory)))
    (if memory
    (setcdr memory thing)
      (setq search-thing-memory (cons (cons key thing)
                      search-thing-memory)))
    (search-forward thing)))
(defun repeat-search-thing-at-point-forward (key)
  "Repeat the last thing searched with `search-thing-at-point'
with a matching key binding."
  (interactive (list (event-basic-type last-command-event)))
  (search-forward (cdr (assq key search-thing-memory))))
(defun repeat-search-thing-at-point-backward (key)
  "Repeat the last thing searched with `search-thing-at-point'
with a matching key binding."
  (interactive (list (event-basic-type last-command-event)))
  (search-backward (cdr (assq key search-thing-memory))))

(global-set-key [C-f2] 'search-thing-at-point)
(global-set-key [C-f3] 'search-thing-at-point)
(global-set-key [C-f4] 'search-thing-at-point)
(global-set-key [f2] 'repeat-search-thing-at-point-forward)
(global-set-key [f3] 'repeat-search-thing-at-point-forward)
(global-set-key [f4] 'repeat-search-thing-at-point-forward)
(global-set-key [S-f2] 'repeat-search-thing-at-point-backward)
(global-set-key [S-f3] 'repeat-search-thing-at-point-backward)
(global-set-key [S-f4] 'repeat-search-thing-at-point-backward)

No creo que su interfaz propuesta sea una adición particularmente útil a Emacs. La búsqueda incorporada básica de Emacs tiene formas fáciles de buscar la cosa en el punto y repetir búsquedas pasadas.

Gilles 'SO- deja de ser malvado'
fuente
2

Al abordar su caso de uso original en lugar de la pregunta de programación de Elisp, el paquete highlight-symbolhace lo que desea. He sido un usuario feliz por muchos años.

http://nschum.de/src/emacs/highlight-symbol/

De la descripción del paquete:

;; Add the following to your .emacs file:
;; (require 'highlight-symbol)
;; (global-set-key [(control f3)] 'highlight-symbol)
;; (global-set-key [f3] 'highlight-symbol-next)
;; (global-set-key [(shift f3)] 'highlight-symbol-prev)
;; (global-set-key [(meta f3)] 'highlight-symbol-query-replace)
;;
;; Use `highlight-symbol' to toggle highlighting of the symbol at
;; point throughout the current buffer.  Use `highlight-symbol-mode' to keep the
;; symbol at point highlighted.
;;
;; The functions `highlight-symbol-next', `highlight-symbol-prev',
;; `highlight-symbol-next-in-defun' and `highlight-symbol-prev-in-defun' allow
;; for cycling through the locations of any symbol at point.  Use
;; `highlight-symbol-nav-mode' to enable key bindings (M-p and M-p) for
;; navigation. When `highlight-symbol-on-navigation-p' is set, highlighting is
;; triggered regardless of `highlight-symbol-idle-delay'.
;;
;; `highlight-symbol-query-replace' can be used to replace the symbol.
Phil Hudson
fuente
¡Agradable! Vi que había varias opciones para este tipo de cosas y en realidad usé HiLock para resaltar (principalmente porque ya está integrado en Emacs (olvido qué versión).
MikeTheTall
Además, me encanta lo que tienes, pero la pregunta se hizo específicamente para 'guardar' lo que estoy buscando en varias claves. Por lo que necesitaría que esto funcione para F3, y también para poder buscar por separado para algo más en F4, y luego buscar una tercera cosa en F5, etc
MikeTheTall