pax_global_header 0000666 0000000 0000000 00000000064 15163401063 0014511 g ustar 00root root 0000000 0000000 52 comment=74cdd9ebc9caf79e47a3f01b81815b9901c61b88
nvimpager-0.14.0/ 0000775 0000000 0000000 00000000000 15163401063 0013563 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/.github/ 0000775 0000000 0000000 00000000000 15163401063 0015123 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15163401063 0017160 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/.github/workflows/test.yml 0000664 0000000 0000000 00000007337 15163401063 0020674 0 ustar 00root root 0000000 0000000 name: Run tests
on:
push:
pull_request:
schedule:
- cron: "0 0 1 * *" # every month
jobs:
ubuntu-ppa:
runs-on: ubuntu-latest
strategy:
matrix:
ppa:
- false
- true
steps:
- uses: actions/checkout@v6
- name: activate the PPA for latest neovim
run: sudo add-apt-repository -y ppa:neovim-ppa/unstable
if: matrix.ppa
- name: install dependencies
run: |
sudo apt-get update
sudo apt-get install -yqq --no-install-recommends neovim scdoc lua-busted
- name: Run the test suite
run: make test BUSTED='busted --exclude-tags=ppa,v10'
env:
TERM: dumb
appimage:
runs-on: ubuntu-latest
strategy:
matrix:
x:
- version: v0.9.0
file: nvim
hash: 0e1e6d53c6c8055de23bdb33f60bb64af0baf11390669c1b40ecbbf2c7a34547
tags: [appimage]
- version: v0.10.0
file: nvim
hash: 6a021e9465fe3d3375e28c3e94c1c2c4f7d1a5a67e4a78cf52d18d77b1471390
tags: [appimage, v10]
- version: v0.11.0
file: nvim-linux-x86_64
hash: ca44cd43fe8d55418414496e8ec7bac83f611705ece167f4ccb93cbf46fec6c0
tags: [appimage, v10]
- version: v0.12.0
file: nvim-linux-x86_64
hash: 7876b67462af08abdc884818b398b3e82907d6a4c89edfe7c6b1ff168eb7c4d6
tags: [appimage, v10]
- version: nightly
file: nvim-linux-x86_64
tags: [appimage, v10]
steps:
- uses: actions/checkout@v6
- name: install dependencies
run: |
sudo apt-get update
sudo apt-get install -yqq --no-install-recommends scdoc lua-busted
- name: Download official neovim appimage
run: |
wget --no-verbose https://github.com/neovim/neovim/releases/download/${{ matrix.x.version }}/${{ matrix.x.file }}.appimage
if [ -n '${{ matrix.x.hash }}' ]; then
echo ${{ matrix.x.hash }} ${{ matrix.x.file }}.appimage | sha256sum -c -
fi
chmod +x ${{ matrix.x.file }}.appimage
./${{ matrix.x.file }}.appimage --appimage-extract
- name: Run the test suite
run: make test "BUSTED=busted --exclude-tags=${{ join(matrix.x.tags, ',') }}"
env:
TERM: dumb
NVIMPAGER_NVIM: squashfs-root/usr/bin/nvim
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: install dependencies
run: |
brew update
brew install neovim scdoc luarocks
luarocks --local install busted
- name: Run the test suite
run: make test BUSTED="$HOME/.luarocks/bin/busted --exclude-tags=mac,v10"
env:
TERM: dumb
macos-nix:
runs-on: macos-latest
steps:
- name: Install Nix
uses: cachix/install-nix-action@v31
- uses: cachix/cachix-action@v17
with:
name: nix-community
- uses: actions/checkout@v6
- name: Run the test suite via nix
run: nix build --print-build-logs
nix:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- default
- nightly
update:
- false
- true
exclude:
- package: nightly
update: false
steps:
- name: Install Nix
uses: cachix/install-nix-action@v31
- uses: cachix/cachix-action@v17
with:
name: nix-community
- uses: actions/checkout@v6
- name: Update the fake inputs
run: nix flake update
if: matrix.update
- name: Run the test suite with ${{ matrix.package }} neovim
run: nix build --print-build-logs '.#${{ matrix.package }}'
nvimpager-0.14.0/.gitignore 0000664 0000000 0000000 00000000066 15163401063 0015555 0 ustar 00root root 0000000 0000000 luacov.*.out
nvimpager.1
nvimpager.configured
result*
nvimpager-0.14.0/LICENSE 0000664 0000000 0000000 00000002377 15163401063 0014601 0 ustar 00root root 0000000 0000000 Copyright (c) 2017 Lucas Hoffmann
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nvimpager-0.14.0/README.md 0000664 0000000 0000000 00000007674 15163401063 0015060 0 ustar 00root root 0000000 0000000 # Nvimpager
Using [neovim] as a pager to view man pages, git diffs, whatnot with neovim's
syntax highlighting and mouse support.
## About
The `nvimpager` script calls neovim in a fashion that turns it into something
like a pager. The idea is not new, this is actually rewrite of [vimpager] but
with less (but stricter) dependencies and specifically for neovim.
Some typical use cases:
```sh
# view a file in nvimpager
nvimpager file
# pipe text to nvimpager
echo some text | nvimpager
# use it as your default $PAGER
export PAGER=nvimpager
man bash
git diff
```
The script also has a "cat mode" which will not start up the neovim interface
but instead print a highlighted version of the file to the terminal. Like cat
with neovim syntax highlighting! If the input has less lines than the terminal
cat mode is activated automatically so nvimpager behaves similar to `less -F`.
Pager mode and cat mode can be enforced with the options `-p` and `-c`
respectively.
Nvimpager comes with a small set of command line options but you can also use
all of neovim's command line options. Use `nvimpager -h` to see the [help
text][options]. The configuration is separated from the users config for
neovim. The main config file is `~/.config/nvimpager/init.vim` (or `.lua`).
See [the manpage][configuration] for further explanation.
## Installation
Nvimpager is already packaged for some distributions. If not for yours, you can
install it manually, read on.
### Dependencies
* [neovim] ≥ v0.9.0
* [bash]
* [busted] (for running the tests)
* [scdoc] (to build the man page)
### Installation instructions
Use the makefile to configure and install the script. It supports the usual
`PREFIX` (defaults to `/usr/local`) and `DESTDIR` (defaults to empty)
variables:
```sh
make PREFIX=$HOME/.local install
```
The target `install-no-man` can be used to install nvimpager without the man
page.
Additionally the variable `BUSTED` can be used to specify the executable for
the test suite:
```sh
make test BUSTED="/path/to/busted --some-args"
```
## Development
Nvimpager is developed on [GitHub][nvimpager] where you are very much invited
to [post][issues] bug reports, feature or pull requests! The test can be run
with `make test`. They are also run on GitHub: [![Build Status]][ghactions]
### Limitations
* if reading from stdin, nvimpager (like nvim) waits for EOF until it starts up
* large files are slowing down neovim on startup (less does a better, i.e.
faster and more memory efficient job at paging large files)
### Ideas
* see how [neovim#5035], [neovim#7438] and [neovim#23093] are resolved and
maybe move more code (logic) from bash to lua (bash's `[[ -t ... ]]` can be
replaced by `has('ttyin')`, `has('ttyout')`)
* proper lazy pipe reading while paging (like less) to improve startup time and
also memory usage for large input on pipes (maybe `stdioopen()` can be used?)
## License
The project is licensed under a BSD-2-clause license. See the
[LICENSE](./LICENSE) file.
[nvimpager]: https://github.com/lucc/nvimpager
[issues]: https://github.com/lucc/nvimpager/issues
[options]: ./nvimpager.md#command-line-options
[configuration]: ./nvimpager.md#configuration
[neovim]: https://github.com/neovim/neovim
[vimpager]: https://github.com/rkitover/vimpager
[bash]: https://www.gnu.org/software/bash/bash.html
[busted]: https://lunarmodules.github.io/busted/
[scdoc]: https://git.sr.ht/~sircmpwn/scdoc
[Build Status]: https://github.com/lucc/nvimpager/actions/workflows/test.yml/badge.svg
[ghactions]: https://github.com/lucc/nvimpager/actions
[neovim#5035]: https://github.com/neovim/neovim/issues/5035 (detach / reattach)
[neovim#7438]: https://github.com/neovim/neovim/issues/7438 (dynamic --headless)
[neovim#23093]: https://github.com/neovim/neovim/issues/23093 (detach current tui)
nvimpager-0.14.0/_nvimpager 0000664 0000000 0000000 00000000600 15163401063 0015631 0 ustar 00root root 0000000 0000000 #compdef nvimpager
typeset -A opt_args
local ret=1
local context curcontext="$curcontext" state line
local arguments
arguments=(
'(* -)-h[show the help text and exit]'
'(* -)-v[show version into and exit]'
'-p[pager mode (overrides -a, -c)]'
'-a[auto mode (overrides -c, -p)]'
'-c[cat mode (overrides -a, -p)]'
'(-)*:file:_files'
)
_arguments -C -S $arguments && ret=0
nvimpager-0.14.0/config.ld 0000664 0000000 0000000 00000000466 15163401063 0015357 0 ustar 00root root 0000000 0000000 -- Config file for ldoc, see
-- https://stevedonovan.github.io/ldoc/manual/doc.md.html
-- vim: filetype=lua
project = "nvimpager"
file = {
"lua",
"test",
exclude = {
"test/diff_test.lua",
"test/meta_test.lua",
"test/no_map_test.lua",
}}
all = true
readme = "README.md"
format = "markdown"
nvimpager-0.14.0/flake.lock 0000664 0000000 0000000 00000006662 15163401063 0015531 0 ustar 00root root 0000000 0000000 {
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"neovim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"neovim": {
"inputs": {
"flake-parts": "flake-parts",
"neovim-src": "neovim-src",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1775076062,
"narHash": "sha256-ruqxqJtdmNm/fmjuAdwtSBNcbBeMgE1hwELlUnAFgyU=",
"owner": "nix-community",
"repo": "neovim-nightly-overlay",
"rev": "215965fbe5b5dbd61bf33c8bda4a20c2b32c3df2",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "neovim-nightly-overlay",
"type": "github"
}
},
"neovim-src": {
"flake": false,
"locked": {
"lastModified": 1774915197,
"narHash": "sha256-yor+eo8CVi7wBp7CjAMQnVoK+m197gsl7MvUzaqicns=",
"owner": "neovim",
"repo": "neovim",
"rev": "dbc4800dda2b0dc3290dc79955f857256e0694e2",
"type": "github"
},
"original": {
"owner": "neovim",
"repo": "neovim",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1774701658,
"narHash": "sha256-CIS/4AMUSwUyC8X5g+5JsMRvIUL3YUfewe8K4VrbsSQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b63fe7f000adcfa269967eeff72c64cafecbbebe",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1775036866,
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"neovim": "neovim",
"nixpkgs": "nixpkgs_2"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
nvimpager-0.14.0/flake.nix 0000664 0000000 0000000 00000004662 15163401063 0015375 0 ustar 00root root 0000000 0000000 {
description = "Development flake for nvimpager";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
neovim.url = "github:nix-community/neovim-nightly-overlay";
};
outputs = { self, nixpkgs, flake-utils, neovim, ... }:
let
inherit (builtins) head match readFile;
inherit (flake-utils.lib) eachDefaultSystem;
version = head (match ".*version=([0-9.]*)\n.*" (readFile ./nvimpager))
+ "-dev-${toString self.sourceInfo.lastModifiedDate}";
inherit (nixpkgs.lib.strings) optionalString;
nvimpager = {
stdenv, neovim, ncurses, procps, scdoc, lua51Packages, util-linux
}:
stdenv.mkDerivation {
pname = "nvimpager";
inherit version;
src = self;
buildInputs = [
ncurses # for tput
procps # for nvim_get_proc() which uses ps(1)
];
nativeBuildInputs = [ scdoc ];
makeFlags = [ "PREFIX=$(out)" "VERSION=${version}" ];
buildFlags = [ "nvimpager.configured" "nvimpager.1" ];
preBuild = ''
patchShebangs nvimpager
substituteInPlace nvimpager --replace ':-nvim' ':-${neovim}/bin/nvim'
'';
doCheck = true;
nativeCheckInputs = [ lua51Packages.busted util-linux neovim ];
# filter out one test that fails in the sandbox of nix
preCheck = let
neovim-version = neovim.version or neovim.passthru.unwrapped.version;
neovim-new = null == match "^0\\.9\\..*" neovim-version;
exclude-tags = "nix" + optionalString stdenv.isDarwin ",mac"
+ optionalString neovim-new ",v10";
in ''
checkFlagsArray+=('BUSTED=busted --output TAP --exclude-tags=${exclude-tags}')
'';
};
in ({
overlays.default = final: prev: {
nvimpager = final.callPackage nvimpager {};
};
} // (eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
callPackage = pkgs.callPackage nvimpager;
neovim-nightly = neovim.packages.${system}.default;
default = callPackage {};
nightly = callPackage { neovim = neovim-nightly; };
ldoc = pkgs.runCommandLocal "nvimpager-api-docs" {}
"cd ${self} && ${pkgs.luaPackages.ldoc}/bin/ldoc . --dir $out";
in {
apps.default = flake-utils.lib.mkApp { drv = default; };
packages = { inherit default nightly ldoc neovim-nightly; inherit (pkgs) neovim; };
checks = { inherit default nightly ldoc; };
})));
}
nvimpager-0.14.0/lua/ 0000775 0000000 0000000 00000000000 15163401063 0014344 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/lua/nvimpager/ 0000775 0000000 0000000 00000000000 15163401063 0016334 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/lua/nvimpager/ansi2highlight.lua 0000664 0000000 0000000 00000027044 15163401063 0021752 0 ustar 00root root 0000000 0000000 --- Functions to convert terminal escape sequences to nvim highlight groups.
-- Neovim defines this object but luacheck doesn't know it. So we define a
-- shortcut and tell luacheck to ignore it.
local nvim = vim.api -- luacheck: ignore
-- A neovim highlight namespace to group together all highlights added to
-- buffers by this module.
local namespace
--- A cache to remember which syntax groups have already been defined.
local cache = {}
--- A mapping of ansi color numbers to neovim color names
local colors = {
[0] = "black", [8] = "darkgray",
[1] = "red", [9] = "lightred",
[2] = "green", [10] = "lightgreen",
[3] = "yellow", [11] = "lightyellow",
[4] = "blue", [12] = "lightblue",
[5] = "magenta", [13] = "lightmagenta",
[6] = "cyan", [14] = "lightcyan",
[7] = "lightgray", [15] = "white",
}
--- the names of neovim's highlighting attributes that are handled by this
--- module
--- Most attributes are referred to by their highlighting attribute name in
--- neovim's :highlight command.
local attributes = {
[1] = "bold",
--[2] = "faint", -- not handled by neovim
[3] = "italic",
[4] = "underline",
--[5] = "slow blink", -- not handled by neovim
--[6] = "underline", -- not handled by neovim
[7] = "reverse",
[8] = "conceal",
[9] = "strikethrough",
-- TODO when to use the gui attribute "standout"?
}
--- Format 24 bit RGB colors in hex notation
--- @param r integer
--- @param g integer
--- @param b integer
local function hexformat_rgb_numbers(r, g, b)
return string.format("#%06x", r * 2^16 + g * 2^8 + b)
end
--- Split one of the predefined colors for 256color terms into their RGB
--- values
---
--- @param color_number integer
local function split_predefined_terminal_color(color_number)
local r = math.floor(color_number / 36)
local g = math.floor(math.floor(color_number / 6) % 6)
local b = math.floor(color_number % 6)
local lookup = {[0]=0, [1]=95, [2]=135, [3]=175, [4]=215, [5]=255}
return lookup[r], lookup[g], lookup[b]
end
--- Create an iterator that tokenizes the given input string into ansi escape
--- sequences.
---
--- Lua patterns for string.gmatch
local function tokenize(input_string)
-- The empty input string is a special case where we return one single
-- token.
if input_string == "" then return string.gmatch("", "") end
-- we keep track of the position in the input with a local variable so that
-- our "next" function does not need to rely on the second argument.
-- Especially if a token appears twice in the input that might be of
-- importance.
local position = 1
local function next(input)
-- If the position we are currently tokenizing is beyond the input string
-- return nil => stop tokenizing.
if input:len() < position then return nil end
-- If we are on the last character and it is a semicolon, return an empty
-- token and move position beyond the input to stop on the next call.
-- This is hard to handle properly in the tokenizer below.
if input:len() == position and input:sub(-1) == ";" then
position = position + 1
return ""
end
-- first check for the special sequences "38;" "48;"
local init = input:sub(position, position+2)
if init == "38;" or init == "48;" then
-- Try to match an 8 bit or a 24 bit color sequence
local patterns = {"([34])8;5;(%d+);?", "([34])8;2;(%d+);(%d+);(%d+);?"}
for _, pattern in ipairs(patterns) do
local start, stop, token, c1, c2, c3 = input:find(pattern, position)
if start == position then
position = stop + 1
return token == "3" and "foreground" or "background", c1, c2, c3
end
end
-- If no valid special sequence was found we fall through to the normal
-- tokenization.
end
-- handle all other tokens, we expect a simple number followed by either a
-- semicolon or the end of the string, or the end of the input string
-- directly.
local oldpos = position
local next_pos = input:find(";", position)
if next_pos == nil then
-- no further semicolon was found, we reached the end of the input
-- string, the next call to this function will return nil
position = input:len() + 1
return input:sub(oldpos, -1)
else
position = next_pos
-- We only skip the semicolon if it was not at the end of the input
-- string.
if next_pos < input:len() then
position = next_pos + 1
end
return input:sub(oldpos, next_pos - 1)
end
end
return next, input_string, nil
end
--- The internal state of the parser
local state = {
line = 1, -- the line where the currently described state starts
column = 1, -- the column where the currently described state starts
}
--- Reset the parser
function state:clear()
self.foreground = ""
self.background = ""
self.ctermfg = ""
self.ctermbg = ""
for _, k in pairs(attributes) do self[k] = false end
end
--- Compute the name of the current active nvim highlight group
function state:state2highlight_group_name()
if self.conceal then return "NvimPagerConceal" end
local name = "NvimPagerFG_" .. self.foreground:gsub("#", "") ..
"_BG_" .. self.background:gsub("#", "")
for _, field in pairs(attributes) do
if self[field] then
name = name .. "_" .. field
end
end
return name
end
--- Parse the terminal escape sequences in the given string
---
--- @param string string
function state:parse(string)
for _token, c1, c2, c3 in tokenize(string) do
local token = _token
-- First we check for 256 colors and 24 bit color sequences.
if c3 ~= nil then
self[token] = hexformat_rgb_numbers(tonumber(c1), tonumber(c2),
tonumber(c3))
elseif c1 ~= nil then
self:parse8bit(token, c1)
self["cterm"..token:sub(1, 1).."g"] = tonumber(c1)
else
if token == "" then token = 0 else token = tonumber(token) end
if token == 0 then
self:clear()
elseif token == 1 or token == 3 or token == 4 or token == 7
or token == 8 or token == 9 then
-- 2, 5 and 6 could be handled here if they were supported.
self[attributes[token]] = true
elseif token == 21 then
-- 22 means "doubley underline" or "bold off", we could implement
-- doubley underline by undercurl.
--self.undercurl = true
elseif token == 22 then
self.bold = false
--self.faint = false
elseif token == 23 or token == 24 or token == 27 or token == 28
or token == 29 then
-- 25 means blink off so it could also be handled here if it was
-- supported.
self[attributes[token - 20]] = false
elseif token >= 30 and token <= 37 then -- foreground color
self.foreground = colors[token - 30]
self.ctermfg = token - 30
elseif token == 39 then -- reset foreground
self.foreground = ""
self.ctermfg = ""
elseif token >= 40 and token <= 47 then -- background color
self.background = colors[token - 40]
self.ctermbg = token - 40
elseif token == 49 then -- reset background
self.background = ""
self.ctermbg = ""
elseif token >= 90 and token <= 97 then -- bright foreground color
self.foreground = colors[token - 82]
elseif token >= 100 and token <= 107 then -- bright background color
self.background = colors[token - 92]
end
end
end
end
--- Parse an 8 bit color number into a highlight definition
---
--- @param fgbg "foreground"|"background"
--- @param color string
function state:parse8bit(fgbg, color)
local colornr = tonumber(color)
if colornr >= 0 and colornr <= 7 then
color = colors[colornr]
elseif colornr >= 8 and colornr <= 15 then -- high pallet colors
color = colors[colornr] -- + 82 + 10 * (fgbg == "background" and 1 or 0)
elseif colornr >= 16 and colornr <= 231 then -- color cube
color = hexformat_rgb_numbers(split_predefined_terminal_color(colornr-16))
else -- grayscale ramp
colornr = 8 + 10 * (colornr - 232)
color = hexformat_rgb_numbers(colornr, colornr, colornr)
end
self[fgbg] = ""..color
end
--- Compute an nvim highlight command for the current internal state with the
--- given highlight group name.
---
--- @param groupname string
function state:compute_highlight_command(groupname)
local args = ""
if self.foreground ~= "" then
args = args.." guifg="..self.foreground
if self.ctermfg ~= "" then
args = args .. " ctermfg=" .. self.ctermfg
end
end
if self.background ~= "" then
args = args.." guibg="..self.background
if self.ctermbg ~= "" then
args = args .. " ctermbg=" .. self.ctermbg
end
end
local attrs = ""
for _, key in pairs(attributes) do
if self[key] then
attrs = attrs .. "," .. key
end
end
attrs = attrs:sub(2)
if attrs ~= "" then
args = args .. " gui=" .. attrs .. " cterm=" .. attrs
end
if args == "" then
return "highlight default link " .. groupname .. " Normal"
else
return "highlight default " .. groupname .. args
end
end
--- Wrapper around nvim_buf_add_highlight to fix index offsets
---
--- The function nvim_buf_add_highlight expects 0 based line numbers and column
--- numbers. Set the start column to 0, the end column to -1 if not given.
---
--- @param groupname string nvim highlight group name
--- @param line integer the line number for the line to highlight
--- @param from integer|nil starting position in the line
--- @param to integer|nil end position in the line
local function add_highlight(groupname, line, from, to)
local line_0 = line - 1
local from_0 = (from or 1) - 1
local to_0 = (to or 0) - 1
nvim.nvim_buf_add_highlight(0, namespace, groupname, line_0, from_0, to_0)
end
--- Apply a highlight to a range in the current buffer
---
--- The highlight attributes are generated from the current state (self).
---
--- @local
--- @param from_line integer
--- @param from_column integer
--- @param to_line integer
--- @param to_column integer
function state:render(from_line, from_column, to_line, to_column)
if from_line == to_line and from_column == to_column then
return
end
local groupname = self:state2highlight_group_name()
-- check if the hl group already exists
if cache[groupname] == nil then
nvim.nvim_command(self:compute_highlight_command(groupname))
cache[groupname] = true
end
if from_line == to_line then
add_highlight(groupname, from_line, from_column, to_column)
else
add_highlight(groupname, from_line, from_column)
for line = from_line+1, to_line-1 do
add_highlight(groupname, line)
end
add_highlight(groupname, to_line, 1, to_column)
end
end
--- Parse the current buffer for ansi escape sequences and add buffer
--- highlights to the buffer instead.
local function ansi2highlight()
nvim.nvim_command(
"syntax match NvimPagerEscapeSequence conceal '\\e\\[[0-9;]*m'")
nvim.nvim_command(
"syntax match NvimPagerEscapeSequence conceal '\\e\\[[0-2]\\?K'")
nvim.nvim_command("highlight NvimPagerConceal gui=NONE guisp=NONE " ..
"guifg=background guibg=background")
nvim.nvim_win_set_option(0, "conceallevel", 3)
nvim.nvim_win_set_option(0, "concealcursor", "nv")
local pattern = "\27%[([0-9;]*)m"
state:clear()
namespace = nvim.nvim_create_namespace("")
for lnum, line in ipairs(nvim.nvim_buf_get_lines(0, 0, -1, false)) do
local start, end_, spec
local col = 1
repeat
start, end_, spec = line:find(pattern, col)
if start ~= nil then
state:render(state.line, state.column, lnum, start)
state.line = lnum
state.column = end_
state:parse(spec)
-- update the position to find the next match in the line
col = end_
end
until start == nil
end
end
return {
hexformat_rgb_numbers = hexformat_rgb_numbers,
run = ansi2highlight,
split_predefined_terminal_color = split_predefined_terminal_color,
state = state,
tokenize = tokenize,
}
nvimpager-0.14.0/lua/nvimpager/cat.lua 0000664 0000000 0000000 00000020147 15163401063 0017612 0 ustar 00root root 0000000 0000000 --- Functions for nvimpager's cat mode
-- Neovim defines this object but luacheck doesn't know it. So we define a
-- shortcut and tell luacheck to ignore it.
local vim = vim -- luacheck: ignore
local nvim = vim.api -- luacheck: ignore
local util = require("nvimpager/util")
-- These variables will be initialized during the first call to cat_mode()
--
-- A local copy of the termguicolors option, used for color output in cat
-- mode.
local colors_24_bit
local color2escape
--- A cache to map syntax groups to ansi escape sequences in cat mode or
--- remember defined syntax groups in the ansi rendering functions.
local cache = {}
--- Split a 24 bit color number into the three red, green and blue values
---
--- @param color_number number
local function split_rgb_number(color_number)
-- The lua implementation of these bit shift operations is taken from
-- http://nova-fusion.com/2011/03/21/simulate-bitwise-shift-operators-in-lua
local r = math.floor(color_number / 2 ^ 16)
local g = math.floor(math.floor(color_number / 2 ^ 8) % 2 ^ 8)
local b = math.floor(color_number % 2 ^ 8)
return r, g, b
end
--- Compute the escape sequences for a 24 bit color number.
---
--- @param color_number number a 24 bit color number
--- @param foreground boolean whether to return the escape sequences for fg or
--- bg colors
local function color2escape_24bit(color_number, foreground)
local red, green, blue = split_rgb_number(color_number)
local escape
if foreground then
escape = '38;2;'
else
escape = '48;2;'
end
return escape .. red .. ';' .. green .. ';' .. blue
end
--- Compute the escape sequences for a 8 bit color number.
---
--- @param color_number number an 8 bit color number
--- @param foreground boolean whether to return the escape sequences for fg or
--- bg colors
local function color2escape_8bit(color_number, foreground)
local prefix
if color_number < 8 then
if foreground then
prefix = '3'
else
prefix = '4'
end
elseif color_number < 16 then
color_number = color_number - 8
if foreground then
prefix = '9'
else
prefix = '10'
end
elseif foreground then
prefix = '38;5;'
else
prefix = '48;5;'
end
return prefix .. color_number
end
--- Compute a ansi escape sequences to render a syntax group on the terminal.
---
--- @param groupid integer
local function group2ansi(groupid)
if cache[groupid] then
return cache[groupid]
end
local info = nvim.nvim_get_hl(0, {id = groupid, link = false})
if colors_24_bit then
info.foreground = info.fg
info.background = info.bg
else
info.foreground = info.ctermfg
info.background = info.ctermbg
if info.cterm ~= nil then
info.bold = info.cterm.bold
info.italic = info.cterm.italic
info.underline = info.cterm.underline
else
info.bold = nil
info.italic = nil
info.underline = nil
end
end
if info.reverse then
info.foreground, info.background = info.background, info.foreground
end
-- Reset all attributes before setting new ones. The vimscript version did
-- use sevel explicit reset codes: 22, 24, 25, 27 and 28. If no foreground
-- or background color was defined for a syntax item they were reset with
-- 39 or 49.
local escape = '\27[0'
if info.bold then escape = escape .. ';1' end
if info.italic then escape = escape .. ';3' end
if info.underline then escape = escape .. ';4' end
if info.foreground then
escape = escape .. ';' .. color2escape(info.foreground, true)
end
if info.background then
escape = escape .. ';' .. color2escape(info.background, false)
end
escape = escape .. 'm'
cache[groupid] = escape
return escape
end
--- Check if the current buffer is empty
local function check_empty()
if nvim.nvim_buf_line_count(0) <= 1 and nvim.nvim_buf_get_offset(0, 0) <= 0 then
local handle = io.open(nvim.nvim_buf_get_name(0))
if handle == nil then
return true
end
local eof = handle:read(0)
handle:close()
if eof == nil then
return true
end
end
return false
end
--- Iterate through the current buffer and print it to stdout with terminal
--- color codes for highlighting.
local function highlight()
-- Detect an empty buffer.
if check_empty() then
return
elseif util.check_escape_sequences() then
for _, line in ipairs(nvim.nvim_buf_get_lines(0, 0, -1, false)) do
io.write(line, '\n')
end
return
end
local conceallevel = nvim.nvim_win_get_option(0, 'conceallevel')
local syntax_id_conceal = nvim.nvim_call_function('hlID', {'Conceal'})
local syntax_id_whitespace = nvim.nvim_call_function('hlID', {'Whitespace'})
local syntax_id_non_text = nvim.nvim_call_function('hlID', {'NonText'})
local list = nvim.nvim_win_get_option(0, "list")
local listchars = list and vim.opt.listchars:get() or {}
local last_syntax_id = -1
local last_conceal_id = -1
local linecount = nvim.nvim_buf_line_count(0)
for lnum, line in ipairs(nvim.nvim_buf_get_lines(0, 0, -1, false)) do
local outline = ''
local skip_next_char = false
local syntax_id
for cnum = 1, line:len() do
local conceal_info = nvim.nvim_call_function('synconcealed',
{lnum, cnum})
local conceal = conceal_info[1] == 1
local replace = conceal_info[2]
local conceal_id = conceal_info[3]
if skip_next_char then
skip_next_char = false
elseif conceal and last_conceal_id == conceal_id then -- luacheck: ignore
-- skip this char
else
local append
if conceal then
syntax_id = syntax_id_conceal
if replace == '' and conceallevel == 1 then replace = ' ' end
append = replace
last_conceal_id = conceal_id
else
append = line:sub(cnum, cnum)
if list and string.find(" \194", append, 1, true) ~= nil then
syntax_id = syntax_id_whitespace
if append == " " then
if line:find("^ +$", cnum) ~= nil then
append = listchars.trail or listchars.space or append
else
append = listchars.space or append
end
elseif append == "\194" and line:sub(cnum + 1, cnum + 1) == "\160" then
-- Utf8 non breaking space is "\194\160", neovim represents all
-- files as utf8 internally, regardless of the actual encoding.
-- See :help 'encoding'.
append = listchars.nbsp or "\194\160"
skip_next_char = true
end
else
syntax_id = nvim.nvim_call_function('synID', {lnum, cnum, true})
end
end
if syntax_id ~= last_syntax_id then
outline = outline .. group2ansi(syntax_id)
last_syntax_id = syntax_id
end
outline = outline .. append
end
end
-- append a eol listchar if &list is set
if list and listchars.eol ~= nil then
syntax_id = syntax_id_non_text
if syntax_id ~= last_syntax_id then
outline = outline .. group2ansi(syntax_id)
last_syntax_id = syntax_id
end
outline = outline .. listchars.eol
end
-- Write the whole line and a newline char. If this was the last line
-- also reset the terminal attributes.
io.write(outline, lnum == linecount and cache[0] or '', '\n')
end
end
--- Initialize some module level variables for cat mode.
local function init()
-- Get the value of &termguicolors from neovim.
colors_24_bit = nvim.nvim_get_option('termguicolors')
-- Select the correct coloe escaping function.
if colors_24_bit then
color2escape = color2escape_24bit
else
color2escape = color2escape_8bit
end
-- Initialize the ansi group to color cache with the "Normal" hl group.
cache[0] = group2ansi(nvim.nvim_call_function('hlID', {'Normal'}))
end
--- Call the highlight function to write the highlighted version of all buffers
--- to stdout and quit nvim.
local function cat_mode()
init()
highlight()
-- We can not use nvim_list_bufs() as a file might appear on the command
-- line twice. In this case we want to behave like cat(1) and display the
-- file twice.
for _ = 2, nvim.nvim_call_function('argc', {}) do
nvim.nvim_command('next')
highlight()
end
nvim.nvim_command('quitall!')
end
--- @export
return {
cat_mode = cat_mode,
init = init,
color2escape_24bit = color2escape_24bit,
color2escape_8bit = color2escape_8bit,
split_rgb_number = split_rgb_number,
group2ansi = group2ansi,
}
nvimpager-0.14.0/lua/nvimpager/init.lua 0000664 0000000 0000000 00000017106 15163401063 0020007 0 ustar 00root root 0000000 0000000 --- Functions to use neovim as a pager.
---
--- This code is a rewrite of two sources: vimcat and vimpager (which also
--- contains a version of vimcat):
---
--- - Vimcat goes back to Matthew J. Wozniski and can be found at
---
--- - Vimpager was written by Rafael Kitover and can be found at
---
-- Information about terminal escape codes:
-- https://en.wikipedia.org/wiki/ANSI_escape_code
-- Neovim defines this object but luacheck doesn't know it. So we define a
-- shortcut and tell luacheck to ignore it.
local nvim = vim.api -- luacheck: ignore
local cat = require("nvimpager/cat")
local pager = require("nvimpager/pager")
-- names that will be exported from this module
local nvimpager = require("nvimpager/options")
-- These variables will be initialized during the first call to cat_mode() or
-- pager_mode().
--
-- This variable holds the name of the detected parent process for pager mode.
local doc
--- Replace a string prefix in all items in a list
local function replace_prefix(table, old_prefix, new_prefix)
-- Escape all punctuation chars to protect from lua pattern chars.
old_prefix = old_prefix:gsub('[^%w]', '%%%0')
for index, value in ipairs(table) do
table[index] = value:gsub('^' .. old_prefix, new_prefix, 1)
end
return table
end
--- Parse the command of the given pid to detect some common
--- documentation programs (man, pydoc, perldoc, git, ...).
---
--- @param pid integer|nil
local function detect_process(pid)
if not pid then return nil end
-- FIXME saving and resetting gcr after nvim_get_proc is a workaround for
-- https://github.com/neovim/neovim/issues/23122, reported in #84
local old_gcr = vim.o.gcr
vim.o.gcr = ''
local proc = nvim.nvim_get_proc(pid)
vim.o.gcr = old_gcr
if proc == nil then return 'none' end
local command = proc.name
if command == 'man' then
return 'man'
elseif command:find('^[Pp]ython[0-9.]*') ~= nil or
command:find('^[Pp]ydoc[0-9.]*') ~= nil then
return 'pydoc'
elseif command == 'ruby' or command == 'irb' or command == 'ri' then
return 'ri'
elseif command == 'perl' or command == 'perldoc' then
return 'perldoc'
elseif command == 'git' then
return 'git'
end
return nil
end
--- Parse the command of the calling process
--- $PARENT was exported by the calling bash script and points to the calling
--- program.
local function detect_parent_process()
return detect_process(tonumber(os.getenv('PARENT')))
end
--- Check if a string uses poor man's bold or underline tricks
---
--- Return true if all characters are followed by backspace and themself again
--- or if all characters are preceded by underscore and backspace. Spaces
--- are ignored.
---
--- @param line string
local function detect_man_page_helper(line)
line = line:gsub('%s+', '')
if line == "" then return false end
local index = 1
while index <= #line do
local cur = line:sub(index, index)
local next = line:sub(index+1, index+1)
local third = line:sub(index+2, index+2)
if (cur == third and next == '\b')
or (cur == '_' and next == '\b' and third ~= nil) then
index = index + 3 -- continue after the overwriting character
else
return false
end
end
return true
end
--- Search the beginning of the current buffer to detect if it contains a man
--- page.
local function detect_man_page_in_current_buffer()
-- Only check the first twelve lines (for speed).
for _, line in ipairs(nvim.nvim_buf_get_lines(0, 0, 12, false)) do
if detect_man_page_helper(line) then
return true
end
end
return false
end
--- Remove ansi escape sequences from the current buffer.
local function strip_ansi_escape_sequences_from_current_buffer()
local modifiable = nvim.nvim_buf_get_option(0, "modifiable")
nvim.nvim_buf_set_option(0, "modifiable", true)
nvim.nvim_command(
[=[keepjumps silent %substitute/\v\e\[[;?]*[0-9.;]*[a-z]//egi]=])
nvim.nvim_win_set_cursor(0, {1, 0})
nvim.nvim_buf_set_option(0, "modifiable", modifiable)
end
--- Detect possible filetypes for the current buffer by looking at the pstree
--- or ansi escape sequences or manpage sequences in the current buffer.
local function detect_filetype()
if not doc and detect_man_page_in_current_buffer() then doc = 'man' end
if doc == 'git' then
if nvimpager.git_colors then
-- Use the highlighting from the git commands.
doc = nil
else
-- Use nvim's syntax highlighting for git buffers instead of git's
-- internal highlighting.
strip_ansi_escape_sequences_from_current_buffer()
end
end
-- python uses the same "highlighting" technique with backspace as roff.
-- This means we have to load the full :Man plugin for python as well and
-- not just set the filetype to man.
if doc == 'man' or doc == 'pydoc' then
nvim.nvim_buf_set_option(0, 'readonly', false)
nvim.nvim_command("Man!")
nvim.nvim_buf_set_option(0, 'readonly', true)
-- do not set the file type again later on
doc = nil
elseif doc == 'perldoc' or doc == 'ri' then
doc = 'man' -- only set the syntax, not the full :Man plugin
end
if doc ~= nil then
nvim.nvim_buf_set_option(0, 'filetype', doc)
end
end
--- Setup function to be called from --cmd.
function nvimpager.stage1()
-- Don't remember file names and positions
nvim.nvim_set_option('shada', '')
-- prevent messages when opening files (especially for the cat version)
nvim.nvim_set_option('shortmess', nvim.nvim_get_option('shortmess')..'F')
-- Define autocmd group for nvimpager.
local group = nvim.nvim_create_augroup('NvimPager', {})
local tmp = os.getenv('TMPFILE')
if tmp and tmp ~= "" then
nvim.nvim_create_autocmd("BufReadPost", {pattern = tmp, once = true,
group = group, callback = function()
nvim.nvim_buf_set_option(0, "buftype", "nofile")
end})
nvim.nvim_create_autocmd("VimLeavePre", {pattern = "*", once = true,
group = group, callback = function() os.remove(tmp) end})
end
doc = detect_parent_process()
if doc == 'git' then
-- We disable modelines for this buffer as they could disturb the git
-- highlighting in diffs.
nvim.nvim_buf_set_option(0, 'modeline', false)
nvim.nvim_set_option('modelines', 0)
end
-- Theoretically these options only affect the pager mode so they could also
-- be set in stage2() but that would overwrite user settings from the init
-- file.
nvim.nvim_set_option('mouse', 'a')
nvim.nvim_set_option('laststatus', 0)
end
--- Set up autocomands to start the correct mode after startup or for each
--- file. This function assumes that in "cat mode" we are called with
--- --headless and hence do not have a user interface. This also means that
--- this function can only be called with -c or later as the user interface
--- would not be available in --cmd.
function nvimpager.stage2()
detect_filetype()
local callback, events
if #nvim.nvim_list_uis() == 0 then
callback, events = cat.cat_mode, 'VimEnter'
else
callback, events = pager.pager_mode, {'VimEnter', 'BufWinEnter'}
end
local group = nvim.nvim_create_augroup('NvimPager', {clear = false})
-- The "nested" in these autocomands enables nested executions of
-- autocomands inside the *_mode() functions. See :h autocmd-nested.
nvim.nvim_create_autocmd(events, {pattern = '*', callback = callback,
nested = true, group = group})
end
-- functions only exported for tests
nvimpager._testable = {
detect_man_page_helper = detect_man_page_helper,
detect_process = detect_process,
detect_parent_process = detect_parent_process,
replace_prefix = replace_prefix,
}
return nvimpager
nvimpager-0.14.0/lua/nvimpager/options.lua 0000664 0000000 0000000 00000000610 15163401063 0020527 0 ustar 00root root 0000000 0000000 -- names that will be exported from this module
return {
-- user facing options
maps = true, -- if the default mappings should be defined
git_colors = false, -- if the highlighting from the git should be used
-- follow the end of the file when it changes (like tail -f or less +F)
follow = false,
follow_interval = 500, -- interval to check the underlying file in ms
}
nvimpager-0.14.0/lua/nvimpager/pager.lua 0000664 0000000 0000000 00000005076 15163401063 0020145 0 ustar 00root root 0000000 0000000 --- Functions for the pager mode of nvimpager.
-- Neovim defines this object but luacheck doesn't know it. So we define a
-- shortcut and tell luacheck to ignore it.
local vim = vim -- luacheck: ignore
local nvim = vim.api -- luacheck: ignore
local nvimpager = require("nvimpager/options")
local util = require("nvimpager/util")
local ansi2highlight = require("nvimpager/ansi2highlight")
local follow_timer = nil
function nvimpager.toggle_follow()
if follow_timer ~= nil then
vim.fn.timer_pause(follow_timer, nvimpager.follow)
nvimpager.follow = not nvimpager.follow
else
follow_timer = vim.fn.timer_start(
nvimpager.follow_interval,
function()
nvim.nvim_command("silent checktime")
nvim.nvim_command("silent $")
end,
{ ["repeat"] = -1 })
nvimpager.follow = true
end
end
--- Set up mappings to make nvim behave a little more like a pager.
local function set_maps()
local function map(lhs, rhs, mode)
-- we are using buffer local maps because we want to overwrite the buffer
-- local maps from the man plugin (and maybe others)
vim.keymap.set(mode or 'n', lhs, rhs, { buffer = true })
end
map('q', 'quitall!')
map('q', 'quitall!', 'v')
map('', '')
map('', '')
map('g', 'gg')
map('', '')
map('', '')
map('k', '')
map('j', '')
map('F', nvimpager.toggle_follow)
end
--- Setup function for the VimEnter autocmd.
--- This function will be called for each buffer once
local function pager_mode()
if util.check_escape_sequences() then
-- Try to highlight ansi escape sequences.
ansi2highlight.run()
-- Lines with concealed ansi esc sequences seem shorter than they are (by
-- character count) so it looks like they wrap to early and the concealing
-- of escape sequences only works for the first &synmaxcol chars.
nvim.nvim_buf_set_option(0, "synmaxcol", 0) -- unlimited
nvim.nvim_win_set_option(0, "wrap", false)
end
nvim.nvim_buf_set_option(0, 'modifiable', false)
nvim.nvim_buf_set_option(0, 'modified', false)
if nvimpager.maps then
-- if this is done in VimEnter it will override any settings in the user
-- config, if we do it globally we are not overwriting the mappings from
-- the man plugin
set_maps()
end
-- Check if the user requested follow mode on startup
if nvimpager.follow then
-- turn follow mode of so that we can use the init logic in toggle_follow
nvimpager.follow = false
nvimpager.toggle_follow()
end
end
--- @export
return {
pager_mode = pager_mode,
}
nvimpager-0.14.0/lua/nvimpager/util.lua 0000664 0000000 0000000 00000001161 15163401063 0020013 0 ustar 00root root 0000000 0000000 --- Utility functions for nvimpager
local nvim = vim.api -- luacheck: ignore
--- Check if the beginning of the current buffer contains ansi escape sequences.
---
--- For performance only the first 100 lines are checked.
local function check_escape_sequences()
local filetype = nvim.nvim_buf_get_option(0, 'filetype')
if filetype == '' or filetype == 'text' then
for _, line in ipairs(nvim.nvim_buf_get_lines(0, 0, 100, false)) do
if line:find('\27%[[;?]*[0-9.;]*[A-Za-z]') ~= nil then return true end
end
end
return false
end
--- @export
return {
check_escape_sequences = check_escape_sequences,
}
nvimpager-0.14.0/makefile 0000664 0000000 0000000 00000005263 15163401063 0015271 0 ustar 00root root 0000000 0000000 DESTDIR ?=
PREFIX ?= /usr/local
RUNTIME = $(PREFIX)/share/nvimpager/runtime
VERSION = $(lastword $(shell ./nvimpager -v))
BUSTED = busted
%.configured: %
sed 's#^RUNTIME=.*$$#RUNTIME='"'$(RUNTIME)'"'#;s#version=.*$$#version=$(VERSION)#' < $< > $@
chmod +x $@
install-no-man: nvimpager.configured
mkdir -p $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(RUNTIME)/lua/nvimpager \
$(DESTDIR)$(PREFIX)/share/zsh/site-functions
install nvimpager.configured $(DESTDIR)$(PREFIX)/bin/nvimpager
install -m 644 lua/nvimpager/*.lua $(DESTDIR)$(RUNTIME)/lua/nvimpager
install -m 644 _nvimpager $(DESTDIR)$(PREFIX)/share/zsh/site-functions
install: install-no-man nvimpager.1
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
install -m 644 nvimpager.1 $(DESTDIR)$(PREFIX)/share/man/man1
uninstall:
$(RM) -r $(PREFIX)/bin/nvimpager $(RUNTIME)/lua/nvimpager \
$(PREFIX)/share/man/man1/nvimpager.1 \
$(PREFIX)/share/zsh/site-functions/_nvimpager
nvimpager.1: SOURCE_DATE_EPOCH = $(shell git log -1 --no-show-signature --pretty="%ct" 2>/dev/null || echo 1775108659)
nvimpager.1: nvimpager.md
sed '1s/$$/ "nvimpager $(VERSION)"/' $< | scdoc > $@
# The patterns prefixed with "lua" are used to require our nvimpager code from
# the tests with the same module names that neovim would find them.
# The patterns without prefix are to find the helper modules in the test
# directory.
LPATH = --lpath "lua/?.lua" --lpath "lua/?/init.lua" \
--lpath "?.lua" --lpath "?/init.lua"
test:
@$(BUSTED) $(LPATH) test
luacov.stats.out: nvimpager lua/nvimpager/*.lua test/unit_spec.lua
@$(BUSTED) $(LPATH) --coverage test/unit_spec.lua
luacov.report.out: luacov.stats.out
luacov lua/nvimpager
TYPE = minor
version: OLD_VERSION = $(patsubst v%,%,$(lastword $(shell git tag --list --sort=version:refname 'v*')))
version: NEW_VERSION = $(shell echo $(OLD_VERSION) | awk -F . -v type=$(TYPE) \
-e 'type == "major" { print $$1+1 ".0.0" }' \
-e 'type == "minor" { print $$1 "." $$2+1 ".0" }' \
-e 'type == "patch" { print $$1 "." $$2 "." $$3+1 }')
version:
[ $(TYPE) = major ] || [ $(TYPE) = minor ] || [ $(TYPE) = patch ]
git switch main
git diff --quiet HEAD
sed -i 's/version=[0-9.]*$$/version=$(NEW_VERSION)/' nvimpager
sed -i '/SOURCE_DATE_EPOCH/s/[0-9]\{10,\}/$(shell date +%s)/' $(MAKEFILE_LIST)
(printf '%s\n' 'Version $(NEW_VERSION)' '' 'Major changes:' 'Breaking changes:' 'Changes:'; \
git log v$(OLD_VERSION)..HEAD) \
| sed -E '/^(commit|Merge:|Author:)/d; /^Date/{N;N; s/.*\n.*\n /-/;}' \
| git commit --edit --file - nvimpager makefile
git tag --message="$$(git show --no-patch --format=format:%s%n%n%b)" \
v$(NEW_VERSION)
clean:
$(RM) nvimpager.configured nvimpager.1 luacov.*
.PHONY: clean install test uninstall version
nvimpager-0.14.0/nvimpager 0000775 0000000 0000000 00000005451 15163401063 0015506 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# Copyright (c) 2017 Lucas Hoffmann
# Licenced under a BSD-2-clause licence. See the LICENSE file.
RUNTIME=${BASH_SOURCE%/*}
PARENT=$PPID
TMPFILE=
export RUNTIME
export PARENT
export TMPFILE
export NVIM_APPNAME=nvimpager
mode=auto
nvim=${NVIMPAGER_NVIM:-nvim}
usage () {
echo "Usage: ${0##*/} [-acp] [--] [nvim options and files]"
echo " ${0##*/} -h"
echo " ${0##*/} -v"
}
description () {
cat <<-EOF
$NVIM_APPNAME provides a simple pager based on neovim.
Options:
-h this help
-v version output
-a enforce auto mode (default)
-c enforce cat mode
-p enforce pager mode
All further arguments are passed to neovim. But one has to add "--"
if the first argument is an option in order to stop this script from
interpreting it.
If "-" or no files are given stdin is read.
In auto mode, if the cumulative length of all file arguments is
smaller than the terminal size, cat mode is used, otherwise pager mode
is used. If any none file argument (neovim option) is given pager
mode is implied.
EOF
}
while getopts achpv flag; do
case $flag in
a) mode=auto;;
c) mode=cat;;
h) usage; description; exit;;
p) mode=pager;;
v)
version=$(git -C "$RUNTIME" describe 2>/dev/null) || version=0.14.0
echo "$NVIM_APPNAME ${version#v}"
exit
;;
*) usage >&2; exit 2;;
esac
done
shift $((OPTIND - 1))
# Display the usage text if no arguments where given and stdin is a tty.
if [[ $# -eq 0 && -t 0 ]]; then
usage
exit 2
fi
# If we are not on a tty just "be" cat.
if [[ ! -t 1 && $mode = auto ]]; then
exec cat "$@"
fi
# Collect all file arguments until the first non file into $files. If one non
# file is found pager mode is enforced. The special "file"-name "-" is
# accepted as stdin.
files=()
while [[ $# -gt 0 ]]; do
if [[ -f $1 ]]; then
files+=("$1")
shift
elif [[ $1 = - ]]; then
TMPFILE=$(mktemp)
files+=("$TMPFILE")
shift
else
if [[ $mode = auto ]]; then
mode=pager
fi
break
fi
done
# If we did not get any file arguments and stdin is not a terminal, read stdin
# into a temp file.
if [[ -z $TMPFILE && ${#files[@]} -eq 0 && ! -t 0 ]]; then
TMPFILE=$(mktemp)
files=("$TMPFILE")
fi
if [[ $TMPFILE ]]; then
# Bash runs the EXIT trap also when exiting due to signals.
trap 'rm -f "$TMPFILE"' EXIT
cat > "$TMPFILE"
fi
# these come before all user supplied -c and --cmd arguments
args=(
-R
--cmd 'set rtp+=$RUNTIME | lua nvimpager = require("nvimpager"); nvimpager.stage1()'
-c 'lua nvimpager.stage2()'
)
# Switch to cat mode if all files combined are shorter than the terminal
# height.
if [[ $mode = cat || \
$mode = auto && $(cat "${files[@]}" | wc -l) -le $(tput lines) ]]
then
args+=(--headless)
fi
exec -a nvimpager $nvim "${args[@]}" "${files[@]}" "$@"
nvimpager-0.14.0/nvimpager.md 0000664 0000000 0000000 00000013110 15163401063 0016071 0 ustar 00root root 0000000 0000000 nvimpager(1)
# NAME
nvimpager - using neovim as a pager
# SYNOPSIS
*nvimpager* [*-acp*] [\--] [nvim options and files] ++
*nvimpager* *-h* ++
*nvimpager* *-v*
# DESCRIPTION
Nvimpager is a small program that can be used like most other pagers.
Internally it uses neovim with the default TUI to display the text. This means
it has all the fancy syntax highlighting, mouse support and other features of
neovim available in the pager, possibly including plugins!
# COMMAND LINE OPTIONS
Nvimpager itself interprets only very few options but all neovim options can
also be specified. If options to neovim are specified before the first file
name they must be preceded by "\--" to prevent nvimpager from trying to
interpret them.
The following options are interpreted by nvimpager itself:
*-a*
Run in "auto mode" (default). Auto mode will detect the terminal size and
switch to pager mode if the content to display would not fit on one screen. If
the content will fit on one screen it will switch to cat mode. This overrides
any previous *-c* and *-p* options.
*-c*
Run in "cat mode". Do not start the neovim TUI, only use neovim for syntax
highlighting and print the result to stdout. This overrides any previous *-a*
and *-p* options.
*-h*
Show the help screen and exit
*-p*
Run in "pager mode". Start the neovim TUI to display the given content. This
overrides any previous *-a* and *-c* options.
*-v*
Show version information and exit
# CONFIGURATION
Like neovim itself nvimpager will honour *$XDG_CONFIG_HOME* and
*$XDG_DATA_HOME*, which default to *~/.config* and *~/.local* respectively.
The main config directory is *$XDG_CONFIG_HOME/nvimpager* and the main user
config file is *$XDG_CONFIG_HOME/nvimpager/init.lua* or
*$XDG_CONFIG_HOME/nvimpager/init.vim*. It is an error if both files are
present. The site directory is *$XDG_DATA_HOME/.local/share/nvimpager/site*.
The manifest for remote plugins is read from (and written to)
*$XDG_DATA_HOME/nvimpager/rplugin.vim*.
The rest of the *&runtimepath* is configured like for neovim. The *-u* option
of *nvim*(1) itself can be used to change the main config file from the command
line.
The default config files for neovim are not used by design as these
potentially load many plugins and do a lot of configuration that is only
relevant for editing. If one really wants to use the same config files for
both nvimpager and nvim it is possible to do so by symlinking the config and
site directories and the rplugin file.
## Environment variables
The environment variable *$NVIMPAGER_NVIM* can be used to specify an nvim
executable to use. If unset it defaults to *nvim*.
## Configuration variables
The script exposes a lua table called *nvimpager* to *--cmd*/*-c* options and
the init file. It can be modified to change some options that are
specific to nvimpager.
The following fields (options) exist:
[[ *option*
:- *type*
:- *default*
:< *explanation*
| follow
: bool
: false
: start in follow mode, i.e. continuously load changes to the opened file and
scroll to the bottom (like *less +F* or *tail -f*)
| follow_interval
: number
: 500
: how often in ms the underlying file should be checked in follow mode
| git_colors
: bool
: false
: use git command highlighting instead of nvim syntax highlighting,
set this to true if you use an external diff
| maps
: bool
: true
: if some default less like maps should be defined inside pager mode
So to start nvimpager and follow changes to the opened file the user can put
```
lua nvimpager.follow = true
```
in the init file (or on the command line).
## Default key mappings
Nvimpager defines some mappings to make it feel more like a pager than an
editor. These mappings are inspired by *less*(1) which are very close to the
defaults in neovim. These mappings can be deactivated altogether by putting
```
lua nvimpager.maps = false
```
in the init file (or on the command line).
The following mappings are defined by default:
- *q* is mapped to quit nvimpager in normal and visual mode
- ** and ** move down or up a page respectively
- *g* goes to the top of the file
- ** and *j* scroll the window down one line
- ** and *k* scroll the window up one line
- *F* toggles "follow mode" where nvimpager continuously loads changes to the
underlying file and scrolls to the bottom. This is useful for watching log
files. It is modeled after the *F* command in *less*(1) or the *-f* option
of *tail*(1)
You can remap the lua function `nvimpager.toggle_follow` if you disabled the
default key mappings.
# EXAMPLES
To use nvimpager to view a file (with neovim's syntax highlighting if the
filetype is detected):
```
nvimpager file
```
Pipe text into nvimpager to view it:
```
echo text | nvimpager
```
Use nvimpager as your default *$PAGER* to view man pages or git diffs:
```
export PAGER=nvimpager
man nvimpager
git diff
```
Options for *nvim*(1) can be specified if they are separated from the options
for nvimpager itself. Either by separating them with *--* or by putting the
nvim options after at least one non option argument:
```
nvimpager -p -- -c 'echo "option for nvim"' file
nvimpager -p file -u custom_init.vim
```
Start nvimpager in "follow mode" to watch a growing log file:
```
nvimpager log_file -c 'lua nvimpager.follow = true'
```
# LIMITATIONS
If reading from stdin, nvimpager (like *nvim*(1)) waits for EOF until it starts
up. This means that it can not be used to continuously watch output from a
long running command even in follow mode.
# SEE ALSO
*nvim(1)* https://github.com/neovim/neovim
*vimpager(1)* https://github.com/rkitover/vimpager
# AUTHORS
Lucas Hoffmann
nvimpager-0.14.0/test/ 0000775 0000000 0000000 00000000000 15163401063 0014542 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/abort_test.lua 0000664 0000000 0000000 00000000271 15163401063 0017413 0 ustar 00root root 0000000 0000000 -- we will report an error
vim.fn.assert_report("this is an error")
-- and then immediately quit so that the test framework can not write the error
-- to the output file
vim.cmd.quit()
nvimpager-0.14.0/test/diff_test.lua 0000664 0000000 0000000 00000000121 15163401063 0017206 0 ustar 00root root 0000000 0000000 vim.cmd.edit("test/fixtures/diff2")
vim.fn.assert_equal("diff", vim.o.filetype)
nvimpager-0.14.0/test/first_test.lua 0000664 0000000 0000000 00000001333 15163401063 0017433 0 ustar 00root root 0000000 0000000 -- assert some things about the nvimpager module that we export
vim.fn.assert_equal("table", type(nvimpager), "nvimpager module not loaded")
vim.fn.assert_equal("boolean", type(nvimpager.maps))
vim.fn.assert_equal("boolean", type(nvimpager.follow))
vim.fn.assert_equal("number", type(nvimpager.follow_interval))
-- assert that the stage2 function has run, i.e. by checking the autocommands
-- it should set up
local cmds = vim.api.nvim_get_autocmds({group = "NvimPager"})
vim.fn.assert_equal(2, #cmds, "missing nvimpager autocommands")
-- check that the buffer local mappings are defined
local maps = vim.api.nvim_buf_get_keymap(0, "v")
vim.fn.assert_equal("q", maps[1].lhs)
vim.fn.assert_equal("quitall!", maps[1].rhs)
nvimpager-0.14.0/test/fixtures/ 0000775 0000000 0000000 00000000000 15163401063 0016413 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/fixtures/ansi-erase-line.txt 0000664 0000000 0000000 00000000053 15163401063 0022126 0 ustar 00root root 0000000 0000000 foo[Kbar
foo[0Kbar
foo[1Kbar
foo[2Kbar
nvimpager-0.14.0/test/fixtures/bin/ 0000775 0000000 0000000 00000000000 15163401063 0017163 5 ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/fixtures/bin/git 0000777 0000000 0000000 00000000000 15163401063 0022466 2test-parent.sh ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/fixtures/bin/man 0000777 0000000 0000000 00000000000 15163401063 0022456 2test-parent.sh ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/fixtures/bin/test-parent.sh 0000775 0000000 0000000 00000000551 15163401063 0021771 0 ustar 00root root 0000000 0000000 #!/bin/sh
# A small wrapper script for the tests. It can be linked to any name in order
# to simulate different parent processes.
# We set PARENT to the process id of the current script in order to allow
# nvimpager's lua code to be called directly here. This variable is otherwise
# set up by the bash script and passed to the lua code.
PARENT=$$ "$@" 2>&1
nvimpager-0.14.0/test/fixtures/conceal.tex 0000664 0000000 0000000 00000000021 15163401063 0020532 0 ustar 00root root 0000000 0000000 \ss should be ß
nvimpager-0.14.0/test/fixtures/conceal.tex.ansi 0000664 0000000 0000000 00000000050 15163401063 0021465 0 ustar 00root root 0000000 0000000 [0;37;48;5;242mß[0m should be ß[0m
nvimpager-0.14.0/test/fixtures/conceal.tex.cole0.ansi 0000664 0000000 0000000 00000000046 15163401063 0022473 0 ustar 00root root 0000000 0000000 [0;38;5;224m\ss[0m should be ß[0m
nvimpager-0.14.0/test/fixtures/conceal.tex.cole1.ansi 0000777 0000000 0000000 00000000000 15163401063 0025546 2conceal.tex.ansi ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/fixtures/conceal.tex.cole2.ansi 0000777 0000000 0000000 00000000000 15163401063 0025547 2conceal.tex.ansi ustar 00root root 0000000 0000000 nvimpager-0.14.0/test/fixtures/conceal.tex.red 0000664 0000000 0000000 00000000046 15163401063 0021312 0 ustar 00root root 0000000 0000000 [0;95m\ss[0;91m should be ß[0;91m
nvimpager-0.14.0/test/fixtures/diff 0000664 0000000 0000000 00000001312 15163401063 0017243 0 ustar 00root root 0000000 0000000 diff --git a/test/nvimpager_spec.lua b/test/nvimpager_spec.lua
index 68e2097..4593495 100644
--- a/test/nvimpager_spec.lua
+++ b/test/nvimpager_spec.lua
@@ -405,3 +405,17 @@ describe("lua functions", function()
end)
end)
end)
+
+describe("parent detection", function()
+ it("handles git", function()
+ local output = run("test/fixtures/bin/git ./nvimpager -c test/fixtures/diff")
+ local expected = read("test/fixtures/diff.ansi")
+ assert.equal(expected, output)
+ end)
+
+ it("handles man", function()
+ local output = run("test/fixtures/bin/man ./nvimpager -c test/fixtures/man.cat")
+ local expected = read("test/fixtures/man.ansi")
+ assert.equal(expected, output)
+ end)
+end)
nvimpager-0.14.0/test/fixtures/diff-modeline 0000664 0000000 0000000 00000000421 15163401063 0021035 0 ustar 00root root 0000000 0000000 diff --git a/nvimpager b/nvimpager
index d7d4e2f..99fb715 100755
--- a/nvimpager
+++ b/nvimpager
@@ -104,4 +104,4 @@ then
default_args+=(--headless)
fi
nvim "${default_args[@]}" "${files[@]}" "$@"