2013-04-18 14 views
5

Tôi đang cố gắng viết thực hiện hoàn thành tab tùy chỉnh để thử một loạt các lần hoàn thành khác nhau tùy thuộc vào vị trí của điểm. Tuy nhiên, nếu không có điều kiện để hoàn thành được đáp ứng, tôi muốn tab để làm những gì bao giờ chế độ hiện tại ban đầu dự định nó để làm.Elisp: Thay đổi điều chỉnh keybinding

Something như thế này:

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

Hiện nay tôi đang kiểm tra cho các chế độ cụ thể và làm đúng cho chế độ đó, nhưng tôi thực sự muốn có một giải pháp mà chỉ làm những điều đúng đắn mà không có tôi cần phải rõ ràng thêm một điều kiện cho chế độ cụ thể đó.

Bất kỳ ý tưởng nào về cách thực hiện việc này?

Cảm ơn!/Erik

+1

Xem tài liệu cho 'xác định-key' và 'địa phương-set-key'. Điều này thường được thực hiện bằng cách sửa đổi khóa-bản đồ cụ thể theo chế độ. –

Trả lời

2

Bạn có thể sử dụng các chức năng như key-binding (hoặc các biến thể cụ thể hơn global-key-binding, minor-mode-key-bindinglocal-key-binding) để thăm dò các keymap hoạt động cho các ràng buộc.

Ví dụ:

(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 

Một cách để tránh vòng lặp vô hạn nếu lệnh của bạn chắc chắn sẽ TAB có thể đặt liên kết của bạn trong một chế độ nhỏ, và tạm thời tắt sơ đồ bàn phím của nó khi nhìn cho TAB ràng buộc:

(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

Cảm ơn câu trả lời của bạn! Vấn đề ở đây là tôi muốn ràng buộc lệnh vào tab, vì vậy khóa-ràng buộc sẽ thực sự trả về chính hàm đó, và do đó gây ra một vòng lặp vô hạn. Tôi có thể tra cứu ràng buộc trong bản đồ khóa "gốc" cho chế độ bằng cách nào đó không? –

+0

Bạn nói đúng, điều này không dễ thực hiện. Xem bản chỉnh sửa của tôi để biết cách giải quyết khả thi. – Francesco

+0

Đối với một chế độ nhỏ, bạn không cần phải xóa mục nhập từ 'minor-mode-map-alist': bạn chỉ có thể cho phép liên kết biến chế độ nhỏ (còn gọi là' my-complete-mode') xung quanh cuộc gọi để 'khóa-ràng buộc'. – Stefan

2

có thể là bạn có thể đạt được điều này mà không có bất kỳ cách giải quyết đặc biệt nào cả. Trong hầu hết các chế độ TAB chỉ cần thụt đầu dòng theo mặc định, nhưng nếu bạn đặt biến toàn cầu tab-always-indent thành 'complete, nó sẽ cố hoàn thành trước và thụt lề nếu không thể hoàn thành. Điều này thường hoạt động thực sự tốt, mặc dù nếu TAB bị ràng buộc với lệnh khác trong một trong các chế độ chính của bạn, bạn có thể không may mắn.

Nếu hoạt động ở chế độ bạn cần, bạn chỉ cần thêm hàm hoàn thành tùy chỉnh vào trước danh sách completion-at-point-functions trong tất cả các bộ đệm áp dụng (có thể sử dụng móc chế độ). Lệnh completion-at-point gọi cho mỗi chức năng được liệt kê trong completion-at-point-functions cho đến khi một trong số chúng trả về không nil, vì vậy tất cả những gì bạn cần làm để có chức năng hoàn thành tùy chỉnh "rơi qua" đối với hành vi hiện tại là trả lại nil từ đó.

Đây không phải là câu trả lời 100% cho câu hỏi, nhưng nếu các chế độ chính bạn đang làm việc được viết theo nguyên tắc thông thường thì đó có thể là cách sạch nhất.

+0

Đó là một điểm rất tốt, nhưng rất nhiều chế độ nhỏ có thể rebind phím 'TAB', như YASnippet hoặc tự động hoàn thành ví dụ. Bạn có biết nếu các chế độ nhỏ như vậy tôn trọng '(setq tab-always-indent 'complete)'? – Francesco

+0

Nếu bạn muốn ghi đè các chế độ nhỏ đó, hãy làm như vậy. Bạn vẫn có thể sử dụng 'indent-for-tab-command' cho điều đó. BTW, nếu giá trị 'hoàn thành' của' tab-luôn-indent' không hoạt động với đoạn mã, bạn có thể báo cáo lỗi này dưới dạng lỗi (trong đoạn mã). – Stefan

3

Đây là macro mà tôi đã viết dựa trên Emacs key binding fallback để xác định keybinding có điều kiện. Nó cho biết thêm keybinding sang chế độ nhỏ quy định nhưng nếu tình trạng này là không đúng sự thật, hành động giao trước đây được thực hiện:

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

Sau đó, tôi có thể làm những việc như sau để sử dụng các ràng buộc đặc biệt cho TAB chỉ khi tôi trên một tiêu đề trong chế độ phác thảo-nhỏ.Nếu không hành động mặc định của tôi (Tôi có cả thụt lề và yasnippets) được thực hiện:

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

BTW, đây là một giải pháp khác:

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

Bí quyết này đã cứu tôi rất nhiều lần. –

0

define-key thể chấp nhận chuỗi trích dẫn hoặc lambdas tương tác như trong ví dụ này.

;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 có thể được thay thế bằng các chức năng có tên như hoàn thành tab của tôi và được sử dụng hiệu quả hơn.

Từ docstring xác định-key của (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'.)