From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Simen =?UTF-8?Q?Heggest=C3=B8yl?= Newsgroups: gmane.emacs.bugs Subject: bug#21798: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements Date: Sat, 31 Oct 2015 09:46:02 +0100 Message-ID: <1446281162.2607.0@smtp.gmail.com> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="=-b4IqqtSK8ay5FwlaWkkt" X-Trace: ger.gmane.org 1446281248 20880 80.91.229.3 (31 Oct 2015 08:47:28 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 31 Oct 2015 08:47:28 +0000 (UTC) To: 21798@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sat Oct 31 09:47:12 2015 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZsRod-000190-6n for geb-bug-gnu-emacs@m.gmane.org; Sat, 31 Oct 2015 09:47:11 +0100 Original-Received: from localhost ([::1]:54737 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZsRoc-00040r-9r for geb-bug-gnu-emacs@m.gmane.org; Sat, 31 Oct 2015 04:47:10 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:50980) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZsRoX-00040m-Gk for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:47:07 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZsRoU-0005O0-5U for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:47:05 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:55998) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZsRoU-0005Nw-2D for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:47:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1ZsRoT-00039r-Qu for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:47:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Simen =?UTF-8?Q?Heggest=C3=B8yl?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 31 Oct 2015 08:47:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 21798 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.144628119612104 (code B ref -1); Sat, 31 Oct 2015 08:47:01 +0000 Original-Received: (at submit) by debbugs.gnu.org; 31 Oct 2015 08:46:36 +0000 Original-Received: from localhost ([127.0.0.1]:46706 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZsRo2-000399-Ql for submit@debbugs.gnu.org; Sat, 31 Oct 2015 04:46:35 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:40872) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZsRnh-00038c-S8 for submit@debbugs.gnu.org; Sat, 31 Oct 2015 04:46:33 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZsRnf-0005Kh-FD for submit@debbugs.gnu.org; Sat, 31 Oct 2015 04:46:13 -0400 Original-Received: from lists.gnu.org ([2001:4830:134:3::11]:52297) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZsRnf-0005Kd-CF for submit@debbugs.gnu.org; Sat, 31 Oct 2015 04:46:11 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:50877) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZsRnd-0003yb-Fr for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:46:11 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZsRna-0005KP-4F for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:46:09 -0400 Original-Received: from mail-wi0-x22a.google.com ([2a00:1450:400c:c05::22a]:38003) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZsRnZ-0005KL-QV for bug-gnu-emacs@gnu.org; Sat, 31 Oct 2015 04:46:06 -0400 Original-Received: by wicll6 with SMTP id ll6so23480654wic.1 for ; Sat, 31 Oct 2015 01:46:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:subject:to:message-id:mime-version:content-type; bh=zyQlVlgyJEk8cBTCuJtVl8M4ogSm8+fSb8t2CHeuhYw=; b=mPm0ljRCA9RM6ac6fNTJSjwkY/nGsrKuDXtgd9vTk3CUjZBslI+Cf37xj/Mvh4jbN1 rzwkeSGMe0mpsZjZ+ruUoEIUONy+mcveZAA54IdFC0hDMDPMFBFYHxAD6wj87QJybkG6 1vc7n6Ejl/NC8XyBKZysIElTfmZqc756sA7MvqNJVM03cRSwVWGt/fb9Wbj01RcVOsqy XiDFfhOw5CaIs/x68/sXkBCopqcbk6Jc9HOmlCVxyWI4lhtOFCw/J1y7sUfbnpNV+mkL BRHHUZpm7XoIEJ5BIsSqnmC5KPDjyKdrvQWr7gK9jDz4xUPjPN/BXEVIvfpP2m10x4w+ ujww== X-Received: by 10.194.61.13 with SMTP id l13mr15307014wjr.76.1446281165048; Sat, 31 Oct 2015 01:46:05 -0700 (PDT) Original-Received: from [192.168.101.25] ([77.40.215.202]) by smtp.gmail.com with ESMTPSA id c67sm6831605wmh.11.2015.10.31.01.46.03 for (version=TLSv1/SSLv3 cipher=OTHER); Sat, 31 Oct 2015 01:46:04 -0700 (PDT) X-Mailer: geary/0.10.0 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:108224 Archived-At: --=-b4IqqtSK8ay5FwlaWkkt Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: quoted-printable Sometimes when working with a JSON structure, one needs to find the path to a particular element. That can be tiresome to do manually, especially if the structure is deep. The proposed patch extends json.el with the ability to retrieve the path to a particular JSON element. See the following video for an example of how it can be used by an interactive command, to show the path to the JSON element at point: http://folk.uio.no/simenheg/json-mode.webm The patch follows! -- Simen From f6ddd3b797d6b0d92a1ffa0f5db59543ac7cdc11 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Simen=3D20Heggest=3DC3=3DB8yl?=3D Date: Sun, 25 Oct 2015 14:44:59 +0100 Subject: [PATCH] Add support for retrieving paths to JSON elements Add support for retrieving the path to a JSON element. This can for instance be useful to retrieve paths in deeply nested JSON structures. * lisp/json.el (json-path-to-position): New function for retrieving the path to a JSON object at a given position. (json--path-to-position, json--path): New variables, used internally by `json-path-to-position'. (json-read-object, json-read-array): Respect `json--path-to-position'. * test/automated/json-tests.el (test-json-path-to-position-with-objects) (test-json-path-to-position-with-arrays): New tests for `json-path-to-position'. --- lisp/json.el | 67 ++++++++++++++++++++++++++++++++++++++++++-- test/automated/json-tests.el | 14 +++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lisp/json.el b/lisp/json.el index b23d12a..2c82eee 100644 --- a/lisp/json.el +++ b/lisp/json.el @@ -196,6 +196,49 @@ 'json-end-of-file +;;; Paths + +(defvar json--path-to-position nil + "When set to a position, `json-read' will return the path to +the JSON element at the specified position, instead of returning +the parsed JSON object.") + +(defvar json--path '() + "Used internally by `json-path-to-position' to keep track of +the path during recursive calls to `json-read'.") + +(defun json-path-to-position (position &optional string) + "Return the path to the JSON element at POSITION. + +When STRING is provided, return the path to the position in the +string, else to the position in the current buffer. + +The return value is a property list with the following +properties: + +:path -- A list of strings and numbers forming the path to + the JSON element at the given position. Strings + denote object names, while numbers denote array + indexes. + +:match-start -- Position where the matched JSON element begins. + +:match-end -- Position where the matched JSON element ends. + +This can for instance be useful to determine the path to a JSON +element in a deeply nested structure." + (save-excursion + (unless string + (goto-char (point-min))) + (let* ((json--path '()) + (json--path-to-position position) + (path (catch :json-path + (if string + (json-read-from-string string) + (json-read))))) + (when (plist-get path :path) + path)))) + ;;; Keywords (defvar json-keywords '("true" "false" "null") @@ -399,11 +442,21 @@ json-read-object (while (not (char-equal (json-peek) ?})) (json-skip-whitespace) (setq key (json-read-string)) + (when json--path-to-position + (push key json--path)) (json-skip-whitespace) (if (char-equal (json-peek) ?:) (json-advance) (signal 'json-object-format (list ":" (json-peek)))) - (setq value (json-read)) + (json-skip-whitespace) + (let ((start (point))) + (setq value (json-read)) + (when json--path-to-position + (when (< start json--path-to-position (+ (point) 1)) + (throw :json-path (list :path (nreverse json--path) + :match-start start + :match-end (point)))) + (pop json--path))) (setq elements (json-add-to-object elements key value)) (json-skip-whitespace) (unless (char-equal (json-peek) ?}) @@ -509,7 +562,17 @@ json-read-array ;; read values until "]" (let (elements) (while (not (char-equal (json-peek) ?\])) - (push (json-read) elements) + (json-skip-whitespace) + (when json--path-to-position + (push (length elements) json--path)) + (let ((start (point))) + (push (json-read) elements) + (when json--path-to-position + (when (< start json--path-to-position (+ (point) 1)) + (throw :json-path (list :path (nreverse json--path) + :match-start start + :match-end (point)))) + (pop json--path))) (json-skip-whitespace) (unless (char-equal (json-peek) ?\]) (if (char-equal (json-peek) ?,) diff --git a/test/automated/json-tests.el b/test/automated/json-tests.el index d1b7a2f..e0672dd 100644 --- a/test/automated/json-tests.el +++ b/test/automated/json-tests.el @@ -49,5 +49,19 @@ (should (equal (json-read-from-string=20 "\"\\nasd\\u0444\\u044b\\u0432fgh\\t\"") "\nasd=D1=84=D1=8B=D0=B2fgh\t"))) +(ert-deftest test-json-path-to-position-with-objects () + (let* ((json-string "{\"foo\": {\"bar\": {\"baz\": \"value\"}}}") + (matched-path (json-path-to-position 32 json-string))) + (should (equal (plist-get matched-path :path) '("foo" "bar" "baz"))) + (should (equal (plist-get matched-path :match-start) 25)) + (should (equal (plist-get matched-path :match-end) 32)))) + +(ert-deftest test-json-path-to-position-with-arrays () + (let* ((json-string "{\"foo\": [\"bar\", [\"baz\"]]}") + (matched-path (json-path-to-position 20 json-string))) + (should (equal (plist-get matched-path :path) '("foo" 1 0))) + (should (equal (plist-get matched-path :match-start) 18)) + (should (equal (plist-get matched-path :match-end) 23)))) + (provide 'json-tests) ;;; json-tests.el ends here -- 2.6.1 = --=-b4IqqtSK8ay5FwlaWkkt Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable Sometimes when working with a JSON structure, one needs to find the
pat= h to a particular element. That can be tiresome to do manually,
e= specially if the structure is deep.

The proposed p= atch extends json.el with the ability to retrieve the
path to a p= articular JSON element.

See the following video fo= r an example of how it can be used by an
interactive command, to = show the path to the JSON element at point:


The patch follows!
=
-- Simen


From f6ddd3= b797d6b0d92a1ffa0f5db59543ac7cdc11 Mon Sep 17 00:00:00 2001
From:= =3D?UTF-8?q?Simen=3D20Heggest=3DC3=3DB8yl?=3D <simenheg@gmail.com>
Date: Sun, 25 Oct 2015 14:44:59 +0100
Subject: [PATCH] A= dd support for retrieving paths to JSON elements

A= dd support for retrieving the path to a JSON element. This can for
instance be useful to retrieve paths in deeply nested JSON
stru= ctures.

* lisp/json.el (json-path-to-position): Ne= w function for retrieving the
path to a JSON object at a given po= sition.
(json--path-to-position, json--path): New variables, used= internally by
`json-path-to-position'.
(json-read-obje= ct, json-read-array): Respect `json--path-to-position'.

* test/automated/json-tests.el (test-json-path-to-position-with-objec= ts)
(test-json-path-to-position-with-arrays): New tests for
=
`json-path-to-position'.
---
 lisp/json.el = | 67 ++++++++++++++++++++++++++++++++++++++++++--
&= nbsp;test/automated/json-tests.el | 14 +++++++++
 2 files ch= anged, 79 insertions(+), 2 deletions(-)

diff --git= a/lisp/json.el b/lisp/json.el
index b23d12a..2c82eee 100644
--- a/lisp/json.el
+++ b/lisp/json.el
@@ -196,6 += 196,49 @@ 'json-end-of-file

 =0C
+;;; Paths
+
+(defvar json--path-to-positi= on nil
+ "When set to a position, `json-read' will return the pa= th to
+the JSON element at the specified position, instead of ret= urning
+the parsed JSON object.")
+
+(defvar = json--path '()
+ "Used internally by `json-path-to-position' to = keep track of
+the path during recursive calls to `json-read'.")<= /div>
+
+(defun json-path-to-position (position &optional= string)
+ "Return the path to the JSON element at POSITION.
+
+When STRING is provided, return the path to the positio= n in the
+string, else to the position in the current buffer.
+
+The return value is a property list with the following<= /div>
+properties:
+
+:path -- A list of str= ings and numbers forming the path to
+ the JSON el= ement at the given position. Strings
+ denote obj= ect names, while numbers denote array
+ indexes.
+
+:match-start -- Position where the matched JSON eleme= nt begins.
+
+:match-end -- Position where the matche= d JSON element ends.
+
+This can for instance be useful= to determine the path to a JSON
+element in a deeply nested stru= cture."
+ (save-excursion
+ (unless string
+ (goto-char (point-min)))
+ (let* ((json--path '())
+ (json--path-to-position position)
+ = (path (catch :json-path
+ (if string
+ (json-read-from-string string)
+ = (json-read)))))
+ (when (plist-get path :path)<= /div>
+ path))))
+
 ;;; Keywords
<= div>
 (defvar json-keywords '("true" "false" "null")
@@ -399,11 +442,21 @@ json-read-object
     = ;(while (not (char-equal (json-peek) ?}))
      &n= bsp;(json-skip-whitespace)
       (setq key (= json-read-string))
+ (when json--path-to-position
= + (push key json--path))
       (json-= skip-whitespace)
       (if (char-equal (json= -peek) ?:)
           (json-advance= )
         (signal 'json-object-format (= list ":" (json-peek))))
- (setq value (json-read))
+ (json-skip-whitespace)
+ (let ((start (point)))
+ (setq value (json-read))
+ (when json--path= -to-position
+ (when (< start json--path-to-position = (+ (point) 1))
+ (throw :json-path (list :path (nrever= se json--path)
+ :match-start = start
+ :match-end (point))))<= /div>
+ (pop json--path)))
      &nbs= p;(setq elements (json-add-to-object elements key value))
  =      (json-skip-whitespace)
      &= nbsp;(unless (char-equal (json-peek) ?})
@@ -509,7 +562,17 @@ jso= n-read-array
   ;; read values until "]"
&nbs= p;  (let (elements)
     (while (not (char-eq= ual (json-peek) ?\]))
- (push (json-read) elements)
+ (json-skip-whitespace)
+ (when json--path-to-positi= on
+ (push (length elements) json--path))
+ = (let ((start (point)))
+ (push (json-read) elements)
+ (when json--path-to-position
+ (when (<= ; start json--path-to-position (+ (point) 1))
+ (throw= :json-path (list :path (nreverse json--path)
+ = :match-start start
+ = :match-end (point))))
+ (pop json--path)))
       (json-skip-whitespace)
  &n= bsp;    (unless (char-equal (json-peek) ?\])
  &nb= sp;      (if (char-equal (json-peek) ?,)
diff --gi= t a/test/automated/json-tests.el b/test/automated/json-tests.el
i= ndex d1b7a2f..e0672dd 100644
--- a/test/automated/json-tests.el
+++ b/test/automated/json-tests.el
@@ -49,5 +49,19 @@
   (should (equal (json-read-from-string "\"\\nasd\\u0444= \\u044b\\u0432fgh\\t\"")
           = ;       "\nasd=D1=84=D1=8B=D0=B2fgh\t")))

+(ert-deftest test-json-path-to-position-with-objects ()
+= (let* ((json-string "{\"foo\": {\"bar\": {\"baz\": \"value\"}}}")
+ (matched-path (json-path-to-position 32 json-string)))
+ (should (equal (plist-get matched-path :path) '("foo" "bar" "baz"))= )
+ (should (equal (plist-get matched-path :match-start) 25))<= /div>
+ (should (equal (plist-get matched-path :match-end) 32))))
+
+(ert-deftest test-json-path-to-position-with-arrays ()=
+ (let* ((json-string "{\"foo\": [\"bar\", [\"baz\"]]}")
<= div>+ (matched-path (json-path-to-position 20 json-string)))
<= div>+ (should (equal (plist-get matched-path :path) '("foo" 1 0)))
=
+ (should (equal (plist-get matched-path :match-start) 18))
+ (should (equal (plist-get matched-path :match-end) 23))))
+
 (provide 'json-tests)
 ;;; json-tests.el = ends here
--
2.6.1

= --=-b4IqqtSK8ay5FwlaWkkt--