;;; json-sexp-mode.el --- Edit JSON in s-expression form. ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Taylan Ulrich Bayırlı/Kammer ;; Keywords: data, files ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Activating `json-sexp-mode' will turn the JSON value in the buffer into an ;; s-expression, and arrange for it to be transparently converted back into a ;; (pretty-printed) JSON value whenever the buffer is saved. Adding ;; `json-sexp-mode' to `auto-mode-alist' can provide a fully transparent ;; experience for editing JSON files as s-expressions. ;; ;; The symbol `false' is used to represent JSON's false value, because nil ;; stands for an empty object. (However, t is used for JSON's true value.) ;; ;; Note that `json-sexp-mode' inherits from `emacs-lisp-mode' since it's for ;; editing JSON data as Elisp s-expressions. ;; ;; ;; The following commands can be used to manually convert a region or whole ;; buffer from a JSON value to an s-expression and from an s-expression to a ;; JSON value: ;; ;; `json-sexp-convert-region-to-sexp' ;; `json-sexp-convert-region-to-json' ;; `json-sexp-convert-buffer-to-sexp' ;; `json-sexp-convert-buffer-to-json' ;;; Code: (require 'json) (defun json-sexp-convert-region-to-sexp (start end) "Convert region from JSON to sexps." (interactive "r") (unless (= start end) (let ((data (let ((json-object-type 'alist) (json-false 'false) (json-null 'null)) (json-read-from-string (buffer-substring start end)))) (point (point))) (delete-region start end) (goto-char start) (insert (pp-to-string data)) ;; Only an approximation of our position in the JSON. (goto-char point)))) (defun json-sexp-convert-region-to-json (start end) "Convert region from sexps to JSON." (interactive "r") (unless (= start end) (let ((data (car (read-from-string (buffer-substring start end)))) (point (point))) (delete-region start end) (goto-char start) (insert (let ((json-encoding-pretty-print t) (json-false 'false) (json-null 'null) (json-nil 'object)) (json-encode data))) ;; Only an approximation of our position in the s-expression. (goto-char point)))) (defun json-sexp-convert-buffer-to-sexp () "Convert buffer from JSON to sexps." (interactive) (json-sexp-convert-region-to-sexp (point-min) (point-max))) (defun json-sexp-convert-buffer-to-json () "Convert buffer from sexps to JSON." (interactive) (json-sexp-convert-region-to-json (point-min) (point-max))) (defvar json-sexp--saved-point nil) (define-derived-mode json-sexp-mode emacs-lisp-mode "JSON-sexp" "Major mode for editing JSON in s-expression form. The buffer-contents, which must be JSON, are transformed to s-expressions when this mode is started, and transformed back temporarily to JSON whenever the buffer is saved." (let ((was-modified (buffer-modified-p))) (json-sexp-convert-buffer-to-sexp) (set-buffer-modified-p was-modified)) (add-hook 'before-save-hook 'json-sexp--before-save nil t) (add-hook 'after-save-hook 'json-sexp--after-save nil t)) (defun json-sexp--before-save () (setq json-sexp--saved-point (point)) (json-sexp-convert-buffer-to-json)) (defun json-sexp--after-save () (json-sexp-convert-buffer-to-sexp) (set-buffer-modified-p nil) (goto-char json-sexp--saved-point)) (provide 'json-sexp-mode) ;;; json-sexp-mode.el ends here