blob: 953b5f79ba5b0693c879cdaa600ffb811d3405b8 (
plain)
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
|
;;; prog-json.el --- JSON Editing, Formatting, and jq Integration -*- lexical-binding: t; coding: utf-8; -*-
;; Author: Craig Jennings <c@cjennings.net>
;;; Commentary:
;;
;; Layer: 3 (Domain Workflow).
;; Category: D/P.
;; Load shape: eager.
;; Eager reason: none necessary; currently eager but should load by JSON major
;; mode (Phase 6 deferral candidate).
;; Top-level side effects: one add-hook, package configuration via use-package.
;; Runtime requires: none (configures packages via use-package).
;; Direct test load: yes.
;;
;; JSON editing with tree-sitter highlighting, one-key formatting, and
;; interactive jq queries against the current buffer.
;;
;; Features:
;; - Tree-sitter: Better syntax highlighting and structural navigation
;; - Formatting: Pretty-print with sorted keys via C-; f
;; - jq: Interactive jq REPL against current JSON buffer
;;
;; Workflow:
;; 1. Open .json file → json-ts-mode with tree-sitter highlighting
;; 2. C-; f → Format/pretty-print the buffer
;; 3. C-c C-q → Open jq interactive buffer to query/transform JSON
;;; Code:
(defvar json-ts-mode-map)
;; -------------------------------- JSON Mode ----------------------------------
;; tree-sitter mode for JSON files (built-in, Emacs 29+)
;; NOTE: No :mode directive here — treesit-auto (in prog-general.el) handles
;; the auto-mode-alist mapping and auto-installs the grammar on first use.
(use-package json-ts-mode
:ensure nil
:defer t)
;; -------------------------------- Formatting ---------------------------------
;; pretty-print with sorted keys, bound to standard format key
(defun cj/--json-format-region (program &rest args)
"Replace the buffer with PROGRAM ARGS run over its contents, via argv.
Runs PROGRAM (with ARGS) on the whole buffer through
`call-process-region' — no shell, so no quoting or word-splitting.
The buffer is replaced only when PROGRAM exits zero; on a non-zero
exit the buffer is left untouched and an error is signalled with
the program's stderr text. Point is preserved as closely as the
reformatted size allows. Returns t on success."
(let* ((point (point))
(src (current-buffer))
(out (generate-new-buffer " *json-format-out*"))
(status (apply #'call-process-region
(point-min) (point-max) program
nil out nil args)))
(unwind-protect
(if (and (integerp status) (zerop status))
(progn
(with-current-buffer src
(replace-buffer-contents out)
(goto-char (min point (point-max))))
t)
(user-error "%s failed: %s" program
(string-trim (with-current-buffer out (buffer-string)))))
(kill-buffer out))))
(defun cj/json-format-buffer ()
"Format the current JSON buffer with sorted keys.
Uses jq if available for reliable formatting, otherwise falls
back to the built-in `json-pretty-print-buffer-ordered'."
(interactive)
(if (executable-find "jq")
(cj/--json-format-region "jq" "--sort-keys" ".")
(json-pretty-print-buffer-ordered)))
(defun cj/json-setup ()
"Set up JSON buffer keybindings."
(local-set-key (kbd "C-; f") #'cj/json-format-buffer))
(add-hook 'json-ts-mode-hook #'cj/json-setup)
;; --------------------------------- jq Mode -----------------------------------
;; interactive jq queries against JSON buffers
(use-package jq-mode
:defer t
:bind (:map json-ts-mode-map
("C-c C-q" . jq-interactively)))
(provide 'prog-json)
;;; prog-json.el ends here.
|