JSON Par mode features

(For non-visual user-agents, ‘|’ is the point and ‘*’ is the mark in the following examples.)

Table of contents

Ctrl-less, yet modeless

If the point is not in a string, each key is interpreted as a command like the vi editor. If the point is in a string, keys are inserted as is:

Type “{” to insert “{}”, “t” to insert “true”.

If the region is active, each key is interpreted as a command.

Structural movement

Moving to the next/previous member (key-value pair), parents, and more:

Press “a” to go to the beginning of the member, “e” to go to the end of the member, and more.
Key Command Description
j json-par-backward-member To the next member (key-value pair).
k json-par-forward-member To the previous member.
h json-par-up-backward To the beginning of the containing object/array.
l, }, or ] json-par-up-forward To the end of the containing object/array.
a json-par-beginning-of-member To the beginning of the member.
e json-par-end-of-member To the end of the member.
v json-par-beginning-of-object-value To the beginning of the value.
J json-par-backward-record To the same key in the previous object.
K json-par-forward-record To the same key in the next object.
A json-par-beginning-of-list To the beginning of the first member.
E json-par-end-of-list To the end of the last member.

Other commands:

|[ 1, 2, 3 ]

↓ i (‘json-par-down’)

[ |1, 2, 3 ]


[ 1, 2, 3 ]| i (‘json-par-down’)

[ 1, 2, 3| ]
{
  "key|": 123
}

↓ TAB (‘json-par-tab’)

{
  "key": |123
}

↓ S-TAB (‘json-par-mark-head-of-member’)

{
  "*key|": 123
}
[
  "The quick brown |fox jumps over the lazy dog."
]

↓ TAB (‘json-par-tab’)

[
  "The quick brown fox jumps over the lazy dog."| 
]
{
  |"name": "Emacs",
  "website": "https://www.gnu.org/software/emacs/",
  "written_in": [ "C", "Emacs Lisp" ]
}

↓ g (‘json-par-goto-key’) website RET

{
  "name": "Emacs",
  |"website": "https://www.gnu.org/software/emacs/",
  "written_in": [ "C", "Emacs Lisp" ]
}
[
  "a",
  |"b",
  "c"
]

↓ M-x json-par-goto-index 0 RET

[
  |"a",
  "b",
  "c"
]

Pro tip: those commands except j, k, J, or K push a mark in the mark ring before moving, and b is bound to pop-to-mark-command. So pressing b goes back to the previous position.

Escape hatch (disabling the mode temporarily)

Press q to disable JSON Par mode only for next command. Press Q to disable JSON Par mode until explicitly enabled again.

Inserting values and balanced brackets

Just one key to insert values:

Key Description
t Insert true
f Insert false
n Insert null
[ Insert []
{ Insert {}
" Insert ""
, Insert ,
: Insert :
+, -, 0-9 Insert number

Commas and line breaks are inserted appropriately:

[
  [
    1, 2, 3
  ]| 
]

↓ [

[
  [
    1, 2, 3
  ],
  [
    | 
  ]
]
[
  [ 1, 2, 3 ]| 
]

↓ [

[
  [ 1, 2, 3 ],
  [|]
]

↓ 1

[
  [ 1, 2, 3 ],
  [ 1| ]
]

When the region is active, [, {, and " wrap the region. With C-u prefix, " unwrap the string.

Marking or deleting things

When the region is active, d or <backspace> deletes the region.

When the region is not active, d + movement key marks members or values:

[
  1|,
  2,
  3,
  4
]

↓ d2j (“mark the following two members”)

[
  1|,
  2,
  3*,
  4
]

↓ d

[
  1|,
  4
]
Key Description
dd Mark the current member (key-value pair).
d. Mark the current value or key.
dj Mark the member following the point.
dk Mark the member preceding the point.
dh Mark the containing object/array (the point move before the parent).
dl Mark the containing object/array (the point move after the parent).
da Mark the key of the current key-value pair.
de or dv Mark the value of the current key-value pair.
dA Mark all members of the containing object/array (the point move before the first member).
dE Mark all members of the containing object/array (the point move after the last member).
di Mark all contents of the object/array/string before/after the point.

Pro tip: you can delete without marking by setting json-par-action-when-deleting-value-or-member to delete.

<backspace> and C-d delete or mark things while keeping structures:

[ 1, 2, 3 ]| <backspace>

[ *1, 2, 3| ]
[]| <backspace>

*[]| 
[| 1, 2, 3 ]

↓ <backspace>

[ |1, 2, 3* ]
[|]<backspace>

|[]*
"Emacs"| <backspace>

"*Emacs|"
"|Emacs"<backspace>

"|Emacs*"
[ 1,| 2 ]

↓ <backspace>

[ *1,| 2 ]
{
  "name":| "Emacs",
  "website": "https://www.gnu.org/sotware/emacs/"
}

↓ <backspace>

{
  *"name": "Emacs",
  |"website": "https://www.gnu.org/sotware/emacs/"
}
Pro tip: you can customize behaviors:

Delete values of object/array

v (json-par-delete-object-values) deletes all values in the current object/array.

{
  "group": "group1",
  "key": "foo",
  "value": |[ 1, 2, 3 ]
}

↓ V

{
  "group": ,
  "key": ,
  "value": | 
}

Switching single-line and multiline

Examples:

|[
  [
    1,
    2
  ],
  [
    3,
    4
  ]
]

↓ O (‘json-par-oneline’) ↑ M (‘json-par-multiline’)

|[ [ 1, 2 ], [ 3, 4 ] ]

↓ O

|[[1,2],[3,4]]
{
  "lyrics": |"Twinkle, twinkle, little star,
How I wonder what you are!
Up above the world so high,
Like a diamond in the sky."
}

↓ O (‘json-par-oneline’) ↑ M (‘json-par-multiline’)

{
  "lyrics": |"Twinkle, twinkle, little star,\nHow I wonder what you are!\nUp above the world so high,\nLike a diamond in the sky."
}
Advanced: with prefix arguments:

With prefix arguments, you can limit depth of affected members:

|[
  [
    1,
    2
  ],
  [
    3,
    4
  ]
]

↓ C-1 O (convert members with levels more than one to oneline)

|[
  [ 1, 2 ],
  [ 3, 4 ]
]

↑ C-1 M (insert line breaks after commas with levels less than or equal to one)

|[ [ 1, 2 ], [ 3, 4 ] ]

When a line break is inserted just inside brackets, line breaks are also inserted after every members. When a line break just inside brackets is deleted, convert the object/array to the oneline:

[| [ 1, 2 ], [ 3, 4 ] ]

↓ RET<backspace>

[
  |[ 1, 2 ],
  [ 3, 4 ]
]
Pro tip: you can customize behaviors:

Cloning members

Examples:

{
  "properties": {
    "tags": |{
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}

↓ cj (‘json-par-clone-member-forward’: clone the current member after it)

{
  "properties": {
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "|tags*": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}
[
  {
    "group": |"group1",
    "key": "foo",
    "value": [ 1, 2, 3 ]
  }
]

↓ CJ (‘json-par-clone-parent-forward-without-value’: clone the parent member after it without values)

[
  {
    "group": "group1",
    "key": "foo",
    "value": [ 1, 2, 3 ]
  },
  {
    "group": |,
    "key": ,
    "value":
  }
]
Key Description
cj, cc Clone the current member after it.
ck Clone the current member before it.
cJ Clone the parent member after it.
cK Clone the parent member before it.
Cj, CC, cvj, cvc Clone the current member after it without value.
Ck, cvk Clone the current member before it without value.
CJ, cvJ Clone the parent member after it without value.
CK, cvK Clone the parent member before it without value.

Advanced: pressing h or i after c selects an ancestor to clone.

Completion

? (json-par-insert-guessed) is a dabbrev-expand for JSON values or keys; insert a guessed value or key based on the context:

{
   "properties": {
     "id": {
       "type": "string",
       "minLength": 1
     },
     "name": {
       "type": "string"
     },
     "email": | 
   }
}

↓ ?

{
   "properties": {
     "id": {
       "type": "string",
       "minLength": 1
     },
     "name": {
       "type": "string"
     },
     "email": {
       "type": "string"
     }| 
   }
}

↓ ?

{
   "properties": {
     "id": {
       "type": "string",
       "minLength": 1
     },
     "name": {
       "type": "string"
     },
     "email": {
       "type": "string",
       "minLength": 1
     }| 
   }
}

In the previous example, the command searches for the key "email" first in the current buffer and other JSON buffers. Then, it searches for the key "properties" and picks its grandchildren. json-par-guess-max-ancestors controls how many ancestors to search.

If the point is inside an object but not after a key, it completes a key:

{
   "properties": {
     "id": {
       "type": "string",
       "minLength": 1
     },
     "name": {
       "type": "string"
     },
     "email": {
       "type": "string"| 
     }
   }
}

↓ ?

{
   "properties": {
     "id": {
       "type": "string",
       "minLength": 1
     },
     "name": {
       "type": "string"
     },
     "email": {
       "type": "string",
       "minLength": | 
     }
   }
}

When guessing a key, it uses the ancestor keys ("email" then "properties") and the sibling keys ("type").

Transposing members

{
  "key": |"key1",
  "value": "value1"
}

↓ s (‘json-par-transpose-member-forward’) ↑ w (‘json-par-transpose-member-backward’)

{
  "value": "value1",
  "key": |"key1"
}

Raising member

{
  "items" {
    "oneOf": [
      |{ "$ref": "#/definitions/Foo" },
      { "$ref": "#/definitions/Bar" }
    ]
  }
}

↓ r (‘json-par-raise-member’)

{
  "items" {
    |{ "$ref": "#/definitions/Foo" }
  }
}

Mark/narrow DWIM

Pressing m (json-par-mark-more) or C-M-SPC repeatedly expands the region. With C-u prefix, undo an expansion.

Similarly, N (json-par-narrow) narrows to the current value or key, and expand the region if repeated.

Split/join

S (json-par-split) splits a string, object, or array at the point. F (json-par-join) join two strings or arrays/objects.

Show ancestors out of the window

Setting custom variables shows ancestors out of the window:

Ancestors are displayed with the following faces:

You can also prepend a char before ancestors or current member: