jsonian.el
provides a major mode for editing JSON files of any size. The goal is to be
feature complete against json-mode with no
external dependencies or file size limits.
To that end, all functionality is guarantied to operate on arbitrarily large JSON files. If you find a feature that works only on small files, or is slower then it should be on large files, please file an issue.
jsonian.el
supports standard JSON (.json
) and JSON with comments (.jsonc
).
jsonian.el
is a single file package, and can be compiled with make build
. Just move
the compiled jsonian.elc
onto your load path.
If you are using Doom Emacs, you can configure
doom to use jsonian
with the following snippet.
;;; In ~/.doom.d/packages.el
(package! jsonian :recipe (:host github :repo "iwahbe/jsonian"))
(package! json-mode :disable t)
;;; In ~/.doom.d/config.el
;; To enable jsonian to work with flycheck
(after! (jsonian flycheck) (jsonian-enable-flycheck))
;; To diasable so-long mode overrides
(after! (jsonian so-long) (jsonian-no-so-long-mode))
Clone the repository
mkdir ~/src
cd ~/src/
git clone [email protected]:iwahbe/jsonian.git
Emacs 27+ includes so-long
mode which will supplant jsonian-mode
if the file has any
long lines. To prevent so-long
mode from taking over from jsonian-mode
, call
jsonian-no-so-long-mode
after so-long
mode has loaded.
Initialize the local package with use-package making it work with so-long
;;; In ~/.emacs.d/init-jsonian-mode.el
(use-package jsonian
:load-path "~/src/jsonian"
:ensure nil
:after so-long
:custom
(jsonian-no-so-long-mode))
Initialize the local package with use-package making it work with
so-long
, and also wrap it in an initialization package
;;; In ~/.emacs.d/init.el
(require 'init-jsonian-mode)
Requires that ~/.emacs.d/site-elisp
(or whichever directory the
package is in) exist and be in the load path
;;; In ~/.emacs.d/site-elisp/init-jsonian-mode.el
;;; Code:
(use-package jsonian
:load-path "~/src/jsonian"
:ensure nil
:after so-long
:custom
(jsonian-no-so-long-mode))
(provide 'init-jsonian-mode)
;;; init-jsonian-mode.el ends here
Flycheck integrates directly with
json-mode. jsonian
provides the convenience
function jsonian-enable-flycheck
which adds jsonian-mode
to all checkers that support
json-mode
. jsonian-enable-flycheck
must run after flycheck
has already loaded.
Return the JSON path (as a list) of POINT in BUFFER. It is assumed that BUFFER is entirely JSON and that the json is valid from POS to ‘point-min’.
For example:
{
"foo": [
{
"bar": "█"
},
{
"fizz": "buzz"
}
]
}
with pos at █ should yield "[foo][0][bar]".
‘jsonian-path’ is optimized to work on very large json files (35 MiB+). This optimization is achieved by a. parsing as little of the file as necessary to find the path and b. leveraging C code whenever possible.
By default, this command is bound to C-c C-p
.
jsonian-find
needs to filter it's results. By default, it filters by prefix. You can
customize the prefix function by setting
jsonian-find-filter-fn
. Orderless provides an
excellent fuzzy search implementation, which you can use via:
(with-eval-after-load 'orderless
(setq jsonian-find-filter-fn #'orderless-filter))
Edit the string at point in another buffer. The string is expanded when being edited and
collapsed back upon exit. For example, editing the string "json\tescapes\nare\nannoying"
will drop you into a buffer containing:
json escapes
are
annoying
When you return from the buffer, the string is collapsed back into its escaped form (preserving edits).
By default, this command is bound to C-c C-s
.
Provide an interactive completion interface for selecting an element in the buffer. When the element is selected, jump to that point in the buffer.
By default, this command is bound to C-c C-f
.
Maximize the JSON contents of the region. This is equivalent to the built-in function
json-pretty-print
, but much faster (see "## Benchmarks"). For example:
{"key":["simple",null,{"cpx": true},[]]}
Calling jsonian-format-region
on the above will transform it into:
{
"key": [
"simple",
null,
{
"cpx": true
},
[]
]
}
If a prefix argument is supplied, jsonian-format-region
minimizes instead of expanding.
By default, this command is bound to C-c C-w
.
Move point to the enclosing node. For example:
[
{ "foo": { "fizz": 3, "buzz": 5 } },
{ "bar": { "fizz": 3, "buzz": 5 } }
]
If the point started on the 5
, calling jsonian-enclosing-item
would move
point to the "
at the beginning of "foo"
. Calling it again would move the
point to the first {
on the second line. Calling it a final time would move
the point to the opening [
.
By default, this function is bound to C-c C-e
.
Enable jsonian-mode
for all checkers where json-mode
is enabled.
The original reason I wrote jsonian is that I needed to read and naviage very large JSON files, and Emacs was slowing me down. To keep jsonian fast, I maintain benchmarks of jsonian doing real world tasks.
This benchmark opens a very large (42M) JSON file, then forces Emacs to fontify it. It finally moves point to the end of the file and exits.
Package | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
fundamental-mode |
1.357 ± 0.007 | 1.346 | 1.372 | 1.00 |
prog-mode |
1.431 ± 0.009 | 1.419 | 1.444 | 1.05 ± 0.01 |
jsonian-mode |
2.315 ± 0.021 | 2.284 | 2.347 | 1.71 ± 0.02 |
json-mode |
3.846 ± 0.062 | 3.781 | 3.992 | 2.83 ± 0.05 |
javascript-mode |
13.638 ± 0.099 | 13.439 | 13.816 | 10.05 ± 0.09 |
We can use this benchmark to derive how long different parts of the proces take.
-
Fundamental mode is the lower limit. This is the time Emacs spends processing the buffer, parsing sexps, etc.
-
prog-mode
doesn't do much more thenfundamental-mode
, which makes sense, since it takes about the same amount of time. -
Applying JSON formatting take at most
jsonian-mode
-prog-mode
. -
Parsing a javascript file is much more complicated (and thus expensive) then parsing a JSON file.
This tests applying formatting to a very large (42M) JSON file that is compressed to remove all whitespace. The formatted files are largely identical.
Package | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
jsonian-format-region |
1.015 ± 0.011 | 1.000 | 1.034 | 1.18 ± 0.02 |
jsonian-format-region (minimize) |
0.860 ± 0.007 | 0.845 | 0.869 | 1.00 |
json-pretty-print-buffer |
4.655 ± 0.005 | 4.650 | 4.666 | 5.42 ± 0.04 |
json-pretty-print-buffer (minimize) |
4.466 ± 0.020 | 4.437 | 4.502 | 5.20 ± 0.05 |
We see that the built-in json-pretty-print-buffer
takes significantly longer then
jsonian-format-region
, regardless of whether we are pretty printing or minimizing.
Notes:
- Both
jsonian
andjson-mode
were byte-compiled for thefont-lock
benchmark. - Tests were run against GNU Emacs 30.0.50.
- These benchmarks were taken on an Apple M2 Max with 64GB running macOS Ventura.
Contributions are welcome, both in the form of new Issues and PRs. Code contributors agree that their contribution falls under the LICENSE.
I use jsonian-mode
every day, and it works perfectly for my needs. I'm not aware of any
bugs at this time, nor do I think any important features are missing. Because of this, I'm
not actively adding new code to the project. The project is not abandon, and I'm happy to
address bugs or consider new feature requests.