2012-04-23 11 views
4

私はcomint-modeに基づいて派生モードを書いています。このモードは、コマンドラインプログラム(GRASS gis)へのインタフェースであり、プログラムのコミットモード補完が機能します。私はcompletion-at-point-functionsを介して、プログラムへの引数を完了するためのサポートを追加しようとしています。おもちゃの例です:Emacs完了時点の機能

(setq my-commands 
     '(("ls" 
     ("my-completion-1") 
     ("my-completion-2")) 
     ("mv" 
     ("my-completion-3") 
     ("my-completion-4")))) 


(defun my-completion-at-point() 
    (interactive) 
    (let ((pt (point)) ;; collect point 
     start end) 

    (save-excursion ;; collect the program name 
     (comint-bol) 
     (re-search-forward "\\(\\S +\\)\\s ?")) 
    (if (and (>= pt (match-beginning 1)) 
      (<= pt (match-end 1))) 
     () ;; if we're still entering the command, pass completion on to 
     ;; comint-completion-at-point by returning nil 

     (let ((command (match-string-no-properties 1))) 
     (when (member* command my-commands :test 'string= :key 'car) 
      ;; If the command is one of my-commands, use the associated completions 
      (goto-char pt) 
      (re-search-backward "\\S *") 
      (setq start (point)) 
      (re-search-forward "\\S *") 
      (setq end (point)) 
      (list start end (cdr (assoc command my-commands)) :exclusive 'no)))))) 

(push 'my-completion-at-point completion-at-point-functions) 

これはほとんど動作します。私はプログラム名の正常な完了を得る。ただし、コマンドラインでlsと入力した場合、タブを押すとmy-completion-が挿入され、2つのオプションは表示されません。もう一度、タブを押すとmy-completion-が挿入され、今度はls my-completion-mycompletion-になります。

私の実際のコードには、複数行のコマンドをチェックするためにいくつかの行が含まれていますが、完了コードは変更されていません。このバージョンのコードでは、プログラム名の1つで始まる行にタブが表示されますmy-commandsコマンドを完了するための引数のリストが表示されますが、バッファに何も挿入されず、リスト議論の最初の数文字をタイプすることによって狭められることはありません。

私はこのマニュアルを読んできましたが、completion-at-point関数を書く正しい方法を理解できません。私は何が欠けているすべてのアイデア?

私は簡単にpcompleteを見ましたが、実際には「ドキュメント」を理解しておらず、進歩もありませんでした。

答えて

7

この問題は、startendが、ポイントの引数の境界を返すように見えているようです。私は詳細を確認するためにそれをデバッグするのに十分な時間を費やしませんでしたが、対話的に関数を呼び出すと、startendの同じ値が返されることがわかります。これは、完了UIがあなたが渡した補完テーブルから選択するためにポイントで引数を使用することを知っている。以下に、あなたの関数の最後の部分を変更する

は1つの修正のようだ:

(when (member* command my-commands :test 'string= :key 'car) 
    ;; If the command is one of my-commands, use the associated completions 
    (goto-char pt) 
    (let ((start 
     (save-excursion 
      (skip-syntax-backward "^ ") 
      (point)))) 

    (list start pt (cdr (assoc command my-commands)) :exclusive 'no))))))) 

completion-at-point-functionsの要素として追加したときにこれが期待される結果が得られます。

ここでは、regexp検索の代わりにskip-syntax-backwardを使用しました。これは、この種のことで少し慣れ親しんだElispだと思います。それは、構文クラス "空白"にないものの向こうにポイントを逆方向に移動するように指示するだけです。 skip-syntax関数はpointの値ではなく移動した距離を返すので、save-excursionの最後にpointへの呼び出しを追加する必要があります。

このような関数でregexp検索を使用する場合は、tを第4引数のnoerrorに渡すことをお勧めします。これにより、一致しなかった場合にユーザーにエラーが渡されることはありません。これは、戻り値がnilかどうかを自分で調べなければならないことを意味します。

代わり pushの最後に

、あなたは次のようにadd-hookを使用する場合があります補完機能を追加する:

(add-hook 'completion-at-point-functions 'my-completion-at-point nil t) 

これは、2つの有用なことを行います。それはあなたの関数がそれを追加する前に、フックにすでに存在するかどうかをチェックし、 (第4引数のためにtを渡すことにより、local)は、バッファローカル完了点のフックの値にのみ関数を追加します。 TABキーを押したときに他のすべてのEmacsバッファでこれらの補完を使用したくないので、これはほぼ確実です。

+0

私は複数の投票権を持っていれば、それも得られます!ご回答有難うございます。私がドキュメントを読んでいるとき、後方検索は、skip-syntax-backwardと同じ場所にポイントを残すべきですが、明らかにそうではありません。私もadd-hookを使い始めましたが、関数名を逆にして間違って引用していました。今はすべてが完璧に動作します。再度、感謝します! – Tyler

+0

うれしかったことがうれしい!もっと考えてみると、 're-search-backward'の問題は、後方検索の際にregexp演算子の貪欲さに関係していると考えられます。もちろん、 '' \\ S * ''は空の文字列にマッチしますが、' * 'を' + 'で置き換えても、空白以外の文字を1つ戻して停止します。 –

関連する問題