summaryrefslogtreecommitdiff
path: root/.config/emacs/site-lisp/live-jq.el
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)