jaro is just another resource opener1. It uses a configuration file to determine which application should be used to open a file. Configuration file is actually a Scheme file which you can use anything that Scheme offers. You don't need to know Scheme to use jaro, configuration is pretty intuitive.
To open some file using jaro, just run:
jaro /path/to/some/file
jaro can also read from stdin
.
echo "path/to/file" | jaro
You can disable stdin
feature with passing --no-stdin
parameter.
Only dependency is guile
. Install it from your package manager. Then just put jaro
script somewhere in your path. You can also replace xdg-open
script with jaro
, if you know what you are doing.
Another option is that you can use this file to redirect xdg-open
calls to jaro
. Just put it into ~/.config/
directory and put this desktop file into ~/.local/share/applications/
.
jaro looks for the file ~/.config/associations
and loads it. This file contains multiple (assoc ...)
definitions and arbitrary Scheme code. jaro
will try to match the given URI with each association in order. I'll go trough some examples that shows you associating files/uris with programs.
- Here is a pretty basic rule for associating all image types with
sxiv
:
(assoc
#:pattern "image/.*"
#:program "sxiv %f")
- Here is another example that opens all Youtube links with
mpv
:
(assoc
#:pattern "^https?:https://(www.)?youtube.com/watch\\?.*v="
#:program "mpv %f")
- This association opens given any URL that starts with
http
orhttps
in qutebrowser if it's already open. If qutebrowser is not open, it'll simply open the URL in Firefox.
(assoc
#:pattern "^https?:https://.*"
#:program "qutebrowser %f"
#:test "pgrep qutebrowser"
#:on-fail "firefox %f")
- This example associates all text files with
vim
. If you calljaro
from command-line it'll simply openvim
with given file, but ifjaro
is called from another GUI application it'll openvim
in a new terminal. In this examplest
is the terminal emulator, so that ifjaro
is called from a GUI application it'll append the#:term
parameter before the#:program
parameter which will end up likest -e vim %f
.
(assoc
#:pattern "^text/.*"
#:program "vim %f"
#:term "st -e")
- This association opens given directory in
ranger
, like the previous example it runs ranger within a terminal if it's needed. But also ifjaro
is called within atmux
session, it splits the window and opensranger
in another split.
(assoc
#:pattern "inode/directory"
#:program "ranger %f"
#:term "st -e"
#:tmux "tmux split-window -h")
An association file may contain all of these associations and jaro
will try to match one of them.
A regular expression to match with mimetype or path. This is required. jaro
will try to match the pattern with mimetype first, if it fails it'll try to match for the path. This can also be a list of regular expressions, if one of them matches the association will be used. Lists are only for convenience, you can always use grouping with |
in a single regular expression.
Examples:
#:pattern "^image/png$" ;; png files
#:pattern "^https?:https://\\w+\\.com" ;; .com websites
#:pattern "\\.mp3" ;; mp3 files
#:pattern '("^application/(x-)?(pdf)$" "^text/postscript$") ;; pdf and postscript files
Patterns may also contain capture groups. You can use these capture groups in #:program
and it's derivatives with %1,%2,%3...
. For example:
(assoc
#:pattern "([0-9]+) - (.+)\.mp3"
#:program "notify-send 'Playing track number %1' 'Track name: %2'; mpv %f")
;; Suppose you called `jaro "02 - From Scythe to Sceptre.mp3"`,
;; It'll send a notification saying "Playing track number 2, Track name: From Scythe to Sceptre" then open it using mpv.
Capture groups do not work when you use list of patterns instead of a single pattern.
The program to open with. This is required. This can either be a string or a list of strings. If it's a string, it will be run in a shell, like /bin/sh
. If it's a list of strings, the program will be run directly. Before running, some substitution happens:
%f
is replaced with given relative path, it will be the path that you calledjaro
with.%F
is replaced with given path but this will be the absolute path.%U
is replaced with given path but in URI form. This is useful if the application only accepts URIs (likefile:https:///home/user/file.txt
).
Examples:
#:program "vim %f" ;; open in vim
#:program "echo %F | xclip -selection clipboard" ;; copy full path to clipboard
#:program '("mpv" "--autofit=1920x1080" "%f")
An arbitrary name to label the association. With given name, you can use the association in other associations. Observe this example:
;; This association opens youtube videos using mpv.
;; If mpv fails to open, it opens the youtube url with
;; association named 'browser.
(assoc
#:pattern "^https?:https://(www.)?youtube.com/watch\\?.*v="
#:program "mpv %f"
#:on-error (open-with 'browser))
;; The 'browser association
;; This association opens URLs in qutebrowser if it's open;
;; if it's not open, it uses firefox.
;; (The name should start with ')
(assoc
#:name 'browser
#:pattern "^https?:https://.*"
#:program "qutebrowser %f"
#:test "pgrep qutebrowser"
#:on-fail "firefox %f")
You can also use #:continue-on-error #t
instead of #:on-error (open-with 'browser)
to get the same desired effect in this particular example but labeling associations makes maintaining the associations file easier in the long run.
The program to run #:program
with, if jaro
is called from outside of a terminal. This command will be prepended in front of #:program
before calling. If #:standalone
is set to #t
, instead of appending before #:program
, #:term
will be called standalone. Like #:program
, this should be either a string or list of strings.
Examples:
;; This will open all text files in vim. When called from outside of terminal,
;; it will open vim in a new terminal.
(assoc
#:pattern "text/"
#:program "vim %f"
#:term "st -e")
;; This will open all text files in vim if it is called in a terminal, but if
;; it is called from outside of a terminal, it will open files in gedit.
;; (Yeah, the name #:term looks confusing here.)
(assoc
#:pattern "text/"
#:program "vim %f"
#:term "gedit %f"
#:standalone #t)
Command to run if jaro
is called inside a tmux
session. This command will be prepended in front of #:program
before calling. If #:standalone
is set to #t
, instead of appending before #:program
, #:tmux
will be called directly. Like #:program
, this should be either a string or list of strings.
Examples:
;; Open directories in ranger. If jaro is called inside a tmux session,
;; split window and open ranger in that split.
(assoc
#:pattern "inode/directory"
#:program "ranger %f"
#:tmux "tmux split-window")
;; Open http(s) links in firefox. If jaro is called inside a tmux session,
;; use w3m to open links in a new tmux split.
(assoc
#:pattern "https?:https://"
#:program "firefox %f"
#:tmux "tmux split-window w3m %f"
#:standalone #t)
Like #:tmux
but for screen
.
If this is set to #t
; #:term
, #:tmux
, #:screen
will be run standalone, instead of appending before #:program
. This is useful for using different programs in different working environments.
A test command. If #:pattern
is matched and #:test
is present, before calling the #:program
, jaro
will run #:test
. If command exits with 0
, #:program
will be run. Otherwise jaro
will skip this association like it hasn't been matched. Like #:program
, this should be either a string or list of strings.
Command to run if the #:test
fails. If #:test
fails and #:on-fail
is present, jaro
will execute #:on-fail
and stop trying next association.
Example:
;; Test if qutebrowser is running. If it is, then open the url in qutebrowser,
;; if it is not, then open the url in firefox.
(assoc
#:pattern "^https?:https://.*"
#:program "qutebrowser %f"
#:test "pgrep qutebrowser"
#:on-fail "firefox %f")
Command to run if #:program
(or #:on-fail
, #:term
, #:tmux
, #:screen
) exits with something other than 0
.
Example:
;; Open images with sxiv. If it fails to open image, opens the image with feh.
(assoc
#:pattern '"^image/.*$"
#:program "sxiv %f"
#:on-error "feh %f")
Command to run if #:program
(or #:on-fail
, #:term
, #:tmux
, #:screen
) exits with 0
.
If this is set to #t
; jaro
will continent trying to match other patterns when an error happens on the matched association. While you can control what happens when an error happens with #:on-error
, you can't control what happens if #:on-error
exits with an error. This is useful in situations like that. When you use #:continue-on-error
with #:on-error
; jaro will call #:on-error
first when #:program
exits with non-zero value. If #:on-error
exits successfully, jaro will stop trying to match. Otherwise it'll continue searching for next match.
Example:
(assoc
#:pattern '("https?:https://.+(png|jpg|gif)"
"^image/")
#:program "sxiv %f"
#:on-error "feh %f"
#:continue-on-error #t)
(assoc
#:pattern "^https?:https://.+"
#:program "firefox %f")
;; Consider this scenario: User opens an URL ending with `png`: "https://example.com/some_pic.png"
;; `sxiv` is unable to open that because it does not support URLs. So `feh` will
;; run. If feh exits successfully, nothing else will happen. But if `feh` also
;; fails to open, jaro will continue matching and it will open the URL with
;; `firefox`.
You can define arbitrary options (like #:edit
, #:view
etc. Anything but the ones explained above.) and start jaro
with a method to use that option. For example:
(assoc
#:pattern "^text/.*"
#:program "vim %f"
#:view "cat %f")
When jaro
is started with --method=view
, it will open given text file in cat
instead of #:program
. You can add arbitrary amount of opening modes. These modes, like #:program
option, should be either a string or a list of strings (or a scheme procedure).
Another example:
(assoc
#:pattern "image/.*"
#:program "sxiv %f"
#:view "sxiv %f"
#:edit "gimp %f")
;; As you can see, `#:view` and `#:program` are just the same thing,
;; you can use `#:view #t` to avoid duplication, so this means the
;; same thing with above:
(assoc
#:pattern "image/.*"
#:program "sxiv %f"
#:view #t
#:edit "gimp %f")
Now you can use jaro --method=view path/to/file
or jaro --method=edit path/to/file
to open an image. You can define aliases in your shell for these different opening modes, like for bash:
alias edit="jaro --method=edit"
alias view="jaro --method=view"
# ...
#:program
or derivatives can also be a procedure. You need to return #t
or #f
to make things work properly.
Add this association to end of your associations file. If nothing has been matched, this association will run and present you a dialog to select which application to use. It will display programs that supports opening mimetype of given file and all the binaries in your system.
;; If jaro is called inside a terminal, it will use fzf for selecting the
;; alternative, otherwise it will use dmenu. `select-alternative-with` function
;; simply pipes alternatives to given program, so you can use any other
;; dmenu-like program.
(assoc
#:pattern ".*"
#:program (select-alternative-with "fzf")
#:term (select-alternative-with "dmenu")
#:standalone #t)
;; This one uses dmenu all the time
(assoc
#:pattern ".*"
#:program (select-alternative-with "dmenu")
#:standalone #t)