diff options
Diffstat (limited to '.config/emacs')
-rw-r--r-- | .config/emacs/site-lisp/live-jq.el | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/.config/emacs/site-lisp/live-jq.el b/.config/emacs/site-lisp/live-jq.el new file mode 100644 index 0000000..f8a0a7f --- /dev/null +++ b/.config/emacs/site-lisp/live-jq.el @@ -0,0 +1,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) |