I was working with NPM package.json files a lot lately and I often found myself saving them in an unparseable state. json-ts-mode highlights syntax errors in yellow but it wasn’t enough.
I didn’t want to use flymake-eslint becuase it requires having the jsonlint binary in the PATH and I just wanted a simple Lisp solution.
The code tries to parse the current buffer on save using Emacs’ built-in json-parse-string and moves the cursor to the location of the parsing error if it fails.
The below code naively assumes that the saved buffer is always the current buffer, which may very well not be the case (e.g. (save-some-buffers)).
It also probably won’t save JSON5 files which have // comments inside
because json-parse-string won’t handle that.
(defun rtz/json-parse-pre ()
(interactive)
(if (eq major-mode 'json-ts-mode)
(condition-case err
(progn
(json-parse-string
(buffer-substring-no-properties
(point-min)
(point-max)))
nil)
(json-parse-error
(goto-char (nth 3 err)) (error err)))))
(setq write-file-functions '(rtz/json-parse-pre))
I find the various linters and checkers a bit too intrusive while I’m trying to code - I prefer to just have a check when I stop fiddling with the code and save it. So I have these checks run in after-save-hook - if there are errors, I get a popup otherwise nothing and all is good:
;; ** syntax checking on file save: (defun bh/check-syntax () "Check syntax for various languages." (when (eq major-mode 'emacs-lisp-mode) (ignore-errors (kill-buffer byte-compile-log-buffer)) (let ((byte-compile-warnings '(not free-vars obsolete unresolved))) (unless (byte-compile-file buffer-file-name) (pop-to-buffer byte-compile-log-buffer)))) (when (eq major-mode 'sh-mode) (compile (format "bash -n %s && shellcheck -f gcc %s" buffer-file-name buffer-file-name) t)) (when (eq major-mode 'ruby-mode) (compile (format "ruby -c %s" buffer-file-name) t)) (when (eq major-mode 'python-mode) (compile (format "python -B -m py_compile %s" buffer-file-name) t)) (when (eq major-mode 'awk-mode) (compile (format "AWKPATH=$PATH gawk --lint --source 'BEGIN { exit(0) } END { exit(0) }' --file %s" buffer-file-name) t)))
(add-hook 'after-save-hook #'bh/check-syntax)
I don’t work much with json files but I daresay the idea could be extended to them. Sorry about the crappy elisp.