emacs - Elisp: cambie condicionalmente la vinculación
emacs lisp (5)
Es posible que pueda lograr esto sin ninguna solución especial en absoluto. En la mayoría de los modos, TAB
simplemente realiza la sangría de forma predeterminada, pero si configura la variable global tab-always-indent
para que se complete, primero intentará completarla y sangría si no es posible completarla. Esto generalmente funciona muy bien, aunque si TAB
está vinculado a otro comando en uno de tus modos principales, es posible que no tengas suerte.
Si eso funciona en los modos que necesita, solo tendrá que agregar su función de finalización personalizada al principio de la lista de funciones de finalización en todas las memorias intermedias correspondientes (tal vez utilizando un enlace de modo). El comando completion-at-point
llama a cada función listada en funciones de completion-at-point-functions
hasta que una de ellas devuelve no- nil
, por lo que todo lo que necesita hacer para que su función de finalización personalizada "se desvíe" del comportamiento existente es return nada de eso.
Esta no es una respuesta al 100% a la pregunta, pero si los modos principales con los que está trabajando están escritos de acuerdo con las pautas normales, podría ser la forma más limpia.
Intento escribir una implementación de finalización de pestañas personalizada que intente varias terminaciones diferentes según el punto. Sin embargo, si no se cumple ninguna de las condiciones de finalización, me gustaría que Tab haga lo que el modo actual originalmente pretendía.
Algo como esto:
(defun my-custom-tab-completion ()
(interactive)
(cond
(some-condition
(do-something))
(some-other-condition
(do-something-else))
(t
(do-whatever-tab-is-supposed-to-do-in-the-current-mode))) ;; How do I do this?
Actualmente estoy buscando modos específicos y haciendo lo correcto para ese modo, pero realmente me gustaría una solución que simplemente haga lo correcto sin que tenga que agregar explícitamente una condición para ese modo específico.
Alguna idea de como hacer esto?
¡Gracias! / Erik
Puede utilizar funciones como key-binding
(o sus variantes más específicas, global-key-binding
, global-key-binding
minor-mode-key-binding
y minor-mode-key-binding
local-key-binding
) para sondear mapeos de teclas activos para enlaces.
Por ejemplo:
(call-interactively (key-binding (kbd "TAB")))
;; in an emacs-lisp-mode buffer:
;; --> indent-for-tab-command
;;
;; in a c++-mode buffer with yas/minor-mode:
;; --> yas/expand
Una forma de evitar bucles infinitos si su comando está vinculado a TAB podría ser poner su enlace en un modo menor, y desactivar temporalmente su mapa de teclas mientras busca el enlace TAB :
(define-minor-mode my-complete-mode
"Smart completion"
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "TAB") ''my-complete)
map))
(defun my-complete ()
(interactive)
(message "my-complete")
(let ((my-complete-mode nil))
(call-interactively (key-binding (kbd "TAB")))))
Por cierto, aquí hay otra solución:
(define-key <map> <key>
`(menu-item "" <my-cmd> :filter ,(lambda (cmd) (if <my-predicate> cmd))))
define-key
puede aceptar cadenas citadas o lambdas interactivas como en este ejemplo.
;Static
(define-key evil-normal-state-mapr "m" ''evil-motion-state)
;Conditional
(define-key evil-normal-state-map "m"
(lambda () (interactive) (message "%s" major-mode)))
Lambda puede ser reemplazado con funciones nombradas como my-tab-completion y se usa de manera más efectiva.
Del docstring de define-key (Emacs 25)
DEF 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),
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''.)
Aquí hay una macro que escribí en función del retorno de enlace de claves de Emacs para definir un enlace de claves de forma condicional. Agrega la vinculación al modo menor especificado, pero si la condición no es verdadera, se ejecuta la acción asignada previamente:
(defmacro define-key-with-fallback (keymap key def condition &optional mode)
"Define key with fallback. Binds KEY to definition DEF in keymap KEYMAP,
the binding is active when the CONDITION is true. Otherwise turns MODE off
and re-enables previous definition for KEY. If MODE is nil, tries to recover
it by stripping off /"-map/" from KEYMAP name."
`(define-key ,keymap ,key
(lambda () (interactive)
(if ,condition ,def
(let* ((,(if mode mode
(let* ((keymap-str (symbol-name keymap))
(mode-name-end (- (string-width keymap-str) 4)))
(if (string= "-map" (substring keymap-str mode-name-end))
(intern (substring keymap-str 0 mode-name-end))
(error "Could not deduce mode name from keymap name (/"-map/" missing?)"))))
nil)
(original-func (key-binding ,key)))
(call-interactively original-func))))))
Luego puedo hacer cosas como las siguientes para usar el enlace especial para TAB solo cuando estoy en un encabezado en outline-minor-mode. De lo contrario, se ejecuta mi acción predeterminada (tengo sangría y yasnippets):
(define-key-with-fallback outline-minor-mode-map (kbd "TAB")
(outline-cycle 1) (outline-on-heading-p))