From 38db5aa9fbf0f60dce63b83cab8f028bfcd152bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Wed, 22 Aug 2012 22:56:30 +0100 Subject: [PATCH 1/4] Refactor: remove horrible 'yas--trigger-key-for-fallback' and rework 'yas--fallback' --- yasnippet.el | 70 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index c65aa2f..9535d32 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -2257,18 +2257,15 @@ expand immediately. Common gateway for end (yas--template-expand-env yas--current-template))))) -(defun yas--trigger-key-for-fallback () - ;; When `yas-trigger-key' is it correctly overrides - ;; org-mode's , for example and searching for fallbacks - ;; correctly returns `org-cycle'. However, most other modes bind - ;; "TAB" (which is translated from ), and calling - ;; (key-binding "TAB") does not place return that command into - ;; our command-2 local. So we cheat. - ;; - (if (string= yas-trigger-key "") - "TAB" - yas-trigger-key)) - +;; Apropos the trigger key and the fallback binding: +;; +;; When `yas-trigger-key' is it correctly overrides +;; org-mode's , for example and searching for fallbacks +;; correctly returns `org-cycle'. However, most other modes bind +;; "TAB" (which is translated from ), and calling +;; (key-binding "TAB") does not place return that command into +;; our command-2 local. So we cheat. +;; (defun yas--fallback (&optional from-trigger-key-p) "Fallback after expansion has failed. @@ -2280,25 +2277,13 @@ Common gateway for `yas-expand-from-trigger-key' and ((eq yas-fallback-behavior 'call-other-command) (let* ((yas-minor-mode nil) (yas--direct-keymaps nil) - (yas-trigger-key (yas--trigger-key-for-fallback)) - (keys-1 (this-command-keys-vector)) - (keys-2 (and yas-trigger-key - from-trigger-key-p - (stringp yas-trigger-key) - (read-kbd-macro yas-trigger-key))) - (command-1 (and keys-1 (key-binding keys-1))) - (command-2 (and keys-2 (key-binding keys-2))) - ;; An (ugly) safety: prevents infinite recursion of - ;; yas-expand* calls. - (command (or (and (symbolp command-1) - (not (string-match "yas-expand" (symbol-name command-1))) - command-1) - (and (symbolp command-2) - command-2)))) - (when (and (commandp command) - (not (string-match "yas-expand" (symbol-name command)))) - (setq this-command command) - (call-interactively command)))) + (keys (this-single-command-keys)) + (beyond-yasnippet (or (key-binding keys t) + (key-binding (yas--fallback-translate-input keys) t)))) + (yas--message 4 "Falling back to %s" beyond-yasnippet) + (when (commandp beyond-yasnippet) + (setq this-original-command beyond-yasnippet) + (call-interactively beyond-yasnippet)))) ((and (listp yas-fallback-behavior) (cdr yas-fallback-behavior) (eq 'apply (car yas-fallback-behavior))) @@ -2312,6 +2297,29 @@ Common gateway for `yas-expand-from-trigger-key' and ;; also return nil if all the other fallbacks have failed nil))) +(defun yas--fallback-translate-input (keys) + "Emulate `read-key-sequence', at least what I think it does. + +Keys should be an untranslated key vector. Returns a translated +vector of keys. XXX not working yet" + (let ((retval []) + (i 0)) + (while (< i (length keys)) + (let ((j i) + (translated local-function-key-map)) + (while (and (< j (length keys)) + translated + (keymapp translated)) + (setq translated (aget (remove 'keymap translated) (aref keys j)) + j (1+ j))) + (setq retval (vconcat retval (cond ((symbolp translated) + `[,translated]) + ((vectorp translated) + translated) + (t + (substring keys i j))))) + (setq i j))) + retval)) ;;; Utils for snippet development: From 1774d7cbd09241b61d21bc788ffd4186cc932997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Tue, 6 Nov 2012 13:27:37 +0000 Subject: [PATCH 2/4] Add: easier to run tests --- Rakefile | 5 +++++ yasnippet-tests.el | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/Rakefile b/Rakefile index 1bf0045..c68929c 100644 --- a/Rakefile +++ b/Rakefile @@ -9,6 +9,11 @@ end find_version FileUtils.mkdir_p('pkg') +desc "run tests in batch mode" +task :tests do + $EMACS=ENV["EMACS"] || "emacs" + sh "#{$EMACS} -Q -L . -l yasnippet-tests.el -nw --batch -e yas/ert" +end desc "convert some textmate bundles to yasnippets" task :convert_bundles do diff --git a/yasnippet-tests.el b/yasnippet-tests.el index 62653b0..c3ed30e 100644 --- a/yasnippet-tests.el +++ b/yasnippet-tests.el @@ -455,6 +455,15 @@ TODO: be meaner" ;;; Helpers ;;; +(defun yas/ert () + (interactive) + (with-temp-buffer + (flet ((message (&rest args) + (declare (ignore args)) + nil)) + (ert t (buffer-name (current-buffer))) + (princ (buffer-string))))) + (defun yas-should-expand (keys-and-expansions) (dolist (key-and-expansion keys-and-expansions) From f8366214801b52c7c1cc9c091fa5dcb42d3fd217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Tue, 6 Nov 2012 14:09:51 +0000 Subject: [PATCH 3/4] Fix: correct expectation for this test in batch-mode --- yasnippet-tests.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yasnippet-tests.el b/yasnippet-tests.el index c3ed30e..f95e772 100644 --- a/yasnippet-tests.el +++ b/yasnippet-tests.el @@ -198,6 +198,12 @@ (should (string= (yas--buffer-contents) "if condition\naaa\nelse\nbbb\nend"))))) (ert-deftest another-example-for-issue-271 () + ;; expect this to fail in batch mode since `region-active-p' doesn't + ;; used by `yas-expand-snippet' doesn't make sense in that context. + ;; + :expected-result (if noninteractive + :failed + :passed) (with-temp-buffer (yas-minor-mode 1) (let ((snippet "\\${${1:1}:`yas/selected-text`}")) From cd70010b83bc2c92afcdfec7223ef84ddba285e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Tue, 6 Nov 2012 14:12:18 +0000 Subject: [PATCH 4/4] Closes #296: No longer use customizable `yas-trigger-key` and friends. Use keymaps that the user can customise with `define-key` like all the other modes --- yasnippet-tests.el | 16 ++-- yasnippet.el | 179 +++++++++++---------------------------------- 2 files changed, 49 insertions(+), 146 deletions(-) diff --git a/yasnippet-tests.el b/yasnippet-tests.el index f95e772..ca80346 100644 --- a/yasnippet-tests.el +++ b/yasnippet-tests.el @@ -444,21 +444,19 @@ TODO: be meaner" (yas-minor-mode 1) (should (eq (key-binding (yas--read-keybinding "")) 'yas-expand)) (yas-expand-snippet "$1 $2 $3") - (dolist (k (if (listp yas-next-field-key) - yas-next-field-key - (list yas-next-field-key))) - (should (eq (key-binding (yas--read-keybinding k)) 'yas-next-field-or-maybe-expand))) - (dolist (k (if (listp yas-prev-field-key) - yas-prev-field-key - (list yas-prev-field-key))) - (should (eq (key-binding (yas--read-keybinding k)) 'yas-prev-field))))) + (should (eq (key-binding [(tab)]) 'yas-next-field-or-maybe-expand)) + (should (eq (key-binding (kbd "TAB")) 'yas-next-field-or-maybe-expand)) + (should (eq (key-binding [(shift tab)]) 'yas-prev-field)) + (should (eq (key-binding [backtab]) 'yas-prev-field)))) (ert-deftest test-yas-in-org () (with-temp-buffer (org-mode) (yas-minor-mode 1) - (should (eq (key-binding (yas--read-keybinding "")) 'yas-expand)))) + (should (eq (key-binding [(tab)]) 'yas-expand)) + (should (eq (key-binding (kbd "TAB")) 'yas-expand)))) + ;;; Helpers ;;; (defun yas/ert () diff --git a/yasnippet.el b/yasnippet.el index 9535d32..95c9f3a 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -63,7 +63,8 @@ ;; M-x yas-expand ;; ;; Try to expand snippets before point. In `yas-minor-mode', -;; this is bound to `yas-trigger-key' which you can customize. +;; this is normally bound to TAB, but you can customize it in +;; `yas-minor-mode-map'. ;; ;; M-x yas-load-directory ;; @@ -236,81 +237,20 @@ Naturally this is only valid when `yas-indent-line' is `auto'" :type 'boolean :group 'yasnippet) -(defcustom yas-trigger-key "" - "The key bound to `yas-expand' when `yas-minor-mode' is active. - -Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'." - :type 'string - :group 'yasnippet - :set #'(lambda (symbol key) - (let ((old (and (boundp symbol) - (symbol-value symbol)))) - (set-default symbol key) - ;; On very first loading of this defcustom, - ;; `yas-trigger-key' is *not* loaded. - (if (fboundp 'yas--trigger-key-reload) - (yas--trigger-key-reload old))))) - -(defcustom yas-next-field-key '("TAB" "") - "The key to navigate to next field when a snippet is active. - -Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. - -Can also be a list of strings." - :type '(choice (string :tag "String") - (repeat :args (string) :tag "List of strings")) - :group 'yasnippet - :set #'(lambda (symbol val) - (set-default symbol val) - (if (fboundp 'yas--init-yas-in-snippet-keymap) - (yas--init-yas-in-snippet-keymap)))) - - -(defcustom yas-prev-field-key '("" "") - "The key to navigate to previous field when a snippet is active. - -Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. - -Can also be a list of strings." - :type '(choice (string :tag "String") - (repeat :args (string) :tag "List of strings")) - :group 'yasnippet - :set #'(lambda (symbol val) - (set-default symbol val) - (if (fboundp 'yas--init-yas-in-snippet-keymap) - (yas--init-yas-in-snippet-keymap)))) - -(defcustom yas-skip-and-clear-key '("C-d" "" "") - "The key to clear the currently active field. - -Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. - -Can also be a list of strings." - :type '(choice (string :tag "String") - (repeat :args (string) :tag "List of strings")) - :group 'yasnippet - :set #'(lambda (symbol val) - (set-default symbol val) - (if (fboundp 'yas--init-yas-in-snippet-keymap) - (yas--init-yas-in-snippet-keymap)))) - (defcustom yas-triggers-in-field nil - "If non-nil, `yas-next-field-key' can trigger stacked expansions. + "If non-nil, allow stacked expansions (snippets inside snippets). -Otherwise, `yas-next-field-key' just tries to move on to the next -field" +Otherwise `yas-next-field-or-maybe-expand' just moves on to the +next field" :type 'boolean :group 'yasnippet) (defcustom yas-fallback-behavior 'call-other-command - "How to act when `yas-trigger-key' does *not* expand a snippet. + "How to act when `yas-expand' does *not* expand a snippet. - `call-other-command' means try to temporarily disable YASnippet - and call the next command bound to `yas-trigger-key'. + and call the next command bound to whatever key was used to + invoke `yas-expand'. - nil or the symbol `return-nil' mean do nothing. (and `yas-expand' returns nil) @@ -417,28 +357,15 @@ the trigger key itself." :group 'yasnippet) -;;; User can also customize the next defvars - -(defun yas--define-some-keys (keys keymap definition) - "Bind KEYS to DEFINITION in KEYMAP, read with `read-kbd-macro'." - (let ((keys (or (and (listp keys) keys) - (list keys)))) - (dolist (key keys) - (define-key keymap (read-kbd-macro key) definition)))) - -(defun yas--init-yas-in-snippet-keymap () - (setq yas-keymap - (let ((map (make-sparse-keymap))) - (mapc #'(lambda (binding) - (yas--define-some-keys (car binding) map (cdr binding))) - `((,yas-next-field-key . yas-next-field-or-maybe-expand) - (,yas-prev-field-key . yas-prev-field) - ("C-g" . yas-abort-snippet) - (,yas-skip-and-clear-key . yas-skip-and-clear-or-delete-char))) - map))) - -(defvar yas-keymap (yas--init-yas-in-snippet-keymap) - "The keymap active while a snippet expansion is in progress.") +(defvar yas-keymap (let ((map (make-sparse-keymap))) + (define-key map [(tab)] 'yas-next-field-or-maybe-expand) + (define-key map (kbd "TAB") 'yas-next-field-or-maybe-expand) + (define-key map [(shift tab)] 'yas-prev-field) + (define-key map [backtab] 'yas-prev-field) + (define-key map (kbd "C-g") 'yas-abort-snippet) + (define-key map (kbd "C-d") 'yas-skip-and-clear-or-delete-char) + map) + "The active keymap while a snippet expansion is in progress.") (defvar yas-key-syntaxes (list "w" "w_" "w_." "w_.()" "^ ") "List of character syntaxes used to find a trigger key before point. @@ -694,6 +621,8 @@ snippet itself contains a condition that returns the symbol ;; Now for the stuff that has direct keybindings ;; + (define-key map [(tab)] 'yas-expand) + (define-key map (kbd "TAB") 'yas-expand) (define-key map "\C-c&\C-s" 'yas-insert-snippet) (define-key map "\C-c&\C-n" 'yas-new-snippet) (define-key map "\C-c&\C-v" 'yas-visit-snippet-file) @@ -702,20 +631,6 @@ snippet itself contains a condition that returns the symbol (defvar yas-minor-mode-map (yas--init-minor-keymap) "The keymap used when `yas-minor-mode' is active.") -(defun yas--trigger-key-reload (&optional unbind-key) - "Rebind `yas-expand' to the new value of `yas-trigger-key'. - -With optional UNBIND-KEY, try to unbind that key from -`yas-minor-mode-map'." - (when (and unbind-key - (stringp unbind-key) - (not (string= unbind-key ""))) - (define-key yas-minor-mode-map (read-kbd-macro unbind-key) nil)) - (when (and yas-trigger-key - (stringp yas-trigger-key) - (not (string= yas-trigger-key ""))) - (define-key yas-minor-mode-map (read-kbd-macro yas-trigger-key) 'yas-expand))) - (defvar yas--tables (make-hash-table) "A hash table of mode symbols to `yas--table' objects.") @@ -774,15 +689,14 @@ and friends." (define-minor-mode yas-minor-mode "Toggle YASnippet mode. -When YASnippet mode is enabled, the `yas-trigger-key' key expands -snippets of code depending on the major mode. +When YASnippet mode is enabled, `yas-expand', normally bound to +the TAB key, expands snippets of code depending on the major +mode. With no argument, this command toggles the mode. positive prefix argument turns on the mode. Negative prefix argument turns off the mode. -You can customize the key through `yas-trigger-key'. - Key bindings: \\{yas-minor-mode-map}" nil @@ -790,9 +704,6 @@ Key bindings: " yas" :group 'yasnippet (cond (yas-minor-mode - ;; Reload the trigger key - ;; - (yas--trigger-key-reload) ;; Install the direct keymaps in `emulation-mode-map-alists' ;; (we use `add-hook' even though it's not technically a hook, ;; but it works). Then define variables named after modes to @@ -1800,9 +1711,6 @@ loading." ;; Reload the direct keybindings ;; (yas-direct-keymaps-reload) - ;; Reload the trigger-key (shoudn't be needed, but see issue #237) - ;; - (yas--trigger-key-reload) (yas--message 3 "Reloaded everything%s...%s." (if interactive "" " (snippets will load just-in-time)") @@ -2259,12 +2167,10 @@ expand immediately. Common gateway for ;; Apropos the trigger key and the fallback binding: ;; -;; When `yas-trigger-key' is it correctly overrides -;; org-mode's , for example and searching for fallbacks -;; correctly returns `org-cycle'. However, most other modes bind -;; "TAB" (which is translated from ), and calling -;; (key-binding "TAB") does not place return that command into -;; our command-2 local. So we cheat. +;; When `yas-minor-mode-map' binds , that correctly overrides +;; org-mode's , for example and searching for fallbacks correctly +;; returns `org-cycle'. However, most other modes bind "TAB". TODO, +;; improve this explanation. ;; (defun yas--fallback (&optional from-trigger-key-p) "Fallback after expansion has failed. @@ -2275,15 +2181,11 @@ Common gateway for `yas-expand-from-trigger-key' and ;; return nil nil) ((eq yas-fallback-behavior 'call-other-command) - (let* ((yas-minor-mode nil) - (yas--direct-keymaps nil) - (keys (this-single-command-keys)) - (beyond-yasnippet (or (key-binding keys t) - (key-binding (yas--fallback-translate-input keys) t)))) + (let* ((beyond-yasnippet (yas--keybinding-beyond-yasnippet))) (yas--message 4 "Falling back to %s" beyond-yasnippet) - (when (commandp beyond-yasnippet) - (setq this-original-command beyond-yasnippet) - (call-interactively beyond-yasnippet)))) + (assert (or (null beyond-yasnippet) (commandp beyond-yasnippet))) + (setq this-original-command beyond-yasnippet) + (call-interactively beyond-yasnippet))) ((and (listp yas-fallback-behavior) (cdr yas-fallback-behavior) (eq 'apply (car yas-fallback-behavior))) @@ -2297,11 +2199,19 @@ Common gateway for `yas-expand-from-trigger-key' and ;; also return nil if all the other fallbacks have failed nil))) +(defun yas--keybinding-beyond-yasnippet () + "Returns the " + (let* ((yas-minor-mode nil) + (yas--direct-keymaps nil) + (keys (this-single-command-keys))) + (or (key-binding keys t) + (key-binding (yas--fallback-translate-input keys) t)))) + (defun yas--fallback-translate-input (keys) "Emulate `read-key-sequence', at least what I think it does. Keys should be an untranslated key vector. Returns a translated -vector of keys. XXX not working yet" +vector of keys. FIXME not thoroughly tested" (let ((retval []) (i 0)) (while (< i (length keys)) @@ -4259,15 +4169,14 @@ When multiple expressions are found, only the last one counts." "A doc synthethizer for `yas--expand-from-trigger-key-doc'." (let ((fallback-description (cond ((eq yas-fallback-behavior 'call-other-command) - (let* ((yas-minor-mode nil) - (fallback (key-binding (read-kbd-macro (yas--trigger-key-for-fallback))))) + (let* ((fallback (yas--keybinding-beyond-yasnippet))) (or (and fallback (format " call command `%s'." (pp-to-string fallback))) - " do nothing."))) + " do nothing (`yas-expand' doesn't shadow\nanything)"))) ((eq yas-fallback-behavior 'return-nil) ", do nothing.") (t - ", defer to `yas--fallback-behaviour' :-)")))) + ", defer to `yas-fallback-behaviour' (which see)")))) (concat "Expand a snippet before point. If no snippet expansion is possible," fallback-description @@ -4446,10 +4355,6 @@ handle the end-of-buffer error fired in it by calling yas-indent-line yas-also-auto-indent-first-line yas-snippet-revival - yas-trigger-key - yas-next-field-key - yas-prev-field-key - yas-skip-and-clear-key yas-triggers-in-field yas-fallback-behavior yas-choose-keys-first