2013-04-18 13 views
5

Sto provando a scrivere un'implementazione di completamento di tabulazione personalizzata che tenta un sacco di diversi completamenti a seconda di dove si trova il punto. Tuttavia, se nessuna delle condizioni per i completamenti è soddisfatta, vorrei che la scheda eseguisse ciò che in origine la modalità corrente intendeva in realtà fare.Elisp: modifica condizionata della combinazione di tasti

Qualcosa di simile a questo:

(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? 

Attualmente sto controllando per le modalità specifiche e di fare la cosa giusta per quel modo, ma mi piacerebbe davvero una soluzione che fa solo la cosa giusta senza di me dover esplicitamente aggiungi una condizione per quella specifica modalità.

Qualche idea su come procedere?

Grazie!/Erik

+1

per 'definire-key' e' locale-set-key' Vedere la documentazione. In genere ciò avviene modificando la mappa chiave specifica della modalità. –

risposta

2

Si potrebbe utilizzare funzioni come key-binding (o le sue varianti più specifiche global-key-binding, minor-mode-key-binding e local-key-binding) per sondare keymap attivi per gli attacchi.

Ad esempio:

(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 

Un modo per evitare cicli infiniti se il comando è destinato a TAB potrebbe essere quello di mettere il vostro legame in un modo minore, e disattivare temporaneamente la sua mappa dei tasti durante la ricerca per la TAB vincolante:

(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"))))) 
+0

Grazie per la tua risposta! Il problema qui è che vorrei associare il comando alla scheda, in modo che l'associazione di tasti restituisca effettivamente la funzione stessa e quindi causi un ciclo infinito. Potrei cercare l'associazione nella mappa chiave "originale" per la modalità in qualche modo? –

+0

Hai ragione, questo non è fatto facilmente. Vedi la mia modifica per una possibile soluzione alternativa. – Francesco

+0

Per una modalità minore, non è necessario rimuovere la voce da 'mode-mode-map-alist': è possibile semplicemente associare la variabile in modalità secondaria (ovvero' my-complete-mode') alla chiamata per "legatura di chiavi". – Stefan

2

E 'possibile che si potrebbe ottenere questo senza alcuna soluzione particolare. Nella maggior parte delle modalità, TAB esegue il rientro per impostazione predefinita, ma se si imposta la variabile globale tab-always-indent su 'complete, tenterà prima di eseguire il completamento e indenterà se il completamento non è possibile. Questo di solito funziona molto bene, anche se se TAB è associato a un altro comando in una delle tue modalità principali potresti essere sfortunato.

Se funziona nelle modalità necessarie, è sufficiente aggiungere la funzione di completamento personalizzata nella parte anteriore dell'elenco completion-at-point-functions in tutti i buffer applicabili (magari utilizzando un hook di modalità). Il comando completion-at-point chiama ciascuna funzione elencata in completion-at-point-functions finché uno di essi non restituisce non nil, quindi tutto ciò che è necessario fare per far sì che la funzione di completamento personalizzata "ricada" sul comportamento esistente è restituire nil da esso.

Questa non è una risposta al 100% alla domanda, ma se le principali modalità con le quali stai lavorando sono scritte secondo le normali linee guida, potrebbe essere il modo più pulito.

+0

Questo è un ottimo punto, ma molte modalità secondarie potrebbero riassociare la chiave 'TAB', ad esempio YASnippet o il completamento automatico, ad esempio. Sai se tali modi minori onorano '(setq tab-always-indent 'completo)'? – Francesco

+0

Se si desidera eseguire l'override di quelle modalità secondarie, eseguire tale operazione. Puoi ancora usare 'indent-for-tab-command' per quello. BTW, se il valore 'complete' di' tab-always-indent' non funziona con yasnippet, potresti segnalarlo come un bug (in yasnippet). – Stefan

3

Ecco una macro che ho scritto in base a Emacs key binding fallback per definire condizionalmente una combinazione di tasti. Si aggiunge la combinazione di tasti al modo minore specificato, ma se la condizione non è vera, l'azione viene eseguita precedentemente assegnato:

(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)))))) 

Allora posso fare le cose come le seguenti operazioni per utilizzare lo speciale legame per TAB solo quando sono su un'intestazione in modalità contorno-minore.In caso contrario, viene eseguita la mia azione di default (ho sia trattino e yasnippets):

(define-key-with-fallback outline-minor-mode-map (kbd "TAB") 
    (outline-cycle 1) (outline-on-heading-p)) 
3

BTW, qui è un'altra soluzione:

(define-key <map> <key> 
    `(menu-item "" <my-cmd> :filter ,(lambda (cmd) (if <my-predicate> cmd)))) 
+0

Questo trucco mi ha salvato così tante volte. –

0

define-key può accettare stringa tra virgolette o lambda interattivi come in questo esempio.

;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 può essere sostituito con funzioni denominate come my-tab-completion e utilizzato in modo più efficace.

Da docstring di definire-chiave (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'.) 
Problemi correlati