Skip to content

I60R/page

Repository files navigation

Page

Rust Build Lines Of Code

Allows you to redirect text into neovim. You can set it as $PAGER to view logs, diffs, various command outputs.

ANSI escape sequences will be interpreted by :term buffer, which makes page noticeably faster than vimpager and nvimpager. And text will be displayed instantly as it arrives - no need to wait until EOF.

Also, text from neovim :term buffer will be redirected directly into a new buffer in the same neovim instance - no nested neovim will be spawned. That's by utilizing $NVIM variable like neovim-remote does.

Bonus: another binary named nv is included, which reimplements neovim-remote but with interface similar to page. There's no intention to have all nvim --remote features — it should be only a simple file picker that prevents spawning nested neovim instance. Also, in contrast with neovim-remote there are some safeguards e.g. it won't open non-text files unless explicit flag is provided for that so nv * opens only text files in current directory. I recommend to read --help output and experiment with options a bit.

Ultimately, page and nv reuses all of neovim's text editing+navigating+searching facilities and will either facilitate all of plugins+mappings+options set in your neovim config.

Usage

  • under regular terminal

usage under regular terminal

  • under neovim's terminal

usage under neovim's terminal


CLI

expand page --help
Usage: page [OPTIONS] [FILE]... Arguments: [FILE]... Open provided file in separate buffer [without other flags revokes implied by default -o or -p option] Options: -o Create and use output buffer (to redirect text from page's stdin) [implied by default unless -x and/or <FILE> provided without other flags] -O [<NOOPEN_LINES>] Prefetch <NOOPEN_LINES> from page's stdin: if all input fits then print it to stdout and exit without neovim usage (to emulate `less --quit-if-one-screen`) [empty: term height - 3 (space for prompt); negative: term height - <NOOPEN_LINES>; 0: disabled and default; ignored with -o, -p, -x and when page isn't piped] -p Print path of pty device associated with output buffer (to redirect text from commands respecting output buffer size and preserving colors) [implied if page isn't piped unless -x and/or <FILE> provided without other flags] -P Set $PWD as working directory at output buffer (to navigate paths with `gf`) -q [<QUERY_LINES>] Read no more than <QUERY_LINES> from page's stdin: next lines should be fetched by invoking :Page <QUERY> command or 'r'/'R' keypress on neovim side [empty: term height - 2 (space for tab and buffer lines); negative: term height - <QUERY_LINES>; 0: disabled and default; <QUERY> is optional and defaults to <QUERY_LINES>; doesn't take effect on <FILE> buffers] -f Cursor follows content of output buffer as it appears instead of keeping top position (like `tail -f`) -F Cursor follows content of output and <FILE> buffers as it appears instead of keeping top position -t <FILETYPE> Set filetype on output buffer (to enable syntax highlighting) [pager: default; not works with text echoed by -O] -b Return back to current buffer -B Return back to current buffer and enter into INSERT/TERMINAL mode -n <NAME> Set title for output buffer (to display it in statusline) [env: PAGE_BUFFER_NAME=] -w Do not remap i, I, a, A, u, d, x, q (and r, R with -q) keys [wouldn't unmap on connected instance output buffer] -z [<PAGERIZE>] Pagerize output when it exceeds <PAGERIZE> lines (to view `journalctl`) [default: disabled; empty: 100_000] ~ ~ ~ ~ ~ ~ -a <ADDRESS> TCP/IP socked address or path to named pipe listened by running host neovim process [env: NVIM=/run/user/1000/nvim.9389.0] -A <ARGUMENTS> Arguments that will be passed to child neovim process spawned when <ADDRESS> is missing [env: NVIM_PAGE_ARGS=] -c <CONFIG> Config that will be used by child neovim process spawned when <ADDRESS> is missing [file:$XDG_CONFIG_HOME/page/init.vim] -C Enable PageConnect PageDisconnect autocommands -e <COMMAND> Run command on output buffer after it was created --e <LUA> Run lua expr on output buffer after it was created -E <COMMAND_POST> Run command on output buffer after it was created or connected as instance --E <LUA_POST> Run lua expr on output buffer after it was created or connected as instance ~ ~ ~ -i <INSTANCE> Create output buffer with <INSTANCE> tag or use existed with replacing its content by text from page's stdin -I <INSTANCE_APPEND> Create output buffer with <INSTANCE_APPEND> tag or use existed with appending to its content text from page's stdin -x <INSTANCE_CLOSE> Close output buffer with <INSTANCE_CLOSE> tag if it exists [without other flags revokes implied by defalt -o or -p option] ~ ~ ~ -W Flush redirection protection that prevents from producing junk and possible overwriting of existed files by invoking commands like `ls > $(NVIM= page -E q)` where the RHS of > operator evaluates not into /path/to/pty as expected but into a bunch of whitespace-separated strings/escape sequences from neovim UI; bad things happens when some shells interpret this as many valid targets for text redirection. The protection is only printing of a path to the existed dummy directory always first before printing of a neovim UI might occur; this makes the first target for text redirection from page's output invalid and disrupts the whole redirection early before other harmful writes might occur. [env:PAGE_REDIRECTION_PROTECT; (0 to disable)] ~ ~ ~ -l... Split left with ratio: window_width * 3 / (<l-PROVIDED> + 1) -r... Split right with ratio: window_width * 3 / (<r-PROVIDED> + 1) -u... Split above with ratio: window_height * 3 / (<u-PROVIDED> + 1) -d... Split below with ratio: window_height * 3 / (<d-PROVIDED> + 1) -L <SPLIT_LEFT_COLS> Split left and resize to <SPLIT_LEFT_COLS> columns -R <SPLIT_RIGHT_COLS> Split right and resize to <SPLIT_RIGHT_COLS> columns -U <SPLIT_ABOVE_ROWS> Split above and resize to <SPLIT_ABOVE_ROWS> rows -D <SPLIT_BELOW_ROWS> Split below and resize to <SPLIT_BELOW_ROWS> rows ^ -+ With any of -r -l -u -d -R -L -U -D open floating window instead of split [to not overwrite data in the current terminal] ~ ~ ~ -h, --help Print help information
expand nv --help
Usage: nv [OPTIONS] [FILE]... Arguments: [FILE]... Open provided files as editable [if none provided nv opens last modified file in currend directory] Options: -o Open non-text files including directories, binaries, images etc -O [<RECURSE_DEPTH>] Ignoring [FILE] open all text files in the current directory and recursively open all text files in its subdirectories [0: disabled and default; empty: defaults to 1 and implied if no <RECURSE_DEPTH> provided; <RECURSE_DEPTH>: also opens in subdirectories at this level of depth] -v Open in `page` instead (just postfix shortcut) ~ ~ ~ -f Open each [FILE] at last line -p <PATTERN> Open and search for a specified <PATTERN> -P <PATTERN_BACKWARDS> Open and search backwars for a specified <PATTERN_BACKWARDS> -b Return back to current buffer -B Return back to current buffer and enter into INSERT/TERMINAL mode -k Keep `nv` process until buffer is closed (for editing git commit message) -K Keep `nv` process until first write occur, then close buffer and neovim if it was spawned by `nv` ~ ~ ~ -a <ADDRESS> TCP/IP socket address or path to named pipe listened by running host neovim process [env: NVIM=/run/user/1000/nvim.604327.0] -A <ARGUMENTS> Arguments that will be passed to child neovim process spawned when <ADDRESS> is missing [env: NVIM_PAGE_PICKER_ARGS=] -c <CONFIG> Config that will be used by child neovim process spawned when <ADDRESS> is missing [file: $XDG_CONFIG_HOME/page/init.vim] -t <FILETYPE> Override filetype on each [FILE] buffer (to enable custom syntax highlighting [text: default] ~ ~ ~ -e <COMMAND> Run command on each [FILE] buffer after it was created --e <LUA> Run lua expr on each [FILE] buffer after it was created -x <COMMAND_ONLY> Just run command with ignoring all other options --x <LUA_ONLY> Just run lua expr with ignoring all other options ~ ~ ~ -l... Split left with ratio: window_width * 3 / (<l-PROVIDED> + 1) -r... Split right with ratio: window_width * 3 / (<r-PROVIDED> + 1) -u... Split above with ratio: window_height * 3 / (<u-PROVIDED> + 1) -d... Split below with ratio: window_height * 3 / (<d-PROVIDED> + 1) -L <SPLIT_LEFT_COLS> Split left and resize to <SPLIT_LEFT_COLS> columns -R <SPLIT_RIGHT_COLS> Split right and resize to <SPLIT_RIGHT_COLS> columns -U <SPLIT_ABOVE_ROWS> Split above and resize to <SPLIT_ABOVE_ROWS> rows -D <SPLIT_BELOW_ROWS> Split below and resize to <SPLIT_BELOW_ROWS> rows ^ -+ With any of -r -l -u -d -R -L -U -D open floating window instead of split [to not overwrite data in the current terminal] ~ ~ ~ -h, --help Print help information

Note: page and nv may be unergonomic to type so I suggest users to create alias like p and v

nvim/init.lua customizations

-- Opacity of popup window spawned with -+ option vim.g.page_popup_winblend = 25

nvim/init.lua customizations (pager only)

Statusline appearance:

-- String that will append to buffer name vim.g.page_icon_pipe = '|' -- When piped vim.g.page_icon_redirect = '>' -- When exposes pty device vim.g.page_icon_instance = '$' -- When `-i, -I` flags provided

Autocommand hooks:

-- Will run once when output buffer is created vim.api.create_autocmd('User', { pattern = 'PageOpen', callback = lua_function, }) -- Will run once when file buffer is created vim.api.create_autocmd('User', { pattern = 'PageOpenFile', callback = lua_function, })

Only with -C option provided:

-- will run always when output buffer is created -- and also when `page` connects to instance `-i, -I` buffers: vim.api.create_autocmd('User', { pattern = 'PageConnect', callback = lua_function, }) -- Will run when page process exits vim.api.create_autocmd('User', { pattern = 'PageDisconnect', callback = lua_function, })

Shell hacks

To use as $PAGER without scrollback overflow:

export PAGER="page -q 90000" # Alternatively export PAGER="page -z 90000" # will pagerize output # And you can combine both export PAGER="page -q 90000 -z 90000"

To configure:

export PAGER="page -WfC -q 90000 -z 90000" # some sensible flags alias page="$PAGER" # Usage ls | page -q 100 # you can specify the same flag multiple times: # last provided will override previous

To use as $MANPAGER:

export MANPAGER="page -t man" # Alternatively, to pick a bit better `man` highlighting: man () { PROGRAM="${@[-1]}" SECTION="${@[-2]}" page -W "man://$PROGRAM${SECTION:+($SECTION)}" }

To set nv as popup git commit message editor:

# Will spawn popup editor and exit on first write git config --global core.editor "nv -K -+-R 80 -B"

To cd into directory passed to nv

nv() { #stdin_is_term #one_argument #it's_dir if [ -t 1 ] && [ 1 -eq $# ] && [ -d $1 ]; then cd $1 else nv $* fi } compdef _nv nv # if you have completions installed

To automatically lcd into terminal's directory:

chpwd () { [ ! -z "$NVIM" ] && nv -x "lcd $PWD" }

To circumvent neovim config picking:

page -c NONE # Alternatively, to override neovim config create this file: touch $XDG_CONFIG_HOME/page/init.lua # init.vim is also supported

To set output buffer name as first two words from invoked command (zsh only):

preexec () { if [ -z "$NVIM" ]; then export PAGE_BUFFER_NAME="page" else WORDS=(${1// *|*}) export PAGE_BUFFER_NAME="${WORDS[@]:0:2}" fi }

Buffer defaults (pager)

expand

These commands are run on each page buffer creation:

vim.b.page_alternate_bufnr = {$initial_buf_nr} if vim.wo.scrolloff > 999 or vim.wo.scrolloff < 0 then vim.g.page_scrolloff_backup = 0 else vim.g.page_scrolloff_backup = vim.wo.scrolloff end vim.bo.scrollback, vim.wo.scrolloff, vim.wo.signcolumn, vim.wo.number = 100000, 999, 'no', false {$filetype} {$edit} vim.api.nvim_create_autocmd('BufEnter', { buffer = 0, callback = function() vim.wo.scrolloff = 999 end }) vim.api.nvim_create_autocmd('BufLeave', { buffer = 0, callback = function() vim.wo.scrolloff = vim.g.page_scrolloff_backup end }) {$notify_closed} {$pre} vim.cmd 'silent doautocmd User PageOpen | redraw' {$lua_provided_by_user} {$cmd_provided_by_user} {$after}

Where:

--{$initial_buf_nr} -- Is always set on all buffers created by page 'number of parent :term buffer or -1 when page isn't spawned from :term'
--{$filetype} -- Is set only on output buffers. -- On files buffers filetypes are detected automatically. vim.bo.filetype='value of -t argument or "pager"'
--{$edit} -- Is appended when no -w option provided vim.bo.modifiable = false _G.page_echo_notification = function(message) vim.defer_fn(function() local msg = "-- [PAGE] " .. message .. " --" vim.api.nvim_echo({{ msg, 'Comment' }, }, false, {}) vim.cmd 'au CursorMoved <buffer> ++once echo' end, 64) end _G.page_bound = function(top, message, move) local row, col, search if top then row, col, search = 1, 1, { '\\S', 'c' } else row, col, search = 9999999999, 9999999999, { '\\S', 'bc' } end vim.api.nvim_call_function('cursor', { row, col }) vim.api.nvim_call_function('search', search) if move ~= nil then move() end _G.page_echo_notification(message) end _G.page_scroll = function(top, message) vim.wo.scrolloff = 0 local move if top then local key = vim.api.nvim_replace_termcodes('z<CR>M', true, false, true) move = function() vim.api.nvim_feedkeys(key, 'nx', true) end else move = function() vim.api.nvim_feedkeys('z-M', 'nx', false) end end _G.page_bound(top, message, move) vim.wo.scrolloff = 999 end _G.page_close = function() local buf = vim.api.nvim_get_current_buf() if buf ~= vim.b.page_alternate_bufnr and vim.api.nvim_buf_is_loaded(vim.b.page_alternate_bufnr) then vim.api.nvim_set_current_buf(vim.b.page_alternate_bufnr) end vim.api.nvim_buf_delete(buf, { force = true }) local exit = true for _, b in ipairs(vim.api.nvim_list_bufs()) do local bt = vim.api.nvim_buf_get_option(b, 'buftype') if bt == "" or bt == "acwrite" or bt == "terminal" or bt == "prompt" then local bm = vim.api.nvim_buf_get_option(b, 'modified') if bm then exit = false break end local bl = vim.api.nvim_buf_get_lines(b, 0, -1, false) if #bl ~= 0 and bl[1] ~= "" and #bl > 1 then exit = false break end end end if exit then vim.cmd "qa!" end end local function page_map(key, expr) vim.api.nvim_buf_set_keymap(0, '', key, expr, { nowait = true }) end page_map('I', '<CMD>lua _G.page_scroll(true, "in the beginning of scroll")<CR>') page_map('A', '<CMD>lua _G.page_scroll(false, "at the end of scroll")<CR>') page_map('i', '<CMD>lua _G.page_bound(true, "in the beginning")<CR>') page_map('a', '<CMD>lua _G.page_bound(false, "at the end")<CR>') page_map('q', '<CMD>lua _G.page_close()<CR>') page_map('u', '<C-u>') page_map('d', '<C-d>') page_map('x', 'G')
--{$notify_closed} -- Is set only on output buffers local closed = 'rpcnotify({channel}, "page_buffer_closed", "{page_id}")' vim.api.nvim_create_autocmd('BufDelete', { buffer = 0, command = 'silent! call ' .. closed })
--{$pre} -- Is appended when -q provided vim.b.page_query_size = {$query_lines_count} local def_args = '{channel}, "page_fetch_lines", "{page_id}", ' local def = 'command! -nargs=? Page call rpcnotify(' .. def_args .. '<args>)' vim.cmd(def) vim.api.create_autocmd('BufEnter', { buffer = 0, command = def, }) -- Also if -q provided and no -w provided page_map('r', '<CMD>call rpcnotify(' .. def_args .. 'b:page_query_size * v:count1)<CR>') page_map('R', '<CMD>call rpcnotify(' .. def_args .. '99999)<CR>') -- If -P provided ({pwd} is $PWD value) vim.b.page_lcd_backup = getcwd() vim.cmd 'lcd {pwd}' vim.api.nvim_create_autocmd('BufEnter', { buffer = 0, command = 'lcd {pwd}' }) vim.api.nvim_create_autocmd('BufLeave', { buffer = 0, command = 'exe "lcd" . b:page_lcd_backup' })
--{$lua_provided_by_user} -- Is appended when --e provided 'value of --e flag'
--{$cmd_provided_by_user} -- Is appended when -e provided vim.cmd [====[{$command}]====]
--{$after} -- Is appended only on file buffers vim.api.nvim_exec_autocmds('User', { pattern = 'PageOpenFile', })

Limitations (pager)

Installation

  • From binaries

    • Grab binary for your platform from releases (currently Linux and OSX are supported)
  • Arch Linux:

    • Package page-git is available on AUR
    • Or: git clone git@github.com:I60R/page.git && cd page && makepkg -ef && sudo pacman -U page-git*.pkg.tar.xz
  • Homebrew:

  • Manually:

    • Install rustup from your distribution package manager
    • Configure toolchain: rustup install stable && rustup default stable
    • git clone git@github.com:I60R/page.git && cd page && cargo install --path .

About

Use neovim as pager

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors