emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 7184664 20/38: Add a jump to definition command bound to M


From: Dmitry Gutov
Subject: [elpa] master 7184664 20/38: Add a jump to definition command bound to M-.
Date: Wed, 09 Sep 2015 18:41:44 +0000

branch: master
commit 71846649a7b8ff2c3888a554a17c578ea68cd847
Author: ScottyB <address@hidden>
Commit: ScottyB <address@hidden>

    Add a jump to definition command bound to M-.
---
 js2-mode.el         |  119 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/navigation.el |   43 ++++++++++++++++++
 2 files changed, 162 insertions(+), 0 deletions(-)

diff --git a/js2-mode.el b/js2-mode.el
index b885efd..54c33cc 100644
--- a/js2-mode.el
+++ b/js2-mode.el
@@ -1115,6 +1115,7 @@ information."
     (define-key map (kbd "C-c C-o") #'js2-mode-toggle-element)
     (define-key map (kbd "C-c C-w") #'js2-mode-toggle-warnings-and-errors)
     (define-key map [down-mouse-3] #'js2-down-mouse-3)
+    (define-key map (kbd "M-.") #'js2-jump-to-definition)
 
     (define-key map [menu-bar javascript]
       (cons "JavaScript" (make-sparse-keymap "JavaScript")))
@@ -12297,6 +12298,124 @@ it marks the next defun after the ones already 
marked."
     (unless (js2-ast-root-p fn)
       (narrow-to-region beg (+ beg (js2-node-len fn))))))
 
+(defun js2-jump-to-definition ()
+  "Jump to the definition of an object's property, variable or function."
+  (interactive)
+  (ring-insert find-tag-marker-ring (point-marker))
+  (let* ((node (js2-node-at-point))
+         (parent (js2-node-parent node))
+         (prop-names (if (js2-prop-get-node-p parent)
+                         (js2-prop-names-left node)))
+         (name (if (and (js2-name-node-p node)
+                     (not (js2-object-prop-node-p parent)))
+                   (js2-name-node-name node)
+                 (error "Node is not a supported jump node")))
+         (node-init (if (and prop-names (listp prop-names))
+                        (js2-find-property prop-names)
+                      (js2-name-declaration name))))
+    (unless node-init
+      (pop-tag-mark)
+      (error "No jump location found"))
+    (goto-char (js2-node-abs-pos node-init))))
+
+(defun js2-prop-names-left (name-node)
+  "Create a list of all of the names in the property NAME-NODE.
+NAME-NODE must have a js2-prop-get-node as parent.  Only adds
+properties to the left of point.  This is so individual jump
+points can be found for each property in the chain."
+  (let* (name
+         (parent (js2-node-parent name-node))
+         left
+         names)
+    (unless (or (js2-prop-get-node-p parent) (js2-name-node-p name-node))
+      (error "Not a name node or doesn't have a prop-get-node as parent"))
+    (setq name (js2-name-node-name name-node)
+          left (js2-prop-get-node-left parent))
+    (if (and (js2-name-node-p left)
+          (string= name (js2-name-node-name left)))
+        (setq names name)
+      (js2-visit-ast
+       parent
+       (lambda (node endp)
+         (unless endp
+           (if (js2-name-node-p node)
+               (push (js2-name-node-name node) names)
+             t))))
+      names)))
+
+(defun js2-find-property (list-names)
+  "Find the property definition that consists of LIST-NAMES.
+Supports navigation to 'foo.bar = 3' and 'foo = {bar: 3}'."
+  (catch 'prop-found
+    (js2-visit-ast-root
+     js2-mode-ast
+     (lambda (node endp)
+       (let ((parent (js2-node-parent node)))
+         (unless endp
+           (if (or (and (js2-prop-get-node-p node)
+                    (not (or (js2-elem-get-node-p parent) (js2-call-node-p 
parent)))
+                    (equal list-names (js2-build-prop-name-list node)))
+                 (and (js2-name-node-p node)
+                    (js2-object-prop-node-p parent)
+                    (string= (js2-name-node-name node)
+                             (first list-names))))
+               (throw 'prop-found node))
+           t))))))
+
+(defun js2-name-declaration (name)
+  "Return the declaration node for node named NAME."
+  (let* ((node (js2-root-or-node))
+         (scope-def (js2-get-defining-scope node name))
+         (scope (if scope-def (js2-scope-get-symbol scope-def name) nil))
+         (symbol (if scope (js2-symbol-ast-node scope) nil)))
+    (if (not symbol)
+        (js2-get-function-node name scope-def)
+      symbol)))
+
+(defun js2-get-function-name (fn-node)
+  "Return the name of the function FN-NODE.
+Value may be either function name or the variable name that holds
+the function."
+  (let ((parent (js2-node-parent fn-node)))
+    (if (js2-function-node-p fn-node)
+        (or (js2-function-name fn-node)
+          (if (js2-var-init-node-p parent)
+              (js2-name-node-name (js2-var-init-node-target parent)))))))
+
+(defun js2-root-or-node ()
+  "Return the current node or js2-ast-root node."
+  (let ((node (js2-node-at-point)))
+    (if (js2-ast-root-p node)
+        node
+      (js2-node-get-enclosing-scope node))))
+
+(defun js2-build-prop-name-list (prop-node)
+  "Build a list of names from a PROP-NODE."
+  (let* (names
+         left
+         left-node)
+    (unless (js2-prop-get-node-p prop-node)
+      (error "Node is not a property prop-node"))
+    (while (js2-prop-get-node-p prop-node)
+      (push (js2-name-node-name (js2-prop-get-node-right prop-node)) names)
+      (setq left-node (js2-prop-get-node-left prop-node))
+      (when (js2-name-node-p left-node)
+        (setq left (js2-name-node-name left-node)))
+      (setq prop-node (js2-node-parent prop-node)))
+    (append names `(,left))))
+
+(defun js2-get-function-node (name scope)
+  "Return node of function named NAME in SCOPE."
+  (catch 'function-found
+    (js2-visit-ast
+     scope
+     (lambda (node end-p)
+       (when (and (not end-p)
+               (string= name (js2-get-function-name node)))
+         (throw 'function-found node))
+       t))
+    nil))
+
 (provide 'js2-mode)
 
 ;;; js2-mode.el ends here
diff --git a/tests/navigation.el b/tests/navigation.el
new file mode 100644
index 0000000..bbd4861
--- /dev/null
+++ b/tests/navigation.el
@@ -0,0 +1,43 @@
+;;; tests/navigation.el --- Some tests for js2-mode.
+
+;; Copyright (C) 2009, 2011-2013  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'js2-mode)
+
+(cl-defun js2-navigation-helper (buffer-content expected-point &optional 
(point-offset 1))
+  (with-temp-buffer
+    (insert buffer-content)
+    (js2-mode)
+    (goto-char (or (- (point) point-offset)))
+    (js2-jump-to-definition)
+    (should (= (point) expected-point))))
+
+(ert-deftest js2-jump-to-var ()
+  (js2-navigation-helper "var soup = 2; soup" 5))
+
+(ert-deftest js2-jump-to-function ()
+  (js2-navigation-helper "function aFunction() {}; aFunction" 1))
+
+(ert-deftest js2-jump-to-function-parameters ()
+  (js2-navigation-helper "var p1 = 4; function aFunction(p1, p2) {p1};" 32 4))
+
+(ert-deftest js2-jump-to-object-property ()
+  (js2-navigation-helper "var aObject = {prop1: 3, prop2: \"hello\"}; 
aObject.prop1" 16))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]