Using Command Line Tools in Neovim
Running External Commands
Read Command Output into Buffer
:r !command " Read command output at cursor position
:r !ls " Insert directory listing
:r !date " Insert current date
:r !echo "Hello World" " Insert text
Run Command on Current File
:!command % " % represents current filename
:!cat % " Show file contents in terminal
:!wc -l % " Count lines in file
:!python % " Run current Python file
Filter Buffer Through Command
:%!command " Filter entire buffer through command
:%!sort " Sort all lines
:%!uniq " Remove duplicate lines
:%!column -t " Format as columns
Filter Visual Selection
" 1. Select text in visual mode (V for lines, v for characters)
" 2. Type :
" 3. Command automatically shows: :'<,'>
:'<,'>!sort " Sort selected lines
:'<,'>!column -t " Format selection as table
Filter Specific Line Range
:5,10!sort " Sort lines 5-10
:.,$!command " Current line to end of file
Working with JSON (jq)
Format JSON in Current Buffer
:%!jq '.' " Pretty-print entire buffer
Query Current File into New Buffer
Basic Pattern:
:enew | r !jq 'QUERY' #
:enew- Create new empty buffer|- Chain commandsr !jq 'QUERY' #- Read jq output#- Refers to alternate buffer (previous file)
Examples:
" Get all names
:enew | r !jq '.results[].name' #
" Get names and types
:enew | r !jq '.results[] | {name: .name, type: .type}' #
" Get unique names
:enew | r !jq '[.results[].name] | unique | .[]' #
" Create custom structure
:enew | r !jq '{results: [.results[] | {name: .name, type: .type}]}' #
Using Current Filename Explicitly
When # doesn’t work or you need more control:
:let filename = expand('%') | enew | execute 'r !jq ''.results[].name'' ' . shellescape(filename)
Breakdown:
:let filename = expand('%')- Save current filename| enew- Create new buffer| execute 'r !jq ...'- Run jq with saved filename
Check JSON Structure
Before querying, understand the structure:
:!jq 'keys' % " Show top-level keys
:!jq '. | type' % " Check root type (object/array)
:!jq '.[0] | keys' % " Show keys of first element
:!jq '.results[0]' % " Preview first result
Common jq Patterns
" Compact output (one line per object)
:enew | r !jq -c '.results[]' #
" Raw string output (no JSON quotes)
:enew | r !jq -r '.results[] | "\(.name): \(.type | join(", "))"' #
" Filter by condition
:enew | r !jq '.results[] | select(.legendary == true)' #
" Count results
:!jq '.results | length' %
" Get specific field from all items
:enew | r !jq '[.results[].name]' #
Custom Commands for jq
For init.vim
Add to ~/.config/nvim/init.vim:
" Format current buffer as JSON
command! JsonFormat %!jq '.'
" Query current JSON file (opens in new buffer)
command! -nargs=1 Jq let filename = expand('%') | enew | execute 'r !jq ' . shellescape(<q-args>) . ' ' . shellescape(filename)
" Compact JSON format
command! JsonCompact %!jq -c '.'
" Check JSON validity
command! JsonCheck !jq empty %
Usage:
:JsonFormat
:Jq '.results[].name'
:Jq '.results[] | select(.legendary == true)'
:JsonCompact
:JsonCheck
For init.lua
Add to ~/.config/nvim/init.lua:
-- Format current buffer as JSON
vim.api.nvim_create_user_command('JsonFormat', function()
vim.cmd('%!jq \'.\'')
end, {})
-- Query current JSON file
vim.api.nvim_create_user_command('Jq', function(opts)
local filename = vim.fn.expand('%')
vim.cmd('enew')
vim.cmd('r !jq ' .. vim.fn.shellescape(opts.args) .. ' ' .. vim.fn.shellescape(filename))
end, { nargs = 1 })
-- Compact JSON
vim.api.nvim_create_user_command('JsonCompact', function()
vim.cmd('%!jq -c \'.\'')
end, {})
-- Check JSON validity
vim.api.nvim_create_user_command('JsonCheck', function()
vim.cmd('!jq empty %')
end, {})
Practical Workflows
Quick JSON Analysis
1. :e data.json " Open file
2. :!jq 'keys' % " Check structure
3. :!jq '.[0]' % " Preview first item
4. :enew | r !jq '.results[].name' # " Extract data
5. gg " Jump to top
Format and Save JSON
:e messy.json
:%!jq '.'
:w
Compare Multiple Queries
" Open source file
:e data.json
" First query in vertical split
:vnew | r !jq '.results[] | select(.region == "Kanto")' #
" Second query in another split
:vnew | r !jq '.results[] | select(.legendary == true)' #
" Navigate between splits with Ctrl-w h/l
Extract Subset to New File
:e source.json
:enew | r !jq '{results: [.results[0:10]]}' # | w subset.json
Working with Other CLI Tools
Git Integration
:r !git log --oneline -5 " Insert recent commits
:r !git diff % " Insert diff of current file
:!git blame % " Show git blame
Markdown/Text Processing
:%!pandoc -f markdown -t html " Convert markdown to HTML
:%!fmt -w 80 " Wrap text to 80 characters
:'<,'>!sort | uniq -c " Count occurrences in selection
CSV/Data Processing
:%!column -t -s',' " Format CSV as table
:r !head -20 data.csv " Read first 20 lines
Code Formatting
:%!prettier --parser json " Format with Prettier
:%!black - " Format Python (stdin)
:%!rustfmt " Format Rust
Using Shell Substitutions
:r !ls %:h " List files in current file's directory
:!wc -l %:p " Full path of current file
Modifiers:
%:h- Head (directory)%:t- Tail (filename)%:r- Root (without extension)%:e- Extension%:p- Full path
Advanced Patterns
Pipe Chain Multiple Commands
:%!sort | uniq | wc -l " Sort, unique, count
:%!grep "pattern" | sort " Filter and sort
Conditional Processing
" Only format if file is valid JSON
:if system('jq empty ' . expand('%')) == '' | %!jq '.' | endif
Read Command with Arguments
:r !curl -s https://api.example.com/data " Fetch API data
:r !cat file1.txt file2.txt " Combine files
Tips
- Test jq queries in terminal first - Build and verify before using in Neovim
- Use
:!jq '.[0]' %to preview structure before extracting data - The
#buffer reference only works right after creating new buffer - Use
expand('%')when you need the filename in complex commands - Chain with
|to create multi-step workflows - Visual select then
:to automatically get'<,'> - Create custom commands for frequently used patterns
Common Issues
Command Not Found
" Check if tool is available
:!which jq
" Use full path if needed
:%!/usr/local/bin/jq '.'
Quotes in Commands
" Use double quotes on outside, single inside
:!jq ".results[] | select(.name == \"Pikachu\")" %
" Or escape appropriately
:!jq '.results[] | select(.name == "Pikachu")' %
Large Output
" Limit output before reading
:r !jq '.results[0:100]' file.json
" Or use head
:r !jq '.results[]' file.json | head -50
Resources
:help :!- External commands:help :r- Read command:help expand()- Filename modifiers:help shellescape()- Escaping for shell
Tags: #neovim #cli