json-format(3)json-format - Reformat the buffer as indented JSON
json-compact - Remove all formatting from the JSON buffer
json-validate - Check that JSON is well formed
json-context - Toggle the sticky JSON parent context window
n json-format [regex replace]
n json-compact [regex replace]
n json-validate [regex replace]
json-context
json-format reformats the current buffer as indented JSON, one value to a line, with the indentation step taken from %json-indent-width (2 by default). Empty objects and arrays are left compact as {} and [].
json-compact performs the reverse, removing all white space that lies outside of a string so that the whole document is reduced to a single line.
Both commands work out which JSON to process, taking the first of the following that applies:
1
2
3
4
5
A buffer that cannot be edited, either in view mode or a pipe buffer such as *shell*, is never changed; in cases 3 and 4 its content is copied to *json-format* instead. The *json-format* buffer is shown in a popup window and given the JSON major mode, so it can be saved, searched or viewed with json-context like any other JSON buffer. Copies are made through the kill ring and removed from it again, without touching the system clipboard. Any narrows (folds) in the buffer being used are removed first, as the lines they hide are part of the document.
Both commands are string aware, matching either a complete string literal or a single structural character, so braces, brackets, commas and colons that occur inside a string are never mistaken for structure. Both run json-validate first and make no change at all if the buffer is not well formed. Both also apply the JSON hilighting scheme to the buffer, so a scratch buffer that is not attached to a .json file is coloured like one.
json-validate checks that JSON is well formed without changing it. It works out which JSON to check in the same way as json-format, with two differences that follow from it never making a change: a buffer that cannot be edited is checked in place rather than copied, and any region that is not empty is checked as it stands, whatever it starts and ends with, so a selection with a missing closing bracket or trailing text is reported rather than passed over for the buffer. If a problem is found the cursor is moved to the first character of the offending token, or to the end of a region that ends too soon, and the reason is reported on the message line. A region is checked in a copy in *json-format* but the cursor is still placed in the source; only JSON taken from the kill buffer, or pre-processed as described below, is left in the *json-format* popup. The source is left untouched, except that any narrows are removed so the hidden lines are checked too. As the JSON standard requires, a string may not hold a raw control character (a tab must be written as \t) or an unknown escape, and a number may not have a leading zero.
When bit 0x02 of the numeric argument n is set (e.g. esc 2) all three commands first make a regular expression replacement in the JSON. This is useful for JSON held in a string literal of another language, where every quote inside it is escaped. The user is prompted for the expression and then for its replacement, both starting empty with the search history available; an empty expression skips the step. For example selecting the text between the outer quotes of the Java line
String s = "{\"name\": \"a\\\"b\"}";
and running esc 2 json-validate, replacing \\\(["\\]\) with \1, which removes one level of escaping so an escaped quote becomes a quote and a doubled backslash a single one, checks
{"name": "a\"b"}
From a macro the expression and replacement are given as arguments, which avoids the prompts:
2 json-validate "\\\\\\([\"\\\\]\\)" "\\1"
The replacement is only ever made in the copy in *json-format*, it is made with replace-string(2) in magic mode and the number of replacements is added to the final message. A buffer or region is always copied, even when it could otherwise be processed or replaced in place, so the source is never changed, and any region is taken as it stands, as with json-validate, because the brackets being checked for may only appear once it has been unescaped. As the positions in the copy no longer match the source, the result or any error is shown in the *json-format* popup.
json-context toggles a sticky window at the top of the frame which shows the parent lines of the value the cursor is currently inside, in the manner of a folded outline:
{
"servers": [
{
"config": {
"tls": {
The window is refreshed when the cursor comes to rest, and it grows and shrinks to the number of levels being shown, up to a limit of %json-context-depth (8 by default); when the nesting is deeper than that the innermost levels are shown. The main window is scrolled by the same amount in the opposite direction as the context window is resized, so the text does not appear to move.
While the context window is open ^G closes it and then performs its usual abort. Issuing json-context a second time also closes it. Each buffer has its own context buffer, named *json-context:name*, so the context of several buffers can be shown at once.
While the context window is open the buffer's own window is locked, so pop-up buffers such as help or the buffer list use another window. The context is turned off and its window removed when the buffer is deleted or is no longer shown in any window, for example when another buffer is selected into its window, its window is deleted or delete-other-windows(2) is used from another window. Run json-context again to restore it.
[JSON line l col c: reason]
expected a name or '}' but found x, expected a name but found x
expected ':' after the name but found x
expected a value but found x
expected ',' or a closing bracket but found x
trailing ',' before '}', trailing ',' before ']'
'}' closes an array that was opened with '[', ']' closes an object that was opened with '{'
unexpected x after the end of the JSON value
unterminated string - no closing quote
control character in a string - it must be escaped, e.g. \t
invalid escape '\x' in a string
'\u' in a string must be followed by 4 hex digits
'x' is not a valid number
'x' is not a value - expected a string, number, true, false or null
reached the end with n container(s) still open - missing 'x'
no JSON value found
[JSON is valid]
[JSON validating... n%], [JSON formatting... n%], [JSON compacting... n%]
[JSON formatted], [JSON compacted]
[No JSON found - select a region, copy some JSON or open a .json file]
[JSON: too many split lines to copy back, the result is in *json-format*]
[JSON context needs a formatted buffer - run json-format first]
These are macros defined in jsonview.emf.
A line in the kill buffer longer than the 65520 character line limit cannot be yanked, so a large single line document on the clipboard is cut short and fails to validate. Save it to a file and open that instead, lines that long are split when a file is loaded and rejoined when it is saved. For the same reason, copying text from such a line puts a line break where it was split, which can fall inside a string; select the JSON as a region instead of copying it. Kill buffer text that starts with more than 2KB of white space is not recognised. A pre-process replacement (bit 0x02) cannot match across the point where such a line was split. NUL characters are dropped when a file is loaded, so a NUL in a JSON file is never seen and cannot be reported.
json-context works from the indentation rather than by parsing, which makes it cheap enough to run as the cursor moves, but does require the buffer to have been formatted first.
The supporting macro json-path is private, but once jsonview.emf has been loaded (by any of the commands above) it may be called from user macros to obtain the position of the cursor within the document. It sets %json-path to the readable path, %json-lines to the parent lines themselves and %json-count to the number of levels, leaving the buffer and the cursor untouched. As a variable holds at most 2KB, %json-lines keeps only the innermost %json-context-depth parent lines and a long %json-path keeps the innermost names, starting with ... / where the outer ones were dropped.
Ref: json-format(3) File: m3mac113.3 Date: 2026/10/25
(c) Copyright JASSPA 2026