jid

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2026 License: MIT Imports: 18 Imported by: 14

README

jid

Test

jid logo

Json Incremental Digger

It's a very simple tool. You can drill down JSON interactively by using filtering queries like jq.

Suggestion, Auto completion, and JMESPath support provide a comfortable JSON exploration experience.

Demo

Drill-down navigation

Interactively navigate JSON using dot-path queries. Tab-complete fields, cycle through candidates, and see the matching key highlighted in the JSON view in real time.

demo-jid-drilldown

JMESPath expressions

Use pipes, wildcards, and built-in functions directly in the filter. Function candidates are shown with usage hints and argument templates are filled in automatically.

demo-jid-jmespath

Installation

With HomeBrew (for macOS)
brew install jid
With MacPorts (for macOS)
sudo port install jid
With pkg (for FreeBSD)
pkg install jid
With scoop (for Windows)
scoop install jid
Other package management systems

Jid can install by package management systems of below OS.

Packaging status

Simply use "jid" command

If you simply want to use jid command, please download binary from below.

https://github.com/simeji/jid/releases

Build

go install github.com/simeji/jid/cmd/jid@latest

Usage

Quick start
simple json example

Please execute the below command.

echo '{"aa":"2AA2","bb":{"aaa":[123,"cccc",[1,2]],"c":321}}'| jid

then, jid will be running.

You can dig JSON data incrementally.

When you enter .bb.aaa[2], you will see the following.

[Filter]> .bb.aaa[2]
[
  1,
  2
]

Then, you press Enter key and output [1,2] and exit.

simple json example2
echo '{"info":{"date":"2016-10-23","version":1.0},"users":[{"name":"simeji","uri":"https://github.com/simeji","id":1},{"name":"simeji2","uri":"https://example.com/simeji","id":2},{"name":"simeji3","uri":"https://example.com/simeji3","id":3}],"userCount":3}}'|jid
With a initial query

First argument of jid is initial query. (Use JSON same as Demo)

demo-jid-with-query

with curl

Sample for using RDAP data.

curl -s http://rdg.afilias.info/rdap/domain/example.info | jid
Load JSON from a file
jid < file.json

Keymaps

key description
TAB / CTRL + I Show available items and choose them (cycles forward); highlights the matching key in the JSON view
Shift + TAB Cycle candidates backward / decrement array index
CTRL + W Delete one JMESPath segment backward (e.g. .id[0]func(@) → pipe)
CTRL + U Delete whole query
CTRL + X Toggle function description display (visible when function candidates are shown)
CTRL + F / Right Arrow (➡) Move cursor a character to the right
CTRL + B / Left Arrow (⬅) Move cursor a character to the left
CTRL + A To the first character of the 'Filter'
CTRL + E To the end of the 'Filter'
CTRL + J Scroll json buffer 1 line downwards
CTRL + K Scroll json buffer 1 line upwards
CTRL + G Scroll json buffer to bottom
CTRL + T Scroll json buffer to top
CTRL + N Scroll json buffer 'Page Down'
CTRL + P Scroll json buffer 'Page Up'
CTRL + L Change view mode whole json or keys (only object)
ESC Hide a candidate box
Up Arrow Navigate to previous query in history
Down Arrow Navigate to next query in history
Option
option description
First argument ($1) Initial query
-h print a help
-help print a help
-version print the version and exit
-q Output query mode (for jq)
-M monochrome output mode

Configuration

jid can be configured via a TOML file located at:

OS Path
macOS ~/Library/Application Support/jid/config.toml
Linux ~/.config/jid/config.toml
Windows %AppData%\jid\config.toml
Example config.toml
[history]
path = "~/.jid_history"  # custom history file path
max_size = 1000           # number of entries to keep

[keybindings]
history_prev    = "up"      # navigate to older query
history_next    = "down"    # navigate to newer query
scroll_down     = "ctrl+j"
scroll_up       = "ctrl+k"
scroll_to_bottom = "ctrl+g"
scroll_to_top   = "ctrl+t"
scroll_page_down = "ctrl+n"
scroll_page_up  = "ctrl+p"
toggle_keymode  = "ctrl+l"
delete_line     = "ctrl+u"
delete_word     = "ctrl+w"
cursor_left     = "ctrl+b"
cursor_right    = "ctrl+f"
cursor_to_start = "ctrl+a"
cursor_to_end   = "ctrl+e"
toggle_func_help = "ctrl+x"
candidate_next  = "tab"       # cycle candidates forward
candidate_prev  = "ctrl+p"    # cycle candidates backward (additional key; Shift+Tab always works)
quit            = "ctrl+q"    # exit jid (used when exit_on_enter = false)

[behavior]
exit_on_enter = true   # set to false to prevent accidental exit on Enter

Note: Shift+Tab (\x1b[Z) is a fixed terminal escape sequence and always triggers backward cycling regardless of candidate_prev.

Preventing accidental exit on Enter

By default, pressing Enter exits jid and prints the current result. If you find yourself accidentally exiting, set exit_on_enter = false in config.toml:

[behavior]
exit_on_enter = false

When disabled, Enter only confirms a candidate selection. Use Ctrl+Q (or your configured quit key) to exit.

Supported key strings

ctrl+actrl+z, up, down, left, right, tab, enter, esc, backspace, home, end, pgup, pgdn, delete, f1f12

Query History

Queries are saved automatically on Enter. The history file path follows the same OS convention as the config file (e.g. ~/Library/Application Support/jid/history on macOS) unless overridden in config.toml.

JMESPath Support

jid supports JMESPath expressions in addition to the traditional dot-path notation. JMESPath mode is automatically activated when the query contains pipe (|), wildcards ([*]), filter expressions ([?), or function calls.

JMESPath Query Examples
.                          traditional: show root JSON
.users                     traditional: navigate to users field
.users[0].name             traditional: array index + field access

.users[*].name             wildcard projection: extract name from every user
.users[*].address.city     nested wildcard projection
.users[*].<Tab>            show field candidates from array elements

. | keys(@)                pipe: list root object keys
.users | length(@)         pipe: count users array
.users | sort_by(@, &name) pipe: sort users by name field
.users | reverse(@)        pipe: reverse the array

.[1] | to_array(@)[0].id   chained pipe with indexing
. | to_array(@)[0]         wrap root in array and index

.users[*].name | [0]       project names then index
Wildcard Projection + Array Index

After a wildcard projection like .game_indices[*].version, the result is an array. Use [N] to navigate into it — jid automatically rewrites to pipe form internally:

.game_indices[*]           → field candidates: game_index, version
.game_indices[*].version   → shows array of version objects; suggests [
.game_indices[*].version[0]           → first version object {name, url}
.game_indices[*].version[0].name      → first version's name
.game_indices[*].version[0] | keys(@) → keys of first version object
.game_indices[*].version[0] | keys(@) | sort(@)  → sorted keys

Note: In standard JMESPath, [*].field[0] applies [0] to each projected element rather than the projected array, producing []. jid detects this pattern and transparently rewrites it to [*].field | [0] so [0] indexes the array.

Function Candidates

When you type | after a field, jid shows available JMESPath functions filtered by the type of the preceding expression:

Input type Suggested functions
Array avg, contains, join, length, map, max, max_by, min, min_by, not_null, reverse, sort, sort_by, sum, to_array, to_string, type
Object keys, length, merge, not_null, to_array, to_string, type, values
String contains, ends_with, length, not_null, reverse, starts_with, to_array, to_number, to_string, type
Number abs, ceil, floor, not_null, to_array, to_string, type

A usage description is shown below the candidate list (toggle with Ctrl+X).

Candidate Key Highlighting

The matching JSON key is highlighted in yellow and the view auto-scrolls to it in two situations:

  • While typing — as soon as the query narrows down to a single candidate (e.g. typing .na when only name matches), the corresponding key is highlighted immediately, before pressing Tab.
  • While cycling with Tab / Shift+Tab — the key for each selected candidate is highlighted as you cycle through the list.

In both cases, if the key is outside the visible area the JSON view scrolls to bring it into view. Only the key at the correct nesting level is highlighted — nested keys with the same name are ignored.

Function Argument Templates

When a function candidate is confirmed, the arguments are automatically filled in and the cursor is placed at the right position:

Function Inserted as Cursor position
contains contains(@, '') inside ''
ends_with ends_with(@, '') inside ''
starts_with starts_with(@, '') inside ''
join join('', @) inside '' (separator)
sort_by sort_by(@, &field) on field placeholder
max_by max_by(@, &field) on field placeholder
min_by min_by(@, &field) on field placeholder
map map(&expr, @) on expr placeholder

Placeholder text is shown in cyan. Typing any character replaces the entire placeholder.

&field Candidate Completion

For functions that take a &field argument (sort_by, max_by, min_by, map), jid automatically shows the available field names from the base array as soon as the &field template is inserted:

.stats | sort_by(@, &field)   →  field names shown: base_stat  effort  stat
.stats | sort_by(@, &b        →  filtered: base_stat
.stats | sort_by(@, &base_stat)  →  confirmed; expression evaluates normally
  • Tab / Shift+Tab cycles through field candidates; cursor stays between & and )
  • Typing filters candidates by the partial name after &
  • Enter or Tab (when only one candidate) confirms the selection
  • Ctrl+W deletes the field name but keeps & (e.g. &base_stat)&)
Wildcard Projection Navigation

After a wildcard expression like .game_indices[*], jid shows the field names of the array elements as candidates:

.game_indices[*]           → candidates: game_index, version
.game_indices[*].<Tab>     → same candidates (trailing dot still shows fields)
.game_indices[*].v<Tab>    → filtered: version
.game_indices[*].version   → shows array result; suggests [ for index navigation
.game_indices[*].version[0] → first version object; candidates: name, url
Ctrl+W in JMESPath Mode

Ctrl+W removes one segment at a time from the end of a JMESPath expression:

.[3] | to_array(@)[0].id  →(Ctrl+W)→  .[3] | to_array(@)[0]
.[3] | to_array(@)[0]     →(Ctrl+W)→  .[3] | to_array(@)
.[3] | to_array(@)        →(Ctrl+W)→  .[3] |
.[3] |                    →(Ctrl+W)→  .[3]

Inside a function call, the &field argument is treated as one unit and & is preserved:

.stats | max_by(@, &base_stat)  →(Ctrl+W)→  .stats | max_by(@, &
.stats | max_by(@, &            →(Ctrl+W)→  .stats |

Documentation

Index

Constants

View Source
const (
	DefaultY     int    = 1
	FilterPrompt string = "[Filter]> "
)

Variables

This section is empty.

Functions

func FunctionDescription added in v1.0.0

func FunctionDescription(name string) string

FunctionDescription returns the usage description for a JMESPath function name. name may include a trailing "(". Returns "" if not found.

func FunctionTemplate added in v1.0.0

func FunctionTemplate(name string) (args string, cursorBack int, placeholderLen int)

FunctionTemplate returns the argument template, cursor-back offset, and placeholder length for a JMESPath function. name may include a trailing "(".

func ParseKey added in v1.1.0

func ParseKey(s string) (termbox.Key, bool)

ParseKey converts a key string (e.g. "ctrl+j", "up", "f5") to a termbox.Key. Returns 0, false if the string is not recognized.

Types

type BehaviorConfig added in v1.1.1

type BehaviorConfig struct {
	// ExitOnEnter controls whether Enter exits jid (default: true for backwards compatibility).
	// Set to false to make Enter only confirm a candidate; use the quit keybinding to exit.
	ExitOnEnter *bool `toml:"exit_on_enter"`
}

BehaviorConfig controls general jid behaviour.

type Config added in v1.1.0

type Config struct {
	History     HistoryConfig     `toml:"history"`
	Keybindings KeybindingsConfig `toml:"keybindings"`
	Behavior    BehaviorConfig    `toml:"behavior"`
}

Config holds all jid configuration.

func LoadConfig added in v1.1.0

func LoadConfig() Config

LoadConfig reads ~/.config/jid/config.toml (or OS equivalent). Missing fields fall back to defaults; missing file returns defaults silently.

func (*Config) HistoryPath added in v1.1.0

func (c *Config) HistoryPath() string

HistoryPath returns the resolved history file path. Uses the configured path if set, otherwise falls back to the OS default.

func (*Config) IsExitOnEnter added in v1.1.1

func (c *Config) IsExitOnEnter() bool

IsExitOnEnter returns true when Enter should exit jid (the default).

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

func (*Engine) GetQuery

func (e *Engine) GetQuery() QueryInterface

func (*Engine) Run

func (e *Engine) Run() EngineResultInterface

type EngineAttribute

type EngineAttribute struct {
	DefaultQuery string
	Monochrome   bool
	PrettyResult bool
}

type EngineInterface

type EngineInterface interface {
	Run() EngineResultInterface
	GetQuery() QueryInterface
}

func NewEngine

func NewEngine(s io.Reader, ea *EngineAttribute) (EngineInterface, error)

type EngineResult

type EngineResult struct {
	// contains filtered or unexported fields
}

func (*EngineResult) GetContent

func (er *EngineResult) GetContent() string

func (*EngineResult) GetError

func (er *EngineResult) GetError() error

func (*EngineResult) GetQueryString

func (er *EngineResult) GetQueryString() string

type EngineResultInterface

type EngineResultInterface interface {
	GetQueryString() string
	GetContent() string
	GetError() error
}

type History added in v1.1.0

type History struct {
	// contains filtered or unexported fields
}

func NewHistory added in v1.1.0

func NewHistory(path string, maxSize int) *History

func (*History) Add added in v1.1.0

func (h *History) Add(query string)

Add adds a query to history with deduplication (moves existing entry to end).

func (*History) AtEnd added in v1.1.0

func (h *History) AtEnd() bool

AtEnd returns true when the navigation cursor is at the newest position.

func (*History) Next added in v1.1.0

func (h *History) Next() (string, bool)

Next moves to the next (newer) entry and returns it. Returns "", false when moving past the newest entry (back to current input).

func (*History) Prev added in v1.1.0

func (h *History) Prev() (string, bool)

Prev moves to the previous (older) entry and returns it.

func (*History) ResetIdx added in v1.1.0

func (h *History) ResetIdx()

ResetIdx resets navigation to the newest position.

func (*History) Save added in v1.1.0

func (h *History) Save() error

type HistoryConfig added in v1.1.0

type HistoryConfig struct {
	Path    string `toml:"path"`
	MaxSize int    `toml:"max_size"`
}

HistoryConfig controls query history behaviour.

type JsonManager

type JsonManager struct {
	// contains filtered or unexported fields
}

func NewJsonManager

func NewJsonManager(reader io.Reader) (*JsonManager, error)

func (*JsonManager) Get

func (jm *JsonManager) Get(q QueryInterface, confirm bool) (string, []string, []string, error)

func (*JsonManager) GetCandidateKeys

func (jm *JsonManager) GetCandidateKeys(q QueryInterface) []string

func (*JsonManager) GetFilteredData

func (jm *JsonManager) GetFilteredData(q QueryInterface, confirm bool) (*simplejson.Json, []string, []string, error)

func (*JsonManager) GetPretty

func (jm *JsonManager) GetPretty(q QueryInterface, confirm bool) (string, []string, []string, error)

type KeybindingsConfig added in v1.1.0

type KeybindingsConfig struct {
	HistoryPrev    string `toml:"history_prev"`
	HistoryNext    string `toml:"history_next"`
	CandidateNext  string `toml:"candidate_next"` // cycle forward (default: tab)
	CandidatePrev  string `toml:"candidate_prev"` // cycle backward (additional key; Shift+Tab always works)
	ScrollDown     string `toml:"scroll_down"`
	ScrollUp       string `toml:"scroll_up"`
	ScrollToBottom string `toml:"scroll_to_bottom"`
	ScrollToTop    string `toml:"scroll_to_top"`
	ScrollPageDown string `toml:"scroll_page_down"`
	ScrollPageUp   string `toml:"scroll_page_up"`
	ToggleKeymode  string `toml:"toggle_keymode"`
	DeleteLine     string `toml:"delete_line"`
	DeleteWord     string `toml:"delete_word"`
	CursorLeft     string `toml:"cursor_left"`
	CursorRight    string `toml:"cursor_right"`
	CursorToStart  string `toml:"cursor_to_start"`
	CursorToEnd    string `toml:"cursor_to_end"`
	ToggleFuncHelp string `toml:"toggle_func_help"`
	Quit           string `toml:"quit"`
}

KeybindingsConfig maps action names to key strings (e.g. "ctrl+j", "up").

type Query

type Query struct {
	// contains filtered or unexported fields
}

func NewQuery

func NewQuery(query []rune) *Query

func NewQueryWithString

func NewQueryWithString(query string) *Query

func (*Query) Add

func (q *Query) Add(query []rune) []rune

func (*Query) Clear

func (q *Query) Clear() []rune

func (*Query) Delete

func (q *Query) Delete(i int) []rune

func (*Query) Get

func (q *Query) Get() []rune

func (*Query) GetChar

func (q *Query) GetChar(idx int) rune

func (*Query) GetKeywords

func (q *Query) GetKeywords() [][]rune

func (*Query) GetLastKeyword

func (q *Query) GetLastKeyword() []rune

func (*Query) IndexOffset

func (q *Query) IndexOffset(i int) int

func (*Query) Insert

func (q *Query) Insert(query []rune, idx int) []rune

func (*Query) Length

func (q *Query) Length() int

func (*Query) PopKeyword

func (q *Query) PopKeyword() ([]rune, []rune)

func (*Query) Set

func (q *Query) Set(query []rune) []rune

func (*Query) StringAdd

func (q *Query) StringAdd(query string) string

func (*Query) StringGet

func (q *Query) StringGet() string

func (*Query) StringGetKeywords

func (q *Query) StringGetKeywords() []string

func (*Query) StringGetLastKeyword

func (q *Query) StringGetLastKeyword() string

func (*Query) StringInsert

func (q *Query) StringInsert(query string, idx int) string

func (*Query) StringPopKeyword

func (q *Query) StringPopKeyword() (string, []rune)

func (*Query) StringSet

func (q *Query) StringSet(query string) string

type QueryInterface

type QueryInterface interface {
	Get() []rune
	Set(query []rune) []rune
	Insert(query []rune, idx int) []rune
	Add(query []rune) []rune
	Delete(i int) []rune
	Clear() []rune
	Length() int
	IndexOffset(int) int
	GetChar(int) rune
	GetKeywords() [][]rune
	GetLastKeyword() []rune
	PopKeyword() ([]rune, []rune)
	StringGet() string
	StringSet(query string) string
	StringInsert(query string, idx int) string
	StringAdd(query string) string
	StringGetKeywords() []string
	StringGetLastKeyword() string
	StringPopKeyword() (string, []rune)
}

type Suggestion

type Suggestion struct {
}

func NewSuggestion

func NewSuggestion() *Suggestion

func (*Suggestion) Get

func (s *Suggestion) Get(json *simplejson.Json, keyword string) []string

func (*Suggestion) GetCandidateKeys

func (s *Suggestion) GetCandidateKeys(json *simplejson.Json, keyword string) []string

func (*Suggestion) GetCurrentType

func (s *Suggestion) GetCurrentType(json *simplejson.Json) SuggestionDataType

func (*Suggestion) GetFunctionCandidates added in v1.0.0

func (s *Suggestion) GetFunctionCandidates(prefix string) []string

GetFunctionCandidates returns JMESPath function names (with trailing "(") that match the given prefix (case-insensitive).

func (*Suggestion) GetFunctionCandidatesFiltered added in v1.0.0

func (s *Suggestion) GetFunctionCandidatesFiltered(prefix string, t SuggestionDataType) []string

GetFunctionCandidatesFiltered returns function candidates filtered by the given data type. UNKNOWN means all functions are shown.

func (*Suggestion) GetFunctionSuggestion added in v1.0.0

func (s *Suggestion) GetFunctionSuggestion(prefix string) []string

GetFunctionSuggestion returns [completion, suggestion] for function-name autocompletion, mirroring the return format of Get().

func (*Suggestion) GetFunctionSuggestionFiltered added in v1.0.0

func (s *Suggestion) GetFunctionSuggestionFiltered(prefix string, t SuggestionDataType) []string

GetFunctionSuggestionFiltered returns [completion, suggestion] filtered by the given data type.

type SuggestionDataType

type SuggestionDataType int
const (
	UNKNOWN SuggestionDataType = iota
	ARRAY
	MAP
	NUMBER
	STRING
	BOOL
)

type SuggestionInterface

type SuggestionInterface interface {
	Get(json *simplejson.Json, keyword string) []string
	GetCandidateKeys(json *simplejson.Json, keyword string) []string
	GetFunctionCandidates(prefix string) []string
	GetFunctionSuggestion(prefix string) []string
}

type Terminal

type Terminal struct {
	// contains filtered or unexported fields
}

func NewTerminal

func NewTerminal(prompt string, defaultY int, monochrome bool) *Terminal

func (*Terminal) Draw

func (t *Terminal) Draw(attr *TerminalDrawAttributes) error

type TerminalDrawAttributes

type TerminalDrawAttributes struct {
	Query                   string
	Contents                []string
	CandidateIndex          int
	ContentsOffsetY         int
	Complete                string
	Candidates              []string
	CursorOffset            int
	FuncHelp                string
	PlaceholderStart        int // rune index in Query; -1 if no placeholder
	PlaceholderLen          int
	SelectedCandidate       string // field name to highlight in JSON; "" if none
	SelectedCandidateIndent int    // indentation level of the target key
}

Directories

Path Synopsis
cmd
jid command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL