Skip to content

Commit

Permalink
Merge pull request #278 from redguardtoo/master
Browse files Browse the repository at this point in the history
`js2-print-json-path' added
  • Loading branch information
dgutov committed Nov 3, 2015
2 parents 2a22635 + 98433fb commit 4955584
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
61 changes: 61 additions & 0 deletions js2-mode.el
Expand Up @@ -10878,6 +10878,67 @@ And, if CHECK-ACTIVATION-P is non-nil, use the value of TOKEN."
(js2-check-activation-name s (or token js2-NAME)))
name))

;;; Use AST to extract semantic information

(defun js2-get-element-index-from-array-node (elem array-node &optional hardcoded-array-index)
"Get index of ELEM from ARRAY-NODE or 0 and return it as string."
(let ((idx 0) elems (rlt hardcoded-array-index))
(setq elems (js2-array-node-elems array-node))
(if (and elem (not hardcoded-array-index))
(setq rlt (catch 'nth-elt
(dolist (x elems)
;; We know the ELEM does belong to ARRAY-NODE,
(if (eq elem x) (throw 'nth-elt idx))
(setq idx (1+ idx)))
0)))
(format "[%s]" rlt)))

(defun js2-print-json-path (&optional hardcoded-array-index)
"Print the path to the JSON value under point, and save it in the kill ring.
If HARDCODED-ARRAY-INDEX provided, array index in JSON path is replaced with it."
(interactive "P")
(let (previous-node current-node
key-name
rlt)

;; The `js2-node-at-point' starts scanning from AST root node.
;; So there is no way to optimize it.
(setq current-node (js2-node-at-point))

(while (not (js2-ast-root-p current-node))
(cond
;; JSON property node
((js2-object-prop-node-p current-node)
(setq key-name (js2-prop-node-name (js2-object-prop-node-left current-node)))
(if rlt (setq rlt (concat "." key-name rlt))
(setq rlt (concat "." key-name))))

;; Array node
((or (js2-array-node-p current-node))
(setq rlt (concat (js2-get-element-index-from-array-node previous-node
current-node
hardcoded-array-index)
rlt)))

;; Other nodes are ignored
(t))

;; current node is archived
(setq previous-node current-node)
;; Get parent node and continue the loop
(setq current-node (js2-node-parent current-node)))

(cond
(rlt
;; Clean the final result
(setq rlt (replace-regexp-in-string "^\\." "" rlt))
(kill-new rlt)
(message "%s => kill-ring" rlt))
(t
(message "No JSON path found!")))

rlt))

;;; Indentation support (bouncing)

;; In recent-enough Emacs, we reuse the indentation code from
Expand Down
64 changes: 64 additions & 0 deletions tests/json-path.el
@@ -0,0 +1,64 @@
;;; tests/json-path.el --- Test of using js2-mode AST to print JSON path.

;; Copyright (C) 2015 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)

(ert-deftest js2-json-path-with-actual-array-index ()
(with-temp-buffer
(insert "var a = { hello: [1, 2, [1,3,3,4, { world: { hell: { yes: [1,2, 'test'] } } }]] };")
(js2-mode)
(goto-char 0)
(search-forward "test")
(should (string= (js2-print-json-path) "hello[2][4].world.hell.yes[2]"))))

(ert-deftest js2-json-path-pure-arrays ()
(with-temp-buffer
(insert "var a = [5, 1, 4, [ 4, [1, 2, [1, 3.9, 4, [1, 2, 'test',3]]]], 9, 9];")
(js2-mode)
(goto-char 0)
(search-forward "test")
(should (string= (js2-print-json-path) "[3][1][2][3][2]"))))

(ert-deftest js2-json-path-key-is-numeric ()
(with-temp-buffer
(insert "var b = {hello: {3 : {world: {2: 'test'}}}};")
(js2-mode)
(goto-char 0)
(search-forward "test")
(should (string= (js2-print-json-path) "hello.3.world.2"))))

(ert-deftest js2-json-path-not-found ()
(with-temp-buffer
(insert "console.log('test');")
(js2-mode)
(goto-char 0)
(search-forward "test")
(should (eq (js2-print-json-path) nil))))

(ert-deftest js2-json-path-with-array-index-hardcoded ()
(with-temp-buffer
(insert "var a = { hello: [1, 2, [1,3,3,4, { world: { hell: { yes: [1,2, 'test'] } } }]] };")
(js2-mode)
(goto-char 0)
(search-forward "test")
(should (string= (js2-print-json-path 1) "hello[1][1].world.hell.yes[1]"))
(should (string= (js2-print-json-path 0) "hello[0][0].world.hell.yes[0]"))))

0 comments on commit 4955584

Please sign in to comment.