blob: f8a0a7f763a029c0469134d6f9f86715c36f0d6c (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
;; TODO: ‘defcustom’ this
(defvar live-jq-major-mode
(cond ((fboundp #'json-ts-mode) #'json-ts-mode)
((fboundp #'json-mode) #'json-mode))
"TODO")
(defvar live-jq--input-buffer nil
"The buffer containing the original JSON data.")
(defvar live-jq--preview-buffer "*JQ Preview*"
"The buffer showing the live jq results.")
(defvar live-jq--last-query "")
(defun live-jq--get-json-input ()
"Return the contents of the input buffer as a string."
(with-current-buffer live-jq--input-buffer
(buffer-substring-no-properties (point-min) (point-max))))
(defun live-jq--run-jq (query)
"Run jq QUERY on the input buffer's content and return result string or nil on error."
(let ((json-input (live-jq--get-json-input)))
(with-temp-buffer
(insert json-input)
(let ((exit-code (call-process-region
(point-min) (point-max)
"jq" :delete t nil "--tab" query)))
(when (zerop exit-code)
(buffer-string))))))
(defun live-jq--render-jq-preview (query)
"Update preview buffer with the result or error of jq QUERY."
(let* ((preview-buffer (get-buffer-create live-jq--preview-buffer))
(json-input (live-jq--get-json-input))
(inhibit-read-only t))
(with-current-buffer preview-buffer
(erase-buffer)
(condition-case err
(with-temp-buffer
(insert json-input)
(let ((exit-code (call-process-region
(point-min) (point-max)
"jq" nil preview-buffer nil "--tab" query)))
(when (not (zerop exit-code))
(erase-buffer)
(insert "%s\n%s"
(propertize (format "jq error (exit %d): %s" exit-code query)
'face 'error)
json-input))))
(error
(insert "%s\n%s"
(propertize (format "Error: %s" err) 'face 'error)
input-json)))
(goto-char (point-min))
(when live-jq-major-mode
(funcall live-jq-major-mode))))
(display-buffer live-jq--preview-buffer))
(defun live-jq--minibuffer-update ()
"Update preview as user types."
(let ((query (minibuffer-contents)))
(unless (equal query live-jq--last-query)
(setq live-jq--last-query query)
(live-jq--render-jq-preview query))))
;;;###autoload
(defun live-jq ()
"Prompt for a jq query, show live results, and replace buffer on confirmation."
(interactive)
(unless (executable-find "jq")
(user-error "`jq' not found in PATH."))
(setq live-jq--input-buffer (current-buffer))
(setq live-jq--last-query "")
;; Clean up preview buffer if user cancels with C-g
(let ((minibuffer-setup-hook
(list (lambda ()
;; Add post-command-hook for live preview
(add-hook 'post-command-hook #'live-jq--minibuffer-update nil t)
;; Add abort cleanup
(add-hook 'minibuffer-exit-hook
(lambda ()
(when (get-buffer live-jq--preview-buffer)
(kill-buffer live-jq--preview-buffer)))
nil t)))))
(let ((query (read-from-minibuffer (format-prompt "Query" nil))))
(unwind-protect
(let ((result (live-jq--run-jq query)))
(if result
(with-current-buffer live-jq--input-buffer
(let ((inhibit-read-only t))
(erase-buffer)
(insert result))
(message "jq applied."))
(user-error "Invalid jq query: see *jq-preview* for details")))
;; Cleanup preview buffer after any outcome
(when (get-buffer live-jq--preview-buffer)
(kill-buffer live-jq--preview-buffer))))))
(provide 'live-jq)
|