JSON Par mode

JSON Par modeは、EmacsでJSONを構造的に編集をするためのマイナーモードであり、lispyに影響を受けています。

(非視覚的UAをお使いの方へ: 以降の例で‘|’はポイントであり、‘*’はマークです。)

目次

Ctrl無しかつモードレス

ポイントが文字列の外にある場合、各キーはコマンドとして解釈されます。一方ポイントが文字列の上にある場合、キーはそのまま入力されます:

“{”をタイプすると“{}”を挿入し、“t”をタイプすると“true”を挿入します。

リージョンがアクティブな場合、各キーはコマンドとして解釈されます。

構造に基づく移動

前後のメンバー(キーと値のペア)や、親などへ移動できます:

“a”を押すとメンバーの先頭に移動し、“e”でメンバーの末尾へ移動し、その他様々な移動方法があります。
キー コマンド 説明
j json-par-backward-member 次のメンバー(キーと値のペア)
k json-par-forward-member 前のメンバー
h json-par-up-backward 囲んでいるオブジェクトや配列の先頭
l, }, あるいは] json-par-up-forward 囲んでいるオブジェクトや配列の末尾
a json-par-beginning-of-member メンバーの先頭
e json-par-end-of-member メンバーの末尾
v json-par-beginning-of-object-value 値の先頭
J json-par-backward-record 前のオブジェクトの同じキー
K json-par-forward-record 次のオブジェクトの同じキー
A json-par-beginning-of-list 最初のメンバーの先頭
E json-par-end-of-list 最後のメンバーの末尾

その他のコマンド:

|[ 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"
]

小技: jkJK以外のコマンドは移動前にマークリングにマークをプッシュします。またbpop-to-mark-commandにバインドされています。そのためbを押すと移動前の位置に戻れます。

非常口(一時的に無効にする)

qを押すと次のコマンドだけJSON Par modeを無効にできます。Qを押すと明示的に再有効化するまで無効になります。

値や括弧対の挿入

1キーで値を入力できます:

キー 説明
t trueを挿入する
f falseを挿入する
n nullを挿入する
[ []を挿入する
{ {}を挿入する
" ""を挿入する
, ,を挿入する
: :を挿入する
+, -, 0-9 数値を挿入する

カンマや改行は適宜挿入されます:

[
  [
    1, 2, 3
  ]| 
]

↓ [

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

↓ [

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

↓ 1

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

リージョンがアクティブな場合[{"でリージョンを囲めます。C-uプレフィックスを付けた場合、"は文字列を展開します。

小技: C-uの代わりにuをプレフィックスとして使えます。

section>

マークおよび削除

リージョンがアクティブな場合、d<backspace>でリージョンを削除できます。

リージョンがアクティブでない場合、d+移動キーでメンバーや値をマークできます:

[
  1|,
  2,
  3,
  4
]

↓ d2j (「後続のメンバーを2つマークする」)

[
  1|,
  2,
  3*,
  4
]

↓ d

[
  1|,
  4
]
tr>
キー 説明
dd 現在のメンバー(キーと値のペア)をマークする。
d. 現在の値やキーをマークする。
dj ポイントの後にあるメンバーをマークする。
dk ポイントの前にあるメンバーをマークする。
dh 囲んでいるオブジェクトや配列をマークする(ポイントは親の前に移動する)。
dl 囲んでいるオブジェクトや配列をマークする(ポイントは親の後に移動する)。
da 現在のキーと値のペアのうち、キーをマークする。
deあるいはdv 現在のキーと値のペアのうち、値をマークする。
dA 囲んでいるオブジェクトや配列の全てのメンバーをマークする(ポイントは最初のメンバーの前に移動する)。
dE 囲んでいるオブジェクトや配列の全てのメンバーをマークする(ポイントは最後のメンバーの後に移動する)。
di ポイントの前か後にあるオブジェクトや配列や文字列の中身を全てマークする。

小技: json-par-action-when-deleting-value-or-memberdeleteにするとマークせずに削除できます。

<backspace>C-dを使うと構造を保ったまま様々な物を削除あるいはマークできます:

[ 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/"
}
小技: 動作はカスタマイズ可能です:
  • json-par-action-when-deleting-brackets-from-outside(括弧を外側から削除しようとする場合)
    • delete-outer: オブジェクトや値全体を削除する。
    • delete-inner: オブジェクトや値が空でなければ中身を削除する。そうでなければオブジェクトや配列全体を削除する。
    • mark-outer: オブジェクトや配列全体をマークする。
    • mark-or-delete-outer: オブジェクトや配列全体をマークする。既にマークされていれば削除する。
    • mark-inner: オブジェクトや配列が空でなければ中身をマークする。そうでなければオブジェクトや配列全体をマークする。
    • enter: オブジェクトや配列の内側にポイントを移動する。
  • json-par-action-when-deleting-brackets-from-inside(括弧を内側から削除しようとする場合)
    • delete-outer: オブジェクトや配列全体を削除する。
    • delete-inner: オブジェクトや配列が空でなければ中身を削除する。そうでなければオブジェクトや配列全体を削除する。
    • mark-outer: オブジェクトや配列全体をマークする。
    • mark-inner: オブジェクトや配列が空でなければ中身をマークする。そうでなければオブジェクトや配列全体をマークする。
    • mark-or-delete-inner: オブジェクトや配列が空でなければ中身をマークする。そうでなければオブジェクトや配列全体をマークする。既にマークされていれば削除する。
    • exit: オブジェクトや配列の外にポイントを移動する。
    • none: 何もしない。
  • json-par-action-when-deleting-string-from-outside(文字列を外側から削除しようとする場合)
    • 括弧の場合と同じ
  • json-par-action-when-deleting-string-from-inside(文字列を内側から削除しようとする場合)
    • 括弧の場合と同じ
  • json-par-action-when-deleting-comma(カンマを削除しようとする場合)
    • delete: カンマの向こうにあるメンバー(キーと値のペア)全体を無条件に削除する。
    • mark-or-delete: カンマの向こうにあるメンバー(キーと値のペア)全体がマークされていれば削除する。
      そうでなければメンバー全体をマークする。
    • mark: カンマの向こうにあるメンバー全体を無条件にマークする。
    • skip: カンマの向こう側にポイントを移動する。
  • json-par-action-when-deleting-colon(コロンを削除しようとする場合)
    • delete: メンバー(キーと値のペア)全体を無条件に削除する。
    • mark-or-delete: メンバー(キーと値のペア)全体がマークされていれば削除する。
      そうでなければメンバー全体をマークする。
    • mark: メンバー全体を無条件にマークする。
    • skip: コロンの向こう側にポイントを移動する。

オブジェクトや配列の値の削除

V (json-par-delete-object-values)は現在のオブジェクトや配列の値を全て削除します。

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

↓ V

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

1行と複数行の切り替え

例:

|[
  [
    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."
}
発展: プレフィックス引数付きの場合:

プレフィックス引数を付けると、影響する範囲を限定できます:

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

↓ C-1 O (レベルが1より大きいメンバーを1行に変換する)

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

↑ C-1 M (レベルが1以下のカンマの後に改行を入れる)

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

括弧のすぐ内側に改行が挿入された場合、各メンバーの後にも改行が挿入されます。括弧のすぐ内側の改行が削除された場合、オブジェクトや配列全体が1行に変換されます:

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

↓ RET<backspace>

[
  |[ 1, 2 ],
  [ 3, 4 ]
]
小技: 動作はカスタマイズ可能です:
  • json-par-action-when-breaking-line-at-just-inside-brackets

    括弧のすぐ内側に改行を入れた場合の動作。

    • break-inside-brackets: 括弧のすぐ内側のみに改行を入れる。

      例 (‘|’はポイント):

      [ |1, 2, 3 ]
      
      ↓ RET
      
      [
        |1, 2, 3
      ]
    • break-each-member: 各メンバーの後にも改行を入れる。

      例 (‘|’はポイント):

      [ |1, 2, 3 ]
      
      ↓ RET
      
      [
        |1,
        2,
        3
      ]

    デフォルト値はbreak-each-memberです。

  • json-par-action-when-breaking-line-after-first-member

    最初のメンバーの後に改行を入れた場合の動作。

    • just-break: その行のみに改行を入れる。

      例 (‘|’はポイント):

      [
        1, |2, 3
      ]
      
      ↓ RET
      
      [
        1,
        |2, 3
      ]
    • break-each-member: 各メンバーの後にも改行を入れる。

      例 (‘|’はポイント):

      [
        1, |2, 3
      ]
      
      ↓ RET
      
      [
        1,
        |2,
        3
      ]

    デフォルト値はjust-breakです。

  • json-par-action-when-joining-non-empty-lines

    空行以外の改行を削除した場合の動作。

    • just-delete: その改行のみを削除する。

      例 (‘|’はポイント):

      [
        1,
      |  2,
        3
      ]
      
      ↓ <backspace>
      
      [
        1,|  2,
        3
      ]
    • delete-line-breaks-between-members: 1メンバー1行になっている場合、各メンバー間の改行(最初のメンバーの前や最後のメンバーの後の改行は含まない)を全て削除する。

      例 (‘|’はポイント):

      [
        1,
      |  2,
        3
      ]
      
      ↓ <backspace>
      
      [
        1,|  2, 3
      ]
      [
        1,
      |  2,
        3, 4
      ]
      
      ↓ <backspace>
      
      [
        1,|  2,
        3, 4
      ]
    • delete-line-breaks-inside-brackets: 1メンバー1行になっている場合、現在の配列やオブジェクト内の改行を全て削除する。

      例 (‘|’はポイント):

      [
        1,
      |  2,
        3
      ]
      
      ↓ <backspace>
      
      [ 1,|  2, 3 ]
      [
        1,
      |  2,
        3, 4
      ]
      
      ↓ <backspace>
      
      [
        1,|  2,
        3, 4
      ]

    デフォルト値はjust-deleteです。

メンバーの複製

例:

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

↓ cj (‘json-par-clone-member-forward’: 現在のメンバーをその後に複製する)

{
  "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’: 親であるメンバーをその後に値を除いて複製する)

[
  {
    "group": "group1",
    "key": "foo",
    "value": [ 1, 2, 3 ]
  },
  {
    "group": |,
    "key": ,
    "value":
  }
]
キー 説明
cj, cc 現在のメンバーをその後に複製する。
ck 現在のメンバーをその前に複製する。
cJ 親であるメンバーをその後に複製する。
cK 親であるメンバーをその前に複製する。
Cj, CC, cvj, cvc 現在のメンバーをその後に値を除いて複製する。
Ck, cvk 現在のメンバーをその前に値を除いて複製する。
CJ, cvJ 親であるメンバーをその後に値を除いて複製する。
CK, cvK 親であるメンバーをその前に値を除いて複製する。

発展: c

kbd>の後にhlを押すと複製する祖先を選択できます。ibhlの効果を打ち消します。

補完

? (json-par-insert-guessed)はJSONの値やキーに対するdabbrev-expandです。つまり文脈から推測した値やキーを挿入します:

{
   "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
     }| 
   }
}

前述の例では、まず現在のバッファや他のJSONのバッファから"email"キーを探します。その後、"properties"を探してその孫を抽出します。json-par-guess-max-ancestorsは検索に使う祖先をどれだけ辿るのか制御します。

ポイントがオブジェクトの内部にあり、キーの後にない場合は、キーを補完します:

{
   "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": | 
     }
   }
}

キーを推測する場合、祖先のキー("email""properties")と兄弟のキー("type")を使います。

メンバーの入れ替え

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

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

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

メンバーの昇格

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

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

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

DWIM(いい感じに)マークやナロー

m (json-par-mark-more)やC-M-SPCを繰り返し押すと、リージョンを拡張できます。C-uプレフィックスを付けると拡張を取り消せます。

同様に、N (json-par-narrow)で現在の値やキーにナロー(表示範囲の限定)でき、繰り返すと拡張できます。

分割/結合

S (json-par-split)でポイントがある文字列やオブジェクトや配列を分割できます。F (json-par-join)で2つの文字列や配列やオブジェクトを結合できます。

ウィンドウ外の祖先の祖先

カスタム変数を設定すると、ウィンドウ外の祖先を表示できます::

祖先は次のフェイスで表示されます:

また、祖先や現在のメンバーの前に文字を表示できます:

その他便利機能

uuniversal-argumentにマップされています。