2013-04-18 3 views
5

私はポイントがどこにあるかによって異なる補完を試すカスタムタブ補完の実装を書こうとしています。しかし、補完の条件が一つも満たされていない場合は、現在のモードが本来意図していたことを行うためにタブを使いたいと思います。このようなElisp:条件付きでキーバインドを変更する

何か:

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

は現在、私は、特定のモードをチェックし、そのモードのために正しいことをやって、私は本当に私が明示的にすることなく、ちょうどいいことをやっソリューションを希望していますその特定のモードの条件を追加します。

これを行う方法のアイデアはありますか?

ありがとうございます! /エリック

+1

'定義-key'と'ローカル・セットkey'のマニュアルを参照してください。これは、通常、モード固有のキーマップを変更することによって行われます。 –

答えて

2

あなたはバインディングのアクティブなキーマップを調べるために、このようなkey-binding(またはその複数の特定の亜種global-key-bindingminor-mode-key-bindinglocal-key-binding)などの機能を使用することができます。例えば

:あなたのコマンドはTABにバインドされている場合、無限ループを回避する1つの方法は、マイナーモードであなたのバインディングを配置すること、そして見ながら、一時的にそのキーマップを無効にすることができ

(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 

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

あなたの答えをありがとう!ここでの問題は、コマンドをタブにバインドしたいので、キーバインディングが実際に関数自体を返すため、無限ループが発生することです。どういうわけか、モードの「元の」キーマップでバインディングを調べることができますか? –

+0

あなたはそうです、これは簡単にはできません。可能な回避策については、私の編集を参照してください。 – Francesco

+0

マイナーモードの場合、 'minor-mode-map-alist'からエントリを削除する必要はありません。マイナーモード変数(通称' my-complete-mode')をコールのまわりでlet-bindすることができます'key-binding'に変換します。 – Stefan

2

それはあなたが達成できることが可能です特別な回避策は一切ありません。ほとんどのモードでは、TABはデフォルトで字下げを行いますが、グローバル変数tab-always-indent'completeに設定すると、最初に完了しようとし、完了できない場合にはインデントします。 TABが主なモードの別のコマンドにバインドされていると、あなたは運が悪いかもしれませんが、これは通常とてもうまくいきます。

これが必要なモードで動作する場合は、適切なすべてのバッファー(おそらくモードフックを使用)でカスタム完了関数をリストの先頭に追加する必要があります。completion-at-point-functionscompletion-at-pointコマンドは、いずれかがnilを返すまで、completion-at-point-functionsにリストされている各関数を呼び出します。したがって、カスタム補完関数を既存の振る舞いに "落とす"ために必要なのは、戻り値nilです。

これは100%の回答ではありませんが、作業しているメジャーモードが通常のガイドラインに従って書かれている場合は、最もクリーンな方法です。

+0

これは非常に良い点ですが、マイナーモードの多くはYASnippetや自動補完のような 'TAB'キーを再バインドできます。そのようなマイナーモードが '(setq tab-always-indent 'complete)'を守るかどうか知っていますか? – Francesco

+0

これらのマイナーモードをオーバーライドする場合は、そのようにします。そのために 'indent-for-tab-command'を使用することはできます。ところで、 'tab-always-indent'の' complete'値がyasnippetで動作しない場合は、これをバグ(yasnippet)として報告したいかもしれません。 – Stefan

3

ここでは、条件付きでキーバインディングを定義するためにEmacs key binding fallbackに基づいて書きました。これは、指定されたマイナーモードにキーバインドを追加しますが、条件が真でない場合は、以前に割り当てられたアクションが実行されます。

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

その後、私は私は場合にのみ、TABのための特別なバインディングを使用するには、次のようなものを行うことができますoutline-minor-modeのヘッダーに表示されます。そうでなければ、私のデフォルトのアクション(私はインデントとyasnippetsの両方を持っている)が実行されます。

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

はところで、ここでは別のソリューションです:

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

このトリックは何度も私を救ってくれました。 –

0

define-keyは引用符で囲まれた文字列またはこの例のような対話型のラムダを受け入れることができます。

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

ラムダは、my-tab-completionのような名前付き関数に置き換えて、より効果的に使用できます。定義キーのドキュメンテーション文字列から

(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'.) 
関連する問題