Merge pull request 'feat(functions): add open-url and repo-open browser helpers' (#67) from feat-open-url-and-repo-open into main
Generate documentation / build-docs (push) Successful in 37s
Offline docs drift reminder / remind (push) Successful in 12s

Reviewed-on: #67
This commit was merged in pull request #67.
This commit is contained in:
2026-07-04 06:12:30 +00:00
7 changed files with 376 additions and 65 deletions
+1
View File
@@ -134,6 +134,7 @@ To browse the docs from the terminal:
| `help config <keyword> --html` | Open HTML docs at the matching section anchor |
| `help config --man` | Open the compiled man page via `man -l` |
| `help config <keyword> --man` | Open the man page jumping to the nearest match |
| `repo-open` | Open this repo's web page in the browser (deep-links to the current branch and sub-directory) |
The pager falls back through: **ov****bat****man -l****less****cat**.
+6
View File
@@ -313,6 +313,12 @@ abbr -a ssct 'sudo systemctl status'
abbr -a sscs 'sudo systemctl start'
abbr -a sscr 'sudo systemctl restart'
### Alternate command names ###
# Expand to the canonical function name so muscle-memory typos still work,
# while surfacing the real command instead of silently forwarding to it.
abbr -a open-repo repo-open
abbr -a url-open open-url
### History Expansions and Substitutions ###
# Bash-style history expansion is opinionated (C3 overrides), gated atomically
# with conf.d/tricks.fish, conf.d/puffer.fish, and functions/expand_*.fish.
+4
View File
@@ -157,6 +157,10 @@ spark=### spark
yt-dlp=### yt-dlp
miscfns=## 5.14 Miscellaneous
config-help=### config-help
open-url=### open-url
url-open=### open-url
repo-open=### repo-open
open-repo=### repo-open
config-update=### config-update
config-settings=### config-settings
config-toggle=### config-settings
+63
View File
@@ -1339,6 +1339,69 @@ Add -i (interactive confirmation) to destructive commands:
Also available as: help config [SECTION] [FLAGS]
### open-url
Synopsis: open-url [-s|--silent] [-v|--verbose] <url>
open-url -h | --help
Opens a URL or file:// URI in the best available graphical web browser,
backgrounded so it never blocks the terminal. Resolves a real browser
binary rather than deferring to xdg-open, whose MIME dispatch can hand
local text/html files to non-browser apps (e.g. ebook readers).
Silent by default: prints nothing on success (errors always go to
stderr). Pass --verbose / -v to report which browser is launched;
--silent / -s is accepted for explicitness.
Resolution order:
1. $fish_help_browser (explicit override)
2. $BROWSER (validated; errors if not a command)
3. xdg-mime default handler for x-scheme-handler/https
4. First known browser binary found in a built-in list
5. xdg-open (last resort)
open-url https://git.rootiest.dev/rootiest/fish-config
open-url "file://$HOME/.config/fish/docs/html/index.html"
Used internally by config-help --html.
Typo abbreviation: url-open (expands to open-url on space/enter).
### repo-open
Synopsis: repo-open [-p|--print] [-r|--root]
repo-open -h | --help
Opens the web page for the current repository's `origin` remote in a
browser (via open-url). Deep-links to the current branch when it exists
on the remote — falling back to the remote's default branch (main/master)
otherwise — and to the current sub-directory when run below the repo root.
The remote URL is normalized from HTTPS and SSH/scp forms
(git@host:owner/repo.git, ssh://…, https://…). The web path layout is
provider-specific; the provider is resolved in order:
1. git config browse.provider (per-repo or --global override)
2. Hostname heuristic (github / gitlab / gitea / bitbucket;
codeberg → gitea)
3. Default: github-style layout
Self-hosted hosts the heuristic can't classify (a Gitea/GitLab instance
on a custom domain) need a one-time override:
git config browse.provider gitea
Flags:
--print / -p Print the resolved URL instead of opening it.
--root / -r Ignore the current sub-directory; link to the repo root.
--help / -h Show usage.
repo-open
repo-open --print
repo-open --root
Typo abbreviation: open-repo (expands to repo-open on space/enter).
### config-update
Synopsis: config-update [-h] [-n] [-f]
+2 -65
View File
@@ -138,71 +138,8 @@ function config-help --description 'Open the offline fish shell configuration ma
set -l page_url "file://$html_dir/$html_path"
# Browser detection — mirrors fish's help.fish priority order but
# resolves actual browser binaries before falling back to xdg-open.
# xdg-open dispatches on the file's MIME type (text/html), which can
# be associated with non-browser apps (e.g. ebook readers). Using a
# real browser binary directly with a file:// URI avoids that lookup.
set -l graphical_browsers \
firefox firefox-esr chromium chromium-browser google-chrome \
brave-browser vivaldi vivaldi-stable epiphany falkon qutebrowser \
opera x-www-browser htmlview
set -l browser $fish_help_browser
if not set -q browser[1]
if set -q BROWSER
echo $BROWSER | read -at browser
if not type -q $browser[1]
set_color red
echo "error: \$BROWSER '$browser[1]' is not a valid command" >&2
set_color normal
return 1
end
else
# Resolve the https scheme handler from xdg-mime and use its
# binary directly — most reliable on modern Linux desktops.
if type -q xdg-mime
set -l desk (xdg-mime query default x-scheme-handler/https 2>/dev/null)
if test -n "$desk"
set -l candidate (string replace -r '\.desktop$' '' -- $desk)
if type -q $candidate
set browser $candidate
end
end
end
# Fall back to trying known browser binaries in order.
if not set -q browser[1]
for b in $graphical_browsers
if type -q -f $b
set browser $b
break
end
end
end
# Last resort: xdg-open (may hit wrong app for local files).
if not set -q browser[1]; and type -q xdg-open
set browser xdg-open
end
end
end
if not set -q browser[1]
set_color red
echo "error: could not find a web browser — set \$fish_help_browser or \$BROWSER" >&2
set_color normal
return 1
end
set_color green
echo "Opening HTML docs in $browser[1]…"
set_color normal
# Background the browser so it doesn't block the terminal.
sh -c '("$@") &' -- $browser $page_url
return 0
open-url $page_url
return $status
end
# ── --man / -m ───────────────────────────────────────────────
+126
View File
@@ -0,0 +1,126 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# open-url [-s|--silent] [-v|--verbose] <url>
# open-url --help
#
# DESCRIPTION
# Opens a URL (or file:// URI) in the best available graphical web browser,
# backgrounded so it never blocks the terminal. Resolves a real browser
# binary rather than deferring to xdg-open, whose MIME dispatch can hand
# local text/html files to non-browser apps (e.g. ebook readers).
#
# Silent by default: prints nothing on success (errors always go to stderr).
#
# Resolution order:
# 1. $fish_help_browser (explicit override)
# 2. $BROWSER (validated; errors if not a command)
# 3. xdg-mime default handler for x-scheme-handler/https
# 4. First known browser binary found in a built-in list
# 5. xdg-open (last resort)
#
# ARGUMENTS
# url The URL or file:// URI to open (required)
# -s, --silent Suppress success output (the default)
# -v, --verbose Print which browser is being launched
# -h, --help Print usage and exit
#
# RETURNS
# 0 Browser launched
# 1 No URL given, invalid $BROWSER, or no browser found
#
# EXAMPLE
# open-url https://git.rootiest.dev/rootiest/fish-config
# open-url -v "file://$HOME/.config/fish/docs/html/index.html"
function open-url --description 'Open a URL in the best available web browser'
argparse h/help s/silent v/verbose -- $argv
or return 1
if set -q _flag_help
echo "Usage: open-url [-s|--silent] [-v|--verbose] <url>"
echo "Open a URL or file:// URI in the best available web browser."
echo
echo " -s, --silent Suppress success output (the default)"
echo " -v, --verbose Print which browser is being launched"
echo " -h, --help Show this help"
return 0
end
set -l url $argv[1]
if test -z "$url"
set_color red
echo "error: open-url requires a URL argument" >&2
set_color normal
return 1
end
# Browser detection — mirrors fish's help.fish priority order but
# resolves actual browser binaries before falling back to xdg-open.
# xdg-open dispatches on the file's MIME type (text/html), which can
# be associated with non-browser apps (e.g. ebook readers). Using a
# real browser binary directly with a file:// URI avoids that lookup.
set -l graphical_browsers \
firefox firefox-esr chromium chromium-browser google-chrome \
brave-browser vivaldi vivaldi-stable epiphany falkon qutebrowser \
opera x-www-browser htmlview
set -l browser $fish_help_browser
if not set -q browser[1]
if set -q BROWSER
echo $BROWSER | read -at browser
if not type -q $browser[1]
set_color red
echo "error: \$BROWSER '$browser[1]' is not a valid command" >&2
set_color normal
return 1
end
else
# Resolve the https scheme handler from xdg-mime and use its
# binary directly — most reliable on modern Linux desktops.
if type -q xdg-mime
set -l desk (xdg-mime query default x-scheme-handler/https 2>/dev/null)
if test -n "$desk"
set -l candidate (string replace -r '\.desktop$' '' -- $desk)
if type -q $candidate
set browser $candidate
end
end
end
# Fall back to trying known browser binaries in order.
if not set -q browser[1]
for b in $graphical_browsers
if type -q -f $b
set browser $b
break
end
end
end
# Last resort: xdg-open (may hit wrong app for local files).
if not set -q browser[1]; and type -q xdg-open
set browser xdg-open
end
end
end
if not set -q browser[1]
set_color red
echo "error: could not find a web browser — set \$fish_help_browser or \$BROWSER" >&2
set_color normal
return 1
end
if set -q _flag_verbose
set_color green
echo "Opening $browser[1]…"
set_color normal
end
# Background the browser so it doesn't block the terminal, and discard its
# own console chatter (e.g. "Opening in existing browser session.").
sh -c '("$@") >/dev/null 2>&1 &' -- $browser $url
return 0
end
+174
View File
@@ -0,0 +1,174 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# repo-open [-p|--print] [-r|--root]
# repo-open --help
#
# DESCRIPTION
# Opens the web page for the current repository's `origin` remote in a
# browser (via open-url). Deep-links to the current branch when it exists
# on the remote, falling back to the remote's default branch (main/master)
# otherwise, and to the current sub-directory when invoked below the repo
# root.
#
# The remote URL is normalized from both HTTPS and SSH/scp forms
# (git@host:owner/repo.git, ssh://…, https://…). The web path layout is
# provider-specific; the provider is resolved in this order:
#
# 1. git config browse.provider (per-repo or --global override)
# 2. Hostname heuristic (github / gitlab / gitea / bitbucket;
# codeberg → gitea)
# 3. Default: github-style layout
#
# For a self-hosted host the heuristic can't classify (e.g. a Gitea or
# GitLab instance on a custom domain), set the provider once:
#
# git config browse.provider gitea
#
# ARGUMENTS
# -p, --print Print the resolved URL instead of opening it
# -r, --root Ignore the current sub-directory; link to the repo root
# -h, --help Print usage and exit
#
# RETURNS
# 0 URL opened (or printed)
# 1 Not a git repo, no origin remote, or browser launch failed
#
# EXAMPLE
# repo-open # open current branch (+ subdir) in browser
# repo-open --print # just print the URL
# repo-open --root # repo home page for the current branch
function repo-open --description 'Open the origin remote of the current repo in a browser'
argparse -X 0 h/help p/print r/root -- $argv
or return 1
if set -q _flag_help
echo "Usage: repo-open [-p|--print] [-r|--root]"
echo "Open the origin remote's web page for the current repo,"
echo "deep-linking to the current branch and sub-directory."
echo
echo " -p, --print Print the URL instead of opening it"
echo " -r, --root Link to the repo root, ignoring the current sub-directory"
echo " -h, --help Show this help"
return 0
end
if not git rev-parse --is-inside-work-tree >/dev/null 2>&1
set_color red
echo "error: not inside a git repository" >&2
set_color normal
return 1
end
set -l remote (git remote get-url origin 2>/dev/null)
if test -z "$remote"
set_color red
echo "error: no 'origin' remote configured" >&2
set_color normal
return 1
end
# ── Normalize remote → host + owner/repo path ────────────────
set -l had_scheme 0
if string match -qr '://' -- $remote
set had_scheme 1
end
set -l u (string replace -r '^[a-z0-9]+://' '' -- $remote) # strip scheme
set u (string replace -r '^[^@/]+@' '' -- $u) # strip user@
set u (string replace -r '\.git$' '' -- $u) # strip .git
if test $had_scheme -eq 0
set u (string replace ':' '/' -- $u) # scp: first colon → path sep
else
set u (string replace -r ':[0-9]+/' '/' -- $u) # url: drop :port
end
set -l parts (string split -m1 '/' -- $u)
set -l host $parts[1]
set -l repo_path $parts[2]
if test -z "$host"; or test -z "$repo_path"
set_color red
echo "error: could not parse origin remote: $remote" >&2
set_color normal
return 1
end
set -l base "https://$host/$repo_path"
# ── Resolve provider (config → hostname → default) ───────────
set -l provider (git config --get browse.provider 2>/dev/null)
if test -z "$provider"
switch $host
case 'github.com' 'www.github.com'
set provider github
case '*gitlab*'
set provider gitlab
case '*gitea*' 'codeberg.org'
set provider gitea
case '*bitbucket*'
set provider bitbucket
case '*'
set provider github # sensible default; override via browse.provider
end
end
# ── Determine the default branch (for the "on default" check)
set -l default_branch (git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null \
| string replace -r '^origin/' '')
if test -z "$default_branch"
for b in main master
if git rev-parse --verify --quiet refs/remotes/origin/$b >/dev/null 2>&1
set default_branch $b
break
end
end
end
test -z "$default_branch"; and set default_branch main
# ── Determine the branch to link to ──────────────────────────
set -l branch (git symbolic-ref --quiet --short HEAD 2>/dev/null)
if test -n "$branch"
# Prefer the local remote-tracking ref (offline, fast); fall back to a
# networked ls-remote before giving up on the current branch.
if not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null 2>&1
if not git ls-remote --exit-code --heads origin $branch >/dev/null 2>&1
set branch $default_branch
end
end
else
set branch $default_branch # detached HEAD
end
# ── Sub-directory relative to repo root ──────────────────────
set -l prefix ""
if not set -q _flag_root
set prefix (git rev-parse --show-prefix 2>/dev/null | string trim -r -c /)
end
# ── Assemble the provider-specific URL ───────────────────────
set -l seg
switch $provider
case gitlab
set seg "/-/tree/$branch"
case gitea
set seg "/src/branch/$branch"
case bitbucket
set seg "/src/$branch"
case '*' # github and default
set seg "/tree/$branch"
end
set -l url $base
if test -n "$prefix"
set url "$base$seg/$prefix"
else if test "$branch" != "$default_branch"
set url "$base$seg"
end
if set -q _flag_print
echo $url
return 0
end
open-url $url
return $status
end