doronbehar / pistol

General purpose file previewer designed for Ranger, Lf to make scope.sh redundant
MIT License
317 stars 7 forks source link

ifdef::env-github[] = Pistol :toc: endif::[] ifndef::env-github[] // If processed locally, the README will be processed as a manpage = pistol(1) :doctype: manpage :manmanual: Pistol :mansource: Pistol :man-linkstyle: pass:[blue R < >]

== Name

pistol - General purpose file previewer designed for Ranger, Lf to make scope.sh redundant. endif::[]

== Introduction

ifdef::env-github[]

Pistol is a file previewer for command line file managers such as https://ranger.github.io/[Ranger], https://github.com/gokcehan/lf[Lf] and https://github.com/jarun/nnn[nnn], intended to replace the file previewer shell script https://github.com/ranger/ranger/blob/v1.9.2/ranger/data/scope.sh[`scope.sh`] commonly used with Ranger and other previewing methods.

scope.sh is a Bash script that uses case switches and external programs to decide how to preview every file it encounters. It knows how to handle every file according to its https://en.wikipedia.org/wiki/Media_type[MIME type] and/or file extension using case switches and external programs. This design makes it hard to configure / maintain and it makes it slow for startup and heavy when running.

Pistol is a Go program (with (almost) 0 dependencies) and its MIME type detection is internal. Moreover, it features native preview support for almost any archive file and for text files along with syntax highlighting while scope.sh relies on external programs to do these basic tasks.

endif::[] ifndef::env-github[]

Pistol features native preview support for almost any archive file and text files along with syntax highlighting.

endif::[]

The following table lists Pistol’s native previewing support:

[cols=",",options="header",] |=== |File/MIME Type |Notes on implementation |text/* |Prints text files with syntax highlighting thanks to https://github.com/alecthomas/chroma[`chroma`].

|Archives |Prints the contents of archive files using https://github.com/mholt/archiver[`archiver`]. |===

In case Pistol encounters a MIME type it doesn't know how to handle natively and you haven’t configured a program to handle it, it’ll print a general description of the file type it encountered. For example, the preview for an executable might be:

.... ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a34861a1ae5358dc1079bc239df9dfe4830a8403, for GNU/Linux 3.2.0, not stripped ....

This feature is available out of the box just like the previews for the common mime types mentioned above.

ifdef::env-github[] See also the WiKi article: https://github.com/doronbehar/pistol/wiki/Pistol-out-in-the-wild[Pistol out in the Wild]. endif::[]

ifdef::env-github[]

=== A Note on MIME type Detection

Some pure Go libraries provide MIME type detection. Here are the top search results I got using a common web search engine:

Pistol uses the last one which leverages the well known C library http://linux.die.net/man/3/libmagic[libmagic(3)]. I made this choice after experimenting with the other candidates and due to their lack of an extensive database such as http://linux.die.net/man/3/libmagic[libmagic(3)] has, I chose https://github.com/rakyll/magicmime[magicmime].

Note that this choice also features compatibility with the standard command file which is available by default on most GNU/Linux distributions.footnote:[Considering Pistol’s indirect dependence on http://linux.die.net/man/3/libmagic[libmagic(3)], I will never take the trouble to personally try and make it work on Windows natively. If you’ll succeed in the heroic task of compiling libmagic for Windows and teach https://github.com/rakyll/magicmime[magicmime] to use it, please let me know.]

=== A Note on Archive Previews

Pistol previews all archive / compression formats supported by the Go library https://github.com/mholt/archiver[`archiver`]. Some formats do nothing but compression, meaning they operate on 1 file alone and some files are a combination of archive, compressed in a certain algorithm.

For example, a .gz file is a single file compressed with gzip. A .tar file is an uncompressed archive (collection) of files. A .tar.gz is a .tar file compressed using gzip.

When pistol encounters a single file compressed using a known compression algorithm, it doesn't know how to handle its content, it displays the type of the archive. If a known compression algorithm has compressed a .tar file, Pistol lists the files themselves.

https://en.wikipedia.org/wiki/Brotli[brotli] compressed archives, (.tar.br) and Brotli compressed files (.br) are not detected by libmagic so Pistol doesn't know how to handle them.footnote:[https://bugs.astron.com/view.php?id=111[`filebug report]; https://github.com/google/brotli/issues/727[brotli` bug report].]

== Install

If someone has packaged Pistol for your distribution, you might find a package for of it linked https://github.com/doronbehar/pistol/wiki/Distributions'-Packages[in the WiKi].

If not, use the following instructions, or grab a statically compiled version of it from https://github.com/doronbehar/pistol/releases[the releases page], available since version 0.3.1. The releases assets include also the manual page pistol.1.gz.

WARNING: Currently, Darwin compatible binaries are not available there, but it should be possible to generate them via CI. All other binaries were cross compiled from my own x86_64 linux machine, using Nix' superb https://nix.dev/tutorials/cross-compilation[cross compilation support].

NOTE: For a statically compiled Pistol to be functional, it needs to read a libmagic database (usually found in /usr/share/misc/magic.mgc) and the static executable includes the contents of this database found on https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/tools/misc/file/default.nix[Nixpkgs' file] package. The content of the magic.mgc database is copied to ~/.local/share/pistol/${libmagic-version}.mgc when you first run the executable. Hence using this executable might not be desirable due to this behaviour which a regular compilation of pistol does not include. This behaviour is compiled into pistol if you use go {build,install} with -tags EMBED_MAGIC_DB.

=== From Source

Since Pistol depends on https://github.com/rakyll/magicmime[magicmime], you’ll need a libmagic package installed. Please refer to https://github.com/rakyll/magicmime/tree/v0.1.0#prerequisites[this section in Magicmime's README] for the appropriate commands for every OS. In particular, if you installed libmagic using brew on a Darwin system, you may need to use the CGO_FLAGS environment variable to compile pistol. See https://github.com/doronbehar/pistol/issues/6#issuecomment-1576673173[#6] for more details.

Assuming you've installed libmagic properly and you have https://golang.org/doc/install[setup a Go environment], Use the following command to install Pistol to $GOPATH/bin/pistol:

==== Go 1.16 or later

[source,sh]

go install github.com/doronbehar/pistol/cmd/pistol@latest

==== Go 1.15 or earlier

[source,sh]

env CGO_ENABLED=1 GO111MODULE=on go get -u github.com/doronbehar/pistol/cmd/pistol

footnote:[env GO111MODULE=on is needed due to a recent bug / issue https://github.com/golang/go/issues/31529[with Go], see https://github.com/doronbehar/pistol/issues/6[#6] for more details. CGO_ENABLED=1 is needed for https://github.com/rakyll/magicmime[magicmime], see https://github.com/doronbehar/pistol/issues/76[#76].]

Besides libmagic, https://repology.org/project/asciidoctor/information[`asciidoctor`] is also recommended, in order to compile the README as a manpage and install it. NixOS For instance, does it like this:

[source,nix]

...

nativeBuildInputs = [ installShellFiles asciidoctor ]; postInstall = '' asciidoctor -b manpage -d manpage README.adoc installManPage pistol.1 '';

...


https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/misc/pistol/default.nix[Link to currently evaluated file on NixOS]. Packagers for other distros should do something similar.

endif::[]

== Usage

.... $ pistol --help Usage: pistol OPTIONS [ ...]

OPTIONS

-V, --version Print version date and exit -c, --config configuration file to use (defaults to ~/.config/pistol/pistol.conf on Linux) -h, --help print help and exit

ARGUMENTS

file the file to preview extras extra arguments passed to the command ....

=== Integrations

==== Ranger / Lf

You can use Pistol as a file previewer in https://ranger.github.io/[Ranger] and https://github.com/gokcehan/lf[Lf]. For Ranger, set your preview_script in your rc.conf as follows:

.... set preview_script ~/.go/bin/pistol ....

The same goes for Lf, but in lfrc:

.... set previewer ~/.go/bin/pistol ....

==== fzf

If you use https://github.com/junegunn/fzf[fzf] to search for files, you can tell it to use pistol as the previewer. For example, the following command edits with your $EDITOR selected python file(s) using pistol as a previewer:

[source,sh]

$EDITOR "$(find -name '*.py' | fzf --preview='pistol {}')"

== Configuration

Although Pistol previews files of certain MIME types by default, it doesn't force you to use these internal previewers for these MIME types. You can change this behaviour by writing a configuration file in ~/.config/pistol/pistol.conf (or $XDG_CONFIG_HOME/pistol/pistol.conf) On GNU systems, with the syntax as explained below.

NOTE: On OS X, this location defaults to: ~/Library/Preferences/pistol/pistol.conf, as the XDG specification imposes.

=== Syntax

You can configure preview commands according to file path or mime type regex. The 1st word may is always interpreted first as a mime type regex such as: text/*.

If a line is not matched but the 1st word is exactly fpath, then the 2nd argument is interpreted as a file path regex, such as: /var/src/.*/README.md.

On every line, whether you used fpath or not, the next arguments are the command’s arguments, where %pistol-filename% is substituted by pistol to the file at question. You’ll see more examples in the following sections.

Both regular expressions (for file paths and for mime types) are interpreted by the https://golang.org/pkg/regexp/#Match[built-in library’s regexp.match]. Please refer to https://golang.org/pkg/regexp/syntax[this link] for the full reference regarding syntax.

==== Matching Mime Types

You can inspect the MIME type of any file on a GNU/Linux OS and on Mac OS with the command file --mime-type <file>.

For example, say you wish to replace Pistol’s internal text previewer with that of https://github.com/sharkdp/bat[bat]’s, you’d put the following in your pistol.conf:

.... text/* bat --paging=never --color=always %pistol-filename% ....

Naturally, your configuration file overrides the internal previewers.

Here’s another example which features http://w3m.sourceforge.net/[w3m] as an HTML previewer:

.... text/html w3m -T text/html -dump %pistol-filename% ....

And here’s an example that leverages ls for printing directories’ contents:

.... inode/directory ls -l --color %pistol-filename% ....

==== Matching File Path

For example, say you wish to preview all files that reside in a certain ./bin directory with https://github.com/sharkdp/bat[bat]’s syntax highlighting for bash. You could use:

.... fpath /var/src/my-bash-project/bin/[^/]+$ bat --map-syntax :bash --paging=never --color=always %pistol-filename% ....

==== A Note on RegEx matching

When Pistol parses your configuration file, as soon as it finds a match be it a file path match or a mime type match, it stops parsing it and it invokes the command written on the rest of the line. Therefor, if you wish to use the examples from above which use w3m and bat, you’ll need to put w3m’s line before bat’s line. Since otherwise, text/* will be matched first and text/html won’t be checked at all.

Similarly, you’d probably want to put fpath lines at the top.

Of course that this is a mere example, the same may apply to any regular expressions you’d choose to match against.

For a list of the internal regular expressions tested against when Pistol reverts to its native previewers, read the file https://github.com/doronbehar/pistol/blob/master/internal_writers/map.go#L8-L12[`internal_writers/map.go`].

More examples can be found in https://github.com/doronbehar/pistol/wiki/Config-examples[this WiKi page].

==== Quoting and Shell Piping

Pistol is pretty dumb when it parses your config, it splits all line by spaces, meaning that e.g:

[source,config]

application/json jq '.' %pistol-filename%

This will result in an error by https://github.com/stedolan/jq[`jq`]:

.... jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shell quoting issues?) at , line 1: '.' jq: 1 compile error ....

Indicating that jq got a literal '.'. When you run in your shell jq '.' file.json you don’t get an error because your shell is stripping the quotes around .. However, Pistol is not smarter then your shell because if you’d try for example:

[source,config]

application/json jq '.[] | .' %pistol-filename%

That would be equivalent to running in the typical shell:

[source,sh]

jq "\'.[]" "|" ".'" file.json

That’s because Pistol doesn't consider your quotes as interesting instructions, it just splits words by spaces. Hence, to overcome this disability, you can use:

[source,config]

application/json sh: jq '.' %pistol-filename%

Thanks to the sh: keyword at the beginning of the command’s definition, the rest of the line goes straight as a single argument to sh -c.

You can worry not about quoting / escaping the rest of the line - it’s not like when you run e.g sh -c 'command' in your shell where you need to make sure single quotes are escaped or not used at all inside command.

More over, with sh: you can use shell pipes:

[source,config]

fpath .*.md$ sh: bat --paging=never --color=always %pistol-filename% | head -8

==== Passing arbitrary extra arguments to commands

Pistol is capable of passing extra arguments to commands if the config says so. The arguments %pistol-extra0%, %pistol-extra1% and so on, are substituted by the extra arguments given to pistol, if these present in invocation and if they are present in the config. Example usage:

With this config:

.... fpath /problematic-bz2/..bz2 bzip2 %pistol-filename% %pistol-extra0% fpath /working-bz2/..bz2 bzip2 %pistol-filename% ....

Running:

.... pistol /problematic-bz2/example.bz2 --test ....

Will run bzip2 while testing the integrity of the compressed file. However, running:

.... pistol /working-bz2/example.bz2 --test ....

Will not pass the --test argument to bzip, due to %pistol-extra0 not present in the config for the files at /working-bz2. This feature is mainly present for usage with https://github.com/gokcehan/lf[Lf] and https://ranger.github.io/[Ranger] which can pass width height and x, y coordinates for image previews.

Here's an example usage for image previews that works with Lf: footnote:[pv script refers to https://github.com/neeshy/lfimg/blob/e9154721514a1384a89f2713092c15dc77992f37/pv[this script].]

.... image/.* pv %pistol-filename% %pistol-extra0% %pistol-extra1% %pistol-extra2% %pistol-extra3% ....

=== Environmental Variables

Pistol’s internal previewer for text files includes syntax highlighting thanks to the Go library https://github.com/alecthomas/chroma[chroma]. You can customize Pistol’s syntax highlighting formatting and style through environmental variables.

==== Chroma Formatters

The term formatter refers to the way the given file is presented in the terminal. These include:

Other formatters include json, and html but I’d be surprised if you’ll find them useful for Pistol’s purpose.

To tell Pistol to use a specific formatter, set PISTOL_CHROMA_FORMATTER in your environment, e.g:

[source,sh]

export PISTOL_CHROMA_FORMATTER=terminal16m

Recent versions of https://github.com/gokcehan/lf[Lf] support https://github.com/gokcehan/lf/pull/93[256 colors] in its preview window. AFAIK, footnote:[I don’t use Ranger anymore, ever since I moved to Lf. If you have evidence it does support 256 colors, let me know and I’ll change the default.], https://ranger.github.io/[Ranger] supports 8 colors and Lf’s color256 isn't enabled by default.

Therefor, I decided that it’ll be best to keep this variable unset in your general environment. If you do set color256 in your lfrc, you may feel free to set PISTOL_CHROMA_FORMATTER in your environment.

==== Chroma Styles

The term style refers to the set of colors used to print a given file. The chroma project documents all styles https://xyproto.github.io/splash/docs/all.html[here] and https://xyproto.github.io/splash/docs/longer/all.html[here].

The default style used by Pistol is pygments. To tell Pistol to use a specific style set PISTOL_CHROMA_STYLE in your environment, e.g:

[source,sh]

export PISTOL_CHROMA_STYLE=monokai

== Debugging

Can’t figure out way does Pistol acts the way he does? You can run pistol with:

[source,sh]

env PISTOL_DEBUG=1 pistol test-file

And you should be able to see messages that may give you a clue.

== NEWS

See https://github.com/doronbehar/pistol/releases[previous releases].