all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: "Simen Heggestøyl" <simenheg@gmail.com>
To: Dmitry Gutov <dgutov@yandex.ru>
Cc: 21798@debbugs.gnu.org
Subject: bug#21798: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements
Date: Sun, 01 Nov 2015 20:52:33 +0100	[thread overview]
Message-ID: <1446407553.4906.0@smtp.gmail.com> (raw)
In-Reply-To: <5634CEE7.3070200@yandex.ru>


[-- Attachment #1.1: Type: text/plain, Size: 1567 bytes --]

Hi Dmitry, thanks for the feedback!

On Sat, Oct 31, 2015 at 3:23 PM, Dmitry Gutov <dgutov@yandex.ru> wrote:
> The video looks great, but the inline patch lacks indentation, and 
> doesn't apply (dunno if the former is the cause of the latter). 
> Please resend it as an attachment.

Ah, yes, it appear that the whitespace got lost in the email for some
reason. I'll try attaching it to this email.

To test it, I've been using the following interactive function:

  (defun json-mode-show-path ()
    "Show the path to the JSON value under point."
    (interactive)
    (let ((path (json-path-to-position (point))))
      (if path
          (let ((formatted-path
                 (json-mode--format-path (plist-get path :path))))
            (pulse-momentary-highlight-region
             (plist-get path :match-start)
             (plist-get path :match-end))
            (message formatted-path)
            (kill-new formatted-path))
        (message "Not a JSON value"))))

  (defun json-mode--format-path (path)
    "Return PATH formatted as a JSON data selector.
  PATH should be a list of keys, which can be either strings or
  integers."
    (mapconcat (lambda (key) (format "[%S]" key)) path ""))

It's also included it in json-mode.el:
http://folk.uio.no/simenheg/json-mode.el.

> Without trying it, my main concern would be any performance 
> regression to json-read (it's not particularly fast already). Have 
> you done any benchmarking?

I agree that would be bad. I'll try to produce some benchmarks when
I've got some free time on my hands!

-- Simen

[-- Attachment #1.2: Type: text/html, Size: 2528 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-support-for-retrieving-paths-to-JSON-elements.patch --]
[-- Type: text/x-patch, Size: 5710 bytes --]

From f6ddd3b797d6b0d92a1ffa0f5db59543ac7cdc11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
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
 
 \f
 
+;;; 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 "\"\\nasd\\u0444\\u044b\\u0432fgh\\t\"")
                  "\nasdфывfgh\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


  reply	other threads:[~2015-11-01 19:52 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-10-31  8:46 bug#21798: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements Simen Heggestøyl
2015-10-31 14:23 ` Dmitry Gutov
2015-11-01 19:52   ` Simen Heggestøyl [this message]
2015-11-01 23:27     ` Simen Heggestøyl
2015-11-03  2:00       ` Dmitry Gutov
2015-11-06 16:31         ` Simen Heggestøyl
2015-11-06 17:15           ` Dmitry Gutov
2015-11-07 13:23             ` Richard Stallman
2015-11-07 13:43               ` Dmitry Gutov
2015-11-07 18:50             ` Simen Heggestøyl
2015-11-07 19:18               ` Dmitry Gutov
2015-11-08 12:32                 ` Simen Heggestøyl
2015-11-08 12:34                   ` Simen Heggestøyl
2015-11-08 16:16                   ` Dmitry Gutov
2015-11-08 21:12                     ` Simen Heggestøyl
2015-11-09  0:20                       ` Dmitry Gutov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1446407553.4906.0@smtp.gmail.com \
    --to=simenheg@gmail.com \
    --cc=21798@debbugs.gnu.org \
    --cc=dgutov@yandex.ru \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.