feat: Dev Container (#1149)

* temporary dvctr

* start of work

* fix: timeouts in windows

* fish?

* feat: better, working stuff

* refactor: rename database-seeds to seeds/

* better

* fdsaf

* feat: don't let our container get owned by root

* fix: better user handling

* feat: falling asleep at my desk i yield to you what i have done

* feat: autoadminify

* docs: denote trickshot

* feat: open in browser on launch

* better seeds workings...

* fix: dont kill shell

* docs: mention dev/debs

* fix: whoops

* fix: make seeds load within the century

* feat: rest of the containerisation

* feat: new alias

* style: fix linter errors

* fix: bad call

* fix: better justfile
This commit is contained in:
zk
2024-08-17 15:01:40 +01:00
committed by GitHub
parent 14831a576c
commit 593adb2717
305 changed files with 6028 additions and 712 deletions
+29
View File
@@ -0,0 +1,29 @@
{
"name": "Tachi",
"dockerComposeFile": [
"../docker-compose-dev.yml"
],
"service": "tachi-dev",
"workspaceFolder": "/tachi",
"postAttachCommand": "./dev/bootstrap.sh",
"updateRemoteUserUID": true,
"containerUser": "tachi",
"customizations": {
"vscode": {
"settings": {
"typescript.tsdk": "node_modules/typescript/lib",
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
},
"extensions": [
"dbaeumer.vscode-eslint"
]
}
}
}
+2 -2
View File
@@ -5,5 +5,5 @@ node_modules
**/.tsbuildinfo
# Extracted data from iidx merge script
database-seeds/scripts/rerunners/iidx/iidx-mdb-parse/ifs-output
database-seeds/scripts/rerunners/iidx/iidx-mdb-parse/logs
seeds/scripts/rerunners/iidx/iidx-mdb-parse/ifs-output
seeds/scripts/rerunners/iidx/iidx-mdb-parse/logs
+5 -5
View File
@@ -1,17 +1,17 @@
name: Database-Seeds CI/CD
name: Seeds CI/CD
on:
push:
branches:
- "main"
paths:
- "database-seeds/**"
- "seeds/**"
- "common/**"
pull_request:
branches:
- "main"
paths:
- "database-seeds/**"
- "seeds/**"
- "common/**"
workflow_dispatch:
@@ -32,10 +32,10 @@ jobs:
cache: pnpm
- name: Install dependencies
run: pnpm --filter tachi-database-seeds-scripts... --filter . install
run: pnpm --filter tachi-seeds-scripts... --filter . install
- name: Run Tests
run: pnpm --filter tachi-database-seeds-scripts test
run: pnpm --filter tachi-seeds-scripts test
env:
NODE_ENV: "test"
deploy:
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"ms-vscode-remote.remote-containers"
]
}
+1 -2
View File
@@ -5,6 +5,5 @@
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
}
},
}
-15
View File
@@ -1,15 +0,0 @@
# Yes, I've made an entire docker container just to run a bash script
# instead of rewriting the script in a ~~real~~ cross-platform scripting language.
# This docker container **depends** on an external volume being mounted
# (see how it's used in docker-compose-dev.yml)
# and *intentionally* mutates state outside of its container.
# I really do not care.
# Want it better? Do it yourself.
FROM node:20 AS base
RUN npm install --silent -g pnpm@8.15.6
WORKDIR /app
CMD ["bash", "_scripts/bootstrap.sh"]
+55
View File
@@ -0,0 +1,55 @@
# For use via `devcontainer.json`. The docker-compose-dev file sets some other
# important variables.
# we use ubuntu because 'just' isn't available on debian 13
# haha.. haha...
FROM ubuntu:24.10
WORKDIR /tachi
RUN apt update
# essentials
RUN apt install -y git npm locales sudo
# setup locales
# https://stackoverflow.com/questions/28405902/how-to-set-the-locale-inside-a-debian-ubuntu-docker-container
RUN echo 'en_US.UTF-8' > /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
# nodeisms
RUN npm install --silent -g pnpm@8.15.6
# docs pythonisms
RUN apt install -y python-is-python3 pip mkdocs mkdocs-material
# https://github.com/python-babel/babel/issues/990
# it wouldn't be python without needing absurd global state manipulation to fix
# an incoherent error message
RUN rm -f /etc/localtime && ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime
# Fix locale issue perl repeatedly complains about
RUN echo "LC_ALL=en_US.UTF-8\nLANG=en_US.UTF-8" > /etc/default/locale
# nice to haves
RUN apt install -y gh fish just fzf curl wget parallel neovim fd-find bat
# `fd` is called `find-fd` on ubuntu. Awesome.
RUN ln -s $(which fdfind) /usr/bin/fd
# rename ubuntu user to tachi and give them sudo
RUN usermod -l tachi ubuntu && \
usermod -d /home/tachi -m tachi && \
echo "tachi ALL = NOPASSWD : ALL" >> /etc/sudoers
# Docker volumes are mounted as root UNLESS the folder already exists inside the host
# and has non-root ownership. This is the only way to declare a volume in docker has
# non-root ownership. Unbelievably obscure.
RUN mkdir node_modules && \
mkdir .pnpm-store && \
chown tachi:1000 node_modules .pnpm-store
# keep container alive indefinitely
CMD ["/bin/fish"]
-26
View File
@@ -1,26 +0,0 @@
# no alpine here as we want bash + general nice-to-haves
FROM node:20 AS base
RUN npm install --silent -g pnpm@8.15.6
FROM base AS build
WORKDIR /app
COPY pnpm-lock.yaml .
COPY patches ./patches
RUN pnpm fetch
COPY database-seeds ./database-seeds
COPY common ./common
COPY *.json *.yaml ./
RUN pnpm --filter tachi-common... --filter . install --offline --frozen-lockfile
RUN pnpm --filter tachi-database-seeds-scripts... --filter . install --offline --frozen-lockfile
WORKDIR /app/database-seeds
# keep container alive indefinitely
# we just have this container so db-seeds maintainers have a nice "context"
# to work with
CMD ["tail", "-f", "/dev/null"]
+48
View File
@@ -0,0 +1,48 @@
mod seeds
mod server
mod client
mod docs
[private]
interactive:
-@just --choose
# Run the frontend and backend for Tachi.
#
# This is the main command you want to use to start up tachi. Go for it!
start:
parallel --lb ::: 'FORCE_COLOR=1 just server start' 'FORCE_COLOR=1 just client start'
# test everything
test:
just server test
just client test
just seeds test
latest_dataset := "2024-05"
# Load the latest Kamaitachi dataset. This is put in the "anon-kamai" database.
load-kamai-dataset:
wget -O- https://cdn-kamai.tachi.ac/datasets/{{latest_dataset}}.dump | mongorestore --uri='mongodb://mongo' --gzip --archive
echo "Successfully loaded. You should change 'server/conf.json5' to use anon-kamai as the database."
# Load the latest Bokutachi dataset. This is put in the "anon-boku" database.
load-boku-dataset:
wget -O- https://cdn-boku.tachi.ac/datasets/{{latest_dataset}}.dump | mongorestore --uri='mongodb://mongo' --gzip --archive
echo "Successfully loaded. You should change 'server/conf.json5' to use anon-boku as the database."
# Check that the data in MongoDB makes any sense.
validate-db:
cd server/ && pnpm validate-database
# reload the shell setup
setup-fish:
@fish dev/setup.fish
@exec fish
# force a re-bootstrap
bootstrap:
-@rm I_HAVE_BOOTSTRAPPED_OK
./dev/bootstrap.sh
+20 -5
View File
@@ -28,13 +28,28 @@ Check the [Documentation](https://docs.tachi.ac/contributing/setup) for how to s
You can then check the component-specific guides to see how to run those components and contribute back!
## Quick Setup For Nerds
## Quick Setup For Experienced Programmers
Already know what you're doing?
Install VSCode and use the dev container extension.
This is my the supported way of working and will ensure you have the correct versions of everything.
Install docker + docker-compose and use `./run.sh start` or `./run.bat start` to start Tachi.
### Unsupported stuff
Other commands you might want to execute are included inside the `run` file.
Tachi is intended to be developed inside a container. This ensures that you have the correct version of MongoDB, Redis, Typescript and all that jazz.
VSCode has excellent native support for dev containers, and as such this is the only method of local development we officially support.
Over the years we have had a *lot* of issues with people having subtle variations on their system (or on windows). Given the contributor-centricity of Tachi, it's untenable to expect every contributor to be an expert with local dev setup.
The devcontainer provides us with the most simple, consistent experience, and allows us to put nice-to-haves inside the user's shell.
That said, if you're ardently against using VSCode or Docker...
**DO NOT REPORT ISSUES TO ME IF YOU DO THIS**
You can run the docker-compose file and `dev/boostrap.sh` inside the container yourself. Work inside the container.
Alternatively if you want to work outside of docker you're on your own. Figure out the correct versions for everything (npm, pnpm, mongo, redis, ts-node...) and `dev/bootstrap.sh`.
## Repository Info
@@ -48,7 +63,7 @@ The client and the server are fairly decoupled. Someone could trivially create t
This contains all of our API calls, and interfaces with our database, and powers the actual score import engine.
- `database-seeds/`, Which is a git-tracked set of data to be synced with Tachi. (unlicense)
- `seeds/`, Which is a git-tracked set of data to be synced with Tachi. (unlicense)
**This is the source of truth for the songs, charts, and more on the site!**
By submitting PRs to this, you can fix bugs on the website, add new charts, and more.
+1 -1
View File
@@ -20,7 +20,7 @@ for kind in "kamai" "boku"; do
mongodump --archive --port=$remote_port --db=$kind | mongorestore --archive --nsFrom="$kind.*" --nsTo="anon-$kind.*"
pnpm ts-node src/scripts/anonymise-db 127.0.0.1:27017/anon-$kind
ts-node src/scripts/anonymise-db 127.0.0.1:27017/anon-$kind
TCHIS_CONF_LOCATION=$kind.dataset.conf.json5 pnpm set-indexes
+12
View File
@@ -0,0 +1,12 @@
[private]
interactive:
-@cd ../ && just
# Run the client interactively. Changes made to files will trigger reloads.
start:
pnpm dev
# Test that the client passes typechecking and linting.
test:
pnpm typecheck
pnpm lint
+1 -1
View File
@@ -4,7 +4,7 @@
"private": true,
"license": "AGPL3",
"scripts": {
"start": "vite",
"dev": "vite --open",
"build": "vite build",
"lint": "eslint src",
"lint-fix": "eslint src --fix",
+2 -2
View File
@@ -271,9 +271,9 @@ function RevSelector({
const params = new URLSearchParams();
if (collection) {
params.set("path", `database-seeds/collections/${collection}`);
params.set("path", `seeds/collections/${collection}`);
} else {
params.set("path", "database-seeds/collections");
params.set("path", "seeds/collections");
}
const res = await fetch(
+10 -9
View File
@@ -36,25 +36,26 @@ try {
}
</style>
<div class="box">
<h1>Failed to connect!</h1>
<div>Welp. Looks like we're down. Sorry about that.</div>
<div>Chances are, this is just a temporary outage and will be fixed soon.</div>
<div style="font-size: 1.25rem; margin-top: 1rem; margin-bottom: 1rem;">
Please be patient, <a href="https://github.com/zkrising/Tachi">Tachi is maintained by a very small team.</a>
</div>
<div>An error message can be found in the console. (<code>Ctrl-Shift-I</code>)</div>
${
process.env.VITE_IS_LOCAL_DEV
? `
<hr />
<div><b>You're in local development mode.</b>
<h1><b>Couldn't connect to the server.</b></h1>
<h3>You are in local development mode.</h3>
<ul style="font-size: 2rem;">
<li>Have you accepted the HTTPS certificates for <a href="${ToAPIURL(
"/status"
)}">the server?</a>. If not, the site won't load.</li>
</ul>
`
: ""
: `<h1>Failed to connect!</h1>
<div>Welp. Looks like we're down. Sorry about that.</div>
<div>Chances are, this is just a temporary outage and will be fixed soon.</div>
<div style="font-size: 1.25rem; margin-top: 1rem; margin-bottom: 1rem;">
Please be patient, <a href="https://github.com/zkrising/Tachi">Tachi is maintained by a very small team.</a>
</div>
<div>An error message can be found in the console. (<code>Ctrl-Shift-I</code>)</div>`
}
</div>
`);
+2 -2
View File
@@ -30,7 +30,7 @@ import { CreateQuestMap, Dedupe, JSONAttributeDiff, JSONCompare } from "./misc";
import { ValueGetterOrHybrid } from "./ztable/search";
/**
* Given a repo and a reference return the status of database-seeds/collections
* Given a repo and a reference return the status of seeds/collections
* as of that commit.
*
* If "WORKING_DIRECTORY" is passed as a ref, and the repository is local, this will
@@ -63,7 +63,7 @@ export async function LoadSeeds(repo: string, ref: string): Promise<Partial<AllD
const res = await fetch(
`https://raw.githubusercontent.com/${repo.substring(
"GitHub:".length
)}/${ref}/database-seeds/collections/${file}`
)}/${ref}/seeds/collections/${file}`
);
if (res.status === 404) {
-70
View File
@@ -1,70 +0,0 @@
throw new Error(`This script doesn't work at the moment. Use mongoexp.sh.`);
const monk = require("monk");
const { Command } = require("commander");
const { StaticConfig } = require("tachi-common");
const logger = require("./logger");
const path = require("path");
const fs = require("fs");
const DeterministicCollectionSort = require("./deterministic-collection-sort");
const { RemoveUnderscoreID } = require("./remove-_id");
const program = new Command();
program
.option("-c, --connection <127.0.0.1:27017/my_database>")
.option("-t, --useTabs")
.option("-a, --all")
.argument("[collection]");
program.parse(process.argv);
const options = program.opts();
if (!options.connection) {
throw new Error(`Please specify a database connection string with -c or --connection.`);
}
// This will blow up horrifically if the connection URL is malformed.
// Just don't do that, I guess!
const db = monk(options.connection);
const collectionsDir = path.join(__dirname, "../collections");
const collections = [];
if (options.all) {
collections.push("folders", "tables");
collections.push(...StaticConfig.allSupportedGames.map((e) => `songs-${e}`));
collections.push(...StaticConfig.allSupportedGames.map((e) => `charts-${e}`));
} else if (program.args[0]) {
collections.push(program.args[0]);
}
// Add the songs-{game} and charts-{game} collections.
(async () => {
logger.info(`Exporting ${collections.length} collections.`);
for (const collection of collections) {
logger.info(`Exporting ${collection}.`);
await ExportCollection(collection);
}
RemoveUnderscoreID();
DeterministicCollectionSort();
logger.info(`Done!`);
process.exit(0);
})();
async function ExportCollection(collection) {
const data = await db.get(collection).find({});
logger.info(`Got ${data.length} documents.`);
const pt = path.join(collectionsDir, `${collection}.json`);
fs.writeFileSync(pt, JSON.stringify(data, null, options.useTabs ? "\t" : undefined));
logger.info(`Wrote to ${pt}.`);
}
-19
View File
@@ -1,19 +0,0 @@
#! /bin/bash
# Exports data from a mongodb instance back to the collections
# folder.
# Useful for backporting updates, or something.
set -eo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: mongoexp.sh <collection> <dbname>"
exit
fi
mongoexport -d "$2" -c "$1" --jsonArray > ../collections/"$1".json
node remove-_id.js
node deterministic-collection-sort.js
-19
View File
@@ -1,19 +0,0 @@
const { IterateCollections } = require("./util");
function RemoveUnderscoreID() {
IterateCollections((data) => {
for (const d of data) {
delete d._id;
}
return data;
});
}
if (require.main === module) {
RemoveUnderscoreID();
}
module.exports = {
RemoveUnderscoreID,
};
+4
View File
@@ -0,0 +1,4 @@
# Tachi Dev Stuff
Various stuff related to your local tachi dev container. These are mostly novelties,
like having a pretty shell and good completions.
+9
View File
@@ -0,0 +1,9 @@
# define new permanent aliases here...
defnew g=git
defnew gp=git push
defnew gsw=git switch
defnew gswc=git switch -c
defnew gsm=git switch main
defnew gl=git pull
defnew rr="exec fish"
+27 -17
View File
@@ -1,20 +1,23 @@
#! /bin/bash
#!/bin/bash
# moves example .env files, generates certificates, etc.
set -eox pipefail
set -eo pipefail
# https://stackoverflow.com/questions/59895/how-can-i-get-the-directory-where-a-bash-script-is-located-from-within-the-scrip
# if you actually think bash is a good programming language you are
# *straight up delusional*
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "$SCRIPT_DIR";
cd ..;
if [ -e I_HAVE_BOOTSTRAPPED_OK ]; then
echo "Already bootstrapped."
exit 0
fi
function installLocalDebs {
for file in /tachi/dev/deb/*.deb; do
sudo apt install -f -y "$file";
done
}
function setupShell {
echo "Setting up fish..."
fish dev/setup.fish
}
function mvExampleFiles {
echo "Moving example config files into usable places..."
@@ -55,16 +58,14 @@ function pnpmInstall {
exit 1
fi
pnpm fetch
pnpm install
# install ts-node aswell so people can use that inside enter-seeds.
#
# forcibly install this to /usr/local/bin because otherwise pnpm needs
# a bunch of other env vars and needs to modify .bashrc and all that jazz
#
# we're installing a global binary; it should go in a global binary place
# and this just works. sweet
PNPM_HOME=/usr/local/bin pnpm install ts-node -g
# This requires quite a bit of ceremony as pnpm wants to install to PNPM_HOME
PATH=$PATH:~/.local/pnpm
PNPM_HOME=~/.local/pnpm pnpm install ts-node -g
echo "Installed dependencies."
}
@@ -87,17 +88,26 @@ function syncDatabaseWithSeeds {
(
cd server
pnpm run sync-database-local
pnpm run load-seeds
)
echo "Synced."
}
# always setup the shell
setupShell
if [ -e I_HAVE_BOOTSTRAPPED_OK ]; then
echo "Already bootstrapped."
exit 0
fi
mvExampleFiles
selfSignHTTPS
pnpmInstall
setIndexes
syncDatabaseWithSeeds
installLocalDebs
echo "Bootstrap Complete."
+5
View File
@@ -0,0 +1,5 @@
MongoDB tools aren't available in apt. I want to have mongodb-database-tools inside the container.
I don't like having `curl | sh` inside containers, so I've saved the file to the repo.
Any `.deb` file in here will be installed inside the container.
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
for file in /tachi/dev/deb/*.deb; do
apt install -y "$file"
done
+7
View File
@@ -0,0 +1,7 @@
Copyright © Jorge Bucaran <<https://jorgebucaran.com>>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+193
View File
@@ -0,0 +1,193 @@
# Fisher
> A plugin manager for [Fish](https://fishshell.com)—your friendly interactive shell. [Snag fresh plugins!](https://github.com/jorgebucaran/awsm.fish#readme)
Take control of functions, completions, bindings, and snippets from the command line. Unleash your shell's true potential, perfect your prompt, and craft repeatable configurations across different systems effortlessly. Fisher's zero impact on shell startup keeps your shell zippy and responsive. No gimmicks, just smooth sailing!
- Fisher is 100% pure-Fish, making it easy to contribute or modify
- Scorching fast concurrent plugin downloads that'll make you question reality
- Zero configuration needed—we're not kidding!
- Oh My Fish! plugins supported too
> #### ☝️ [Upgrading from Fisher `3.x` or older? Strap in and read this!](https://github.com/jorgebucaran/fisher/issues/652)
## Installation
```console
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher
```
## Quickstart
Fisher lets you install, update, and remove plugins like a boss. Revel in Fish's [tab completion](https://fishshell.com/docs/current/index.html#completion) and rich syntax highlighting while you're at it.
### Installing plugins
To install plugins, use the `install` command and point it to the GitHub repository.
```console
fisher install jorgebucaran/nvm.fish
```
> Wanna install from GitLab? No problemo—just prepend `gitlab.com/` to the plugin path.
You can also snag a specific version of a plugin by adding an `@` symbol after the plugin name, followed by a tag, branch, or [commit](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefcommit-ishacommit-ishalsocommittish).
```console
fisher install IlanCosman/tide@v5
```
And hey, you can install plugins from a local directory too!
```console
fisher install ~/path/to/plugin
```
> Heads up! Fisher expands plugins into your Fish configuration directory by default, overwriting existing files. If that's not your jam, set `$fisher_path` to your preferred location and put it in your function path ([#640](https://github.com/jorgebucaran/fisher/issues/640)).
### Listing plugins
Use the `list` command to see all your shiny installed plugins.
```console
$ fisher list
jorgebucaran/fisher
ilancosman/tide@v5
jorgebucaran/nvm.fish
/home/jb/path/to/plugin
```
The `list` command also plays nice with regular expressions for filtering the output.
```console
$ fisher list \^/
/home/jb/path/to/plugin
```
### Updating plugins
`update` command to the rescue! It updates one or more plugins to their latest and greatest version.
```console
fisher update jorgebucaran/fisher
```
> Just type `fisher update` to update everything in one fell swoop.
### Removing plugins
Say goodbye to installed plugins with the `remove` command.
```console
fisher remove jorgebucaran/nvm.fish
```
Feeling destructive? Wipe out everything, including Fisher itself.
```console
fisher list | fisher remove
```
## Using your `fish_plugins` file
Whenever you install or remove a plugin from the command line, Fisher jots down all the installed plugins in `$__fish_config_dir/fish_plugins`. Add this file to your dotfiles or version control to easily share your configuration across different systems.
You can also edit this file and run `fisher update` to commit changes like a pro:
```console
$EDITOR $__fish_config_dir/fish_plugins
```
```diff
jorgebucaran/fisher
ilancosman/tide@v5
jorgebucaran/nvm.fish
+ PatrickF1/fzf.fish
- /home/jb/path/to/plugin
```
```console
fisher update
```
This will install **PatrickF1**/**fzf.fish**, remove /**home**/**jb**/**path**/**to**/**plugin**, and update everything else.
## Creating a plugin
Plugins can include any number of files in `functions`, `conf.d`, and `completions` directories. Most plugins are just a single function or a [configuration snippet](https://fishshell.com/docs/current/index.html#configuration). Behold the anatomy of a typical plugin:
<pre>
<b>flipper</b>
├── <b>completions</b>
│ └── flipper.fish
├── <b>conf.d</b>
│ └── flipper.fish
└── <b>functions</b>
└── flipper.fish
</pre>
Non `.fish` files and directories inside these locations will be copied to `$fisher_path` under `functions`, `conf.d`, or `completions` respectively.
### Event system
Fish [events](https://fishshell.com/docs/current/cmds/emit.html) notify plugins when they're being installed, updated, or removed.
> Keep in mind, `--on-event` functions must be loaded when their event is emitted. So, place your event handlers in the `conf.d` directory.
```fish
# Defined in flipper/conf.d/flipper.fish
function _flipper_install --on-event flipper_install
# Set universal variables, create bindings, and other initialization logic.
end
function _flipper_update --on-event flipper_update
# Migrate resources, print warnings, and other update logic.
end
function _flipper_uninstall --on-event flipper_uninstall
# Erase "private" functions, variables, bindings, and other uninstall logic.
end
```
## Creating a theme
A theme is like any other Fish plugin, but with a `.theme` file in the `themes` directory. Themes were introduced in [Fish `3.4`](https://github.com/fish-shell/fish-shell/releases/tag/3.4.0) and work with the `fish_config` builtin. A theme can also have files in `functions`, `conf.d`, or `completions` if necessary. Check out what a typical theme plugin looks like:
<pre>
<b>gills</b>
├── <b>conf.d</b>
│ └── gills.fish
└── <b>themes</b>
└── gills.theme
</pre>
### Using `$fisher_path` with themes
If you customize `$fisher_path` to use a directory other than `$__fish_config_dir`, your themes won't be available via `fish_config`. That's because Fish expects your themes to be in `$__fish_config_dir/themes`, not `$fisher_path/themes`. This isn't configurable in Fish yet, but there's [a request to add that feature](https://github.com/fish-shell/fish-shell/issues/9456).
Fear not! You can easily solve this by symlinking Fisher's `themes` directory into your Fish config. First, backup any existing themes directory.
```console
mv $__fish_config_dir/themes $__fish_config_dir/themes.bak
```
Next, create a symlink for Fisher's themes directory.
```console
ln -s $fisher_path/themes $__fish_config_dir/themes
```
Want to use theme plugins and maintain your own local themes? You can do that too ([#708](https://github.com/jorgebucaran/fisher/issues/708)).
## Discoverability
While Fisher doesn't rely on a central plugin repository, discovering new plugins doesn't have to feel like navigating uncharted waters. To boost your plugin's visibility and make it easier for users to find, [add relevant topics to your repository](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics#adding-topics-to-your-repository) using [`fish-plugin`](https://github.com/topics/fish-plugin). By doing so, you're not only contributing to the Fisher community but also enabling users to explore new plugins and enhance their Fish shell experience. Don't let plugin discovery be a fishy business, tag your plugins today!
## Acknowledgments
Fisher started its journey in 2016 by [@jorgebucaran](https://github.com/jorgebucaran) as a shell configuration manager for Fish. Along the way, many helped shape it into what it is today. [Oh My Fish](https://github.com/oh-my-fish/oh-my-fish) paved the way as the first popular Fish framework. [@jethrokuan](https://github.com/jethrokuan) provided crucial support during the early years. [@PatrickF1](https://github.com/PatrickF1)'s candid feedback proved invaluable time and again. Bootstrapping Fisher was originally [@IlanCosman](https://github.com/IlanCosman)'s brilliant idea. Thank you to all our contributors! <3
## License
[MIT](LICENSE.md)
@@ -0,0 +1,240 @@
function fisher --argument-names cmd --description "A plugin manager for Fish"
set --query fisher_path || set --local fisher_path $__fish_config_dir
set --local fisher_version 4.4.4
set --local fish_plugins $__fish_config_dir/fish_plugins
switch "$cmd"
case -v --version
echo "fisher, version $fisher_version"
case "" -h --help
echo "Usage: fisher install <plugins...> Install plugins"
echo " fisher remove <plugins...> Remove installed plugins"
echo " fisher update <plugins...> Update installed plugins"
echo " fisher update Update all installed plugins"
echo " fisher list [<regex>] List installed plugins matching regex"
echo "Options:"
echo " -v, --version Print version"
echo " -h, --help Print this help message"
echo "Variables:"
echo " \$fisher_path Plugin installation path. Default: $__fish_config_dir" | string replace --regex -- $HOME \~
case ls list
string match --entire --regex -- "$argv[2]" $_fisher_plugins
case install update remove
isatty || read --local --null --array stdin && set --append argv $stdin
set --local install_plugins
set --local update_plugins
set --local remove_plugins
set --local arg_plugins $argv[2..-1]
set --local old_plugins $_fisher_plugins
set --local new_plugins
test -e $fish_plugins && set --local file_plugins (string match --regex -- '^[^\s]+$' <$fish_plugins)
if ! set --query argv[2]
if test "$cmd" != update
echo "fisher: Not enough arguments for command: \"$cmd\"" >&2 && return 1
else if ! set --query file_plugins
echo "fisher: \"$fish_plugins\" file not found: \"$cmd\"" >&2 && return 1
end
set arg_plugins $file_plugins
end
for plugin in $arg_plugins
set plugin (test -e "$plugin" && realpath $plugin || string lower -- $plugin)
contains -- "$plugin" $new_plugins || set --append new_plugins $plugin
end
if set --query argv[2]
for plugin in $new_plugins
if contains -- "$plugin" $old_plugins
test "$cmd" = remove &&
set --append remove_plugins $plugin ||
set --append update_plugins $plugin
else if test "$cmd" = install
set --append install_plugins $plugin
else
echo "fisher: Plugin not installed: \"$plugin\"" >&2 && return 1
end
end
else
for plugin in $new_plugins
contains -- "$plugin" $old_plugins &&
set --append update_plugins $plugin ||
set --append install_plugins $plugin
end
for plugin in $old_plugins
contains -- "$plugin" $new_plugins || set --append remove_plugins $plugin
end
end
set --local pid_list
set --local source_plugins
set --local fetch_plugins $update_plugins $install_plugins
set --local fish_path (status fish-path)
echo (set_color --bold)fisher $cmd version $fisher_version(set_color normal)
for plugin in $fetch_plugins
set --local source (command mktemp -d)
set --append source_plugins $source
command mkdir -p $source/{completions,conf.d,themes,functions}
$fish_path --command "
if test -e $plugin
command cp -Rf $plugin/* $source
else
set temp (command mktemp -d)
set repo (string split -- \@ $plugin) || set repo[2] HEAD
if set path (string replace --regex -- '^(https://)?gitlab.com/' '' \$repo[1])
set name (string split -- / \$path)[-1]
set url https://gitlab.com/\$path/-/archive/\$repo[2]/\$name-\$repo[2].tar.gz
else
set url https://api.github.com/repos/\$repo[1]/tarball/\$repo[2]
end
echo Fetching (set_color --underline)\$url(set_color normal)
if command curl -q --silent -L \$url | command tar -xzC \$temp -f - 2>/dev/null
command cp -Rf \$temp/*/* $source
else
echo fisher: Invalid plugin name or host unavailable: \\\"$plugin\\\" >&2
command rm -rf $source
end
command rm -rf \$temp
end
set files $source/* && string match --quiet --regex -- .+\.fish\\\$ \$files
" &
set --append pid_list (jobs --last --pid)
end
wait $pid_list 2>/dev/null
for plugin in $fetch_plugins
if set --local source $source_plugins[(contains --index -- "$plugin" $fetch_plugins)] && test ! -e $source
if set --local index (contains --index -- "$plugin" $install_plugins)
set --erase install_plugins[$index]
else
set --erase update_plugins[(contains --index -- "$plugin" $update_plugins)]
end
end
end
for plugin in $update_plugins $remove_plugins
if set --local index (contains --index -- "$plugin" $_fisher_plugins)
set --local plugin_files_var _fisher_(string escape --style=var -- $plugin)_files
if contains -- "$plugin" $remove_plugins
for name in (string replace --filter --regex -- '.+/conf\.d/([^/]+)\.fish$' '$1' $$plugin_files_var)
emit {$name}_uninstall
end
printf "%s\n" Removing\ (set_color red --bold)$plugin(set_color normal) " "$$plugin_files_var | string replace -- \~ ~
set --erase _fisher_plugins[$index]
end
command rm -rf (string replace -- \~ ~ $$plugin_files_var)
functions --erase (string replace --filter --regex -- '.+/functions/([^/]+)\.fish$' '$1' $$plugin_files_var)
for name in (string replace --filter --regex -- '.+/completions/([^/]+)\.fish$' '$1' $$plugin_files_var)
complete --erase --command $name
end
set --erase $plugin_files_var
end
end
if set --query update_plugins[1] || set --query install_plugins[1]
command mkdir -p $fisher_path/{functions,themes,conf.d,completions}
end
for plugin in $update_plugins $install_plugins
set --local source $source_plugins[(contains --index -- "$plugin" $fetch_plugins)]
set --local files $source/{functions,themes,conf.d,completions}/*
if set --local index (contains --index -- $plugin $install_plugins)
set --local user_files $fisher_path/{functions,themes,conf.d,completions}/*
set --local conflict_files
for file in (string replace -- $source/ $fisher_path/ $files)
contains -- $file $user_files && set --append conflict_files $file
end
if set --query conflict_files[1] && set --erase install_plugins[$index]
echo -s "fisher: Cannot install \"$plugin\": please remove or move conflicting files first:" \n" "$conflict_files >&2
continue
end
end
for file in (string replace -- $source/ "" $files)
command cp -RLf $source/$file $fisher_path/$file
end
set --local plugin_files_var _fisher_(string escape --style=var -- $plugin)_files
set --query files[1] && set --universal $plugin_files_var (string replace -- $source $fisher_path $files | string replace -- ~ \~)
contains -- $plugin $_fisher_plugins || set --universal --append _fisher_plugins $plugin
contains -- $plugin $install_plugins && set --local event install || set --local event update
printf "%s\n" Installing\ (set_color --bold)$plugin(set_color normal) " "$$plugin_files_var | string replace -- \~ ~
for file in (string match --regex -- '.+/[^/]+\.fish$' $$plugin_files_var | string replace -- \~ ~)
source $file
if set --local name (string replace --regex -- '.+conf\.d/([^/]+)\.fish$' '$1' $file)
emit {$name}_$event
end
end
end
command rm -rf $source_plugins
if set --query _fisher_plugins[1]
set --local commit_plugins
for plugin in $file_plugins
contains -- (string lower -- $plugin) (string lower -- $_fisher_plugins) && set --append commit_plugins $plugin
end
for plugin in $_fisher_plugins
contains -- (string lower -- $plugin) (string lower -- $commit_plugins) || set --append commit_plugins $plugin
end
printf "%s\n" $commit_plugins >$fish_plugins
else
set --erase _fisher_plugins
command rm -f $fish_plugins
end
set --local total (count $install_plugins) (count $update_plugins) (count $remove_plugins)
test "$total" != "0 0 0" && echo (string join ", " (
test $total[1] = 0 || echo "Installed $total[1]") (
test $total[2] = 0 || echo "Updated $total[2]") (
test $total[3] = 0 || echo "Removed $total[3]")
) plugin/s
case \*
echo "fisher: Unknown command: \"$cmd\"" >&2 && return 1
end
end
if ! set --query _fisher_upgraded_to_4_4
set --universal _fisher_upgraded_to_4_4
if functions --query _fisher_list
set --query XDG_DATA_HOME[1] || set --local XDG_DATA_HOME ~/.local/share
command rm -rf $XDG_DATA_HOME/fisher
functions --erase _fisher_{list,plugin_parse}
fisher update >/dev/null 2>/dev/null
else
for var in (set --names | string match --entire --regex '^_fisher_.+_files$')
set $var (string replace -- ~ \~ $$var)
end
functions --erase _fisher_fish_postexec
end
end
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2017-2021 Wenxuan Zhang <wenxuangm@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+376
View File
@@ -0,0 +1,376 @@
<h1 align="center">💤 forgit</h1>
<p align="center">
<em>Utility tool for using git interactively. Powered by <a href="https://github.com/junegunn/fzf">junegunn/fzf</a>.</em>
</p>
<p align="center">
<a href="https://github.com/wfxr/forgit/actions">
<img src="https://github.com/wfxr/forgit/workflows/ci/badge.svg"/>
</a>
<a href="https://wfxr.mit-license.org/2017">
<img src="https://img.shields.io/badge/License-MIT-brightgreen.svg"/>
</a>
<a href="https://img.shields.io/badge/Shell-Bash%20%7C%20Zsh%20%7C%20Fish-blue">
<img src="https://img.shields.io/badge/Shell-Bash%20%7C%20Zsh%20%7C%20Fish-blue"/>
</a>
<a href="https://github.com/unixorn/awesome-zsh-plugins">
<img src="https://img.shields.io/badge/Awesome-zsh--plugins-d07cd0?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABVklEQVRIS+3VvWpVURDF8d9CRAJapBAfwWCt+FEJthIUUcEm2NgIYiOxsrCwULCwktjYKSgYLfQF1JjCNvoMNhYRCwOO7HAiVw055yoBizvN3nBmrf8+M7PZsc2RbfY3AfRWeNMSVdUlHEzS1t6oqvt4n+TB78l/AKpqHrdwLcndXndU1WXcw50k10c1PwFV1fa3cQVzSR4PMd/IqaoLeIj2N1eTfG/f1gFVtQMLOI+zSV6NYz4COYFneIGLSdZSVbvwCMdxMsnbvzEfgRzCSyzjXAO8xlHcxMq/mI9oD+AGlhqgxjD93OVOD9TUuICdXd++/VeAVewecKKv2NPlfcHUAM1qK9FTnBmQvJjkdDfWzzE7QPOkAfZiEce2ECzhVJJPHWAfGuTwFpo365pO0NYjmEFr5Eas4SPeJfll2rqb38Z7/yaaD+0eNM3kPejt86REvSX6AamgdXkgoxLxAAAAAElFTkSuQmCC"/>
</a>
<a href="https://github.com/pre-commit/pre-commit">
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white" alt="pre-commit" />
</a>
<a href="https://github.com/wfxr/forgit/graphs/contributors">
<img src="https://img.shields.io/github/contributors/wfxr/forgit" alt="Contributors"/>
</a>
</p>
This tool is designed to help you use git more efficiently.
It's **lightweight** and **easy to use**.
# 📥 Installation
*Make sure you have [`fzf`](https://github.com/junegunn/fzf) installed.*
``` zsh
# for zplug
zplug 'wfxr/forgit'
# for zgen
zgen load 'wfxr/forgit'
# for antigen
antigen bundle 'wfxr/forgit'
# for fisher (requires fisher v4.4.3 or higher)
fisher install wfxr/forgit
# for omf
omf install https://github.com/wfxr/forgit
# for zinit
zinit load wfxr/forgit
# for oh-my-zsh
git clone https://github.com/wfxr/forgit.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/forgit
# manually
# Clone the repository and source it in your shell's rc file or put bin/git-forgit into your $PATH
```
## Homebrew
To install using brew
```sh
brew install forgit
```
Then add the following to your shell's config file:
```sh
# Fish:
# ~/.config/fish/config.fish:
[ -f $HOMEBREW_PREFIX/share/forgit/forgit.plugin.fish ]; and source $HOMEBREW_PREFIX/share/forgit/forgit.plugin.fish
# Zsh:
# ~/.zshrc:
[ -f $HOMEBREW_PREFIX/share/forgit/forgit.plugin.zsh ] && source $HOMEBREW_PREFIX/share/forgit/forgit.plugin.zsh
# Bash:
# ~/.bashrc:
[ -f $HOMEBREW_PREFIX/share/forgit/forgit.plugin.sh ] && source $HOMEBREW_PREFIX/share/forgit/forgit.plugin.sh
```
## Fig
[Fig](https://fig.io) adds apps, shortcuts, and autocomplete to your existing terminal.
Install `forgit` in just one click.
[![Install with Fig](https://fig.io/badges/install-with-fig.svg)](https://fig.io/plugins/other/forgit)
## Arch User Repository
[AUR](https://wiki.archlinux.org/title/Arch_User_Repository) packages, maintained by the developers of forgit, are available. Install the [forgit](https://aur.archlinux.org/packages/forgit) package for the latest release or [forgit-git](https://aur.archlinux.org/packages/forgit-git) to stay up to date with the latest commits from the master branch of this repository.
# 📝 Features
- **Interactive `git add` selector** (`ga`)
![screenshot](https://raw.githubusercontent.com/wfxr/i/master/forgit-ga.png)
- **Interactive `git log` viewer** (`glo`)
![screenshot](https://raw.githubusercontent.com/wfxr/i/master/forgit-glo.png)
*The log graph can be disabled by option `FORGIT_LOG_GRAPH_ENABLE` (see discuss in [issue #71](https://github.com/wfxr/forgit/issues/71)).*
- **Interactive `.gitignore` generator** (`gi`)
![screenshot](https://raw.githubusercontent.com/wfxr/i/master/forgit-gi.png)
- **Interactive `git diff` viewer** (`gd`)
- **Interactive `git reset HEAD <file>` selector** (`grh`)
- **Interactive `git checkout <file>` selector** (`gcf`)
- **Interactive `git checkout <branch>` selector** (`gcb`)
- **Interactive `git branch -D <branch>` selector** (`gbd`)
- **Interactive `git checkout <tag>` selector** (`gct`)
- **Interactive `git checkout <commit>` selector** (`gco`)
- **Interactive `git revert <commit>` selector** (`grc`)
- **Interactive `git stash` viewer** (`gss`)
- **Interactive `git stash push` selector** (`gsp`)
- **Interactive `git clean` selector** (`gclean`)
- **Interactive `git cherry-pick` selector** (`gcp`)
- **Interactive `git rebase -i` selector** (`grb`)
- **Interactive `git blame` selector** (`gbl`)
- **Interactive `git commit --fixup && git rebase -i --autosquash` selector** (`gfu`)
# ⌨ Keybindings
| Key | Action |
| :-------------------------------------------: | ------------------------------------------- |
| <kbd>Enter</kbd> | Confirm |
| <kbd>Tab</kbd> | Toggle mark and move down |
| <kbd>Shift</kbd> - <kbd>Tab</kbd> | Toggle mark and move up |
| <kbd>?</kbd> | Toggle preview window |
| <kbd>Alt</kbd> - <kbd>W</kbd> | Toggle preview wrap |
| <kbd>Ctrl</kbd> - <kbd>S</kbd> | Toggle sort |
| <kbd>Ctrl</kbd> - <kbd>R</kbd> | Toggle selection |
| <kbd>Ctrl</kbd> - <kbd>Y</kbd> | Copy commit hash/stash ID* |
| <kbd>Ctrl</kbd> - <kbd>K</kbd> / <kbd>P</kbd> | Selection move up |
| <kbd>Ctrl</kbd> - <kbd>J</kbd> / <kbd>N</kbd> | Selection move down |
| <kbd>Alt</kbd> - <kbd>K</kbd> / <kbd>P</kbd> | Preview move up |
| <kbd>Alt</kbd> - <kbd>J</kbd> / <kbd>N</kbd> | Preview move down |
| <kbd>Alt</kbd> - <kbd>E</kbd> | Open file in default editor (when possible) |
\* Available when the selection contains a commit hash or a stash ID.
For Linux users `FORGIT_COPY_CMD` should be set to make copy work. Example: `FORGIT_COPY_CMD='xclip -selection clipboard'`.
# ⚙ Options
Options can be set via environment variables. They have to be **exported** in
order to be recognized by `forgit`.
For instance, if you want to order branches in `gcb` by the last committed date you could:
```shell
export FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS='--sort=-committerdate'
```
## shell aliases
You can change the default aliases by defining these variables below.
(To disable all aliases, Set the `FORGIT_NO_ALIASES` flag.)
``` bash
forgit_log=glo
forgit_diff=gd
forgit_add=ga
forgit_reset_head=grh
forgit_ignore=gi
forgit_checkout_file=gcf
forgit_checkout_branch=gcb
forgit_branch_delete=gbd
forgit_checkout_tag=gct
forgit_checkout_commit=gco
forgit_revert_commit=grc
forgit_clean=gclean
forgit_stash_show=gss
forgit_stash_push=gsp
forgit_cherry_pick=gcp
forgit_rebase=grb
forgit_blame=gbl
forgit_fixup=gfu
```
## git integration
You can use forgit as a sub-command of git by making `git-forgit` available in `$PATH`:
```sh
# after `forgit` was loaded
PATH="$PATH:$FORGIT_INSTALL_DIR/bin"
```
*Some plugin managers can help do this.*
Then, any forgit command will be a sub-command of git:
```cmd
git forgit log
git forgit add
git forgit diff
```
Optionally you can add [aliases in git](https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases):
```sh
git config --global alias.cf 'forgit checkout_file'
```
And use forgit functions via a git alias:
```sh
git cf
```
## git options
If you want to customize `git`'s behavior within forgit there is a dedicated variable for each forgit command.
These are passed to the according `git` calls.
| Command | Option |
| -------- | --------------------------------------------------------------------------- |
| `ga` | `FORGIT_ADD_GIT_OPTS` |
| `glo` | `FORGIT_LOG_GIT_OPTS` |
| `gd` | `FORGIT_DIFF_GIT_OPTS` |
| `grh` | `FORGIT_RESET_HEAD_GIT_OPTS` |
| `gcf` | `FORGIT_CHECKOUT_FILE_GIT_OPTS` |
| `gcb` | `FORGIT_CHECKOUT_BRANCH_GIT_OPTS`, `FORGIT_CHECKOUT_BRANCH_BRANCH_GIT_OPTS` |
| `gbd` | `FORGIT_BRANCH_DELETE_GIT_OPTS` |
| `gct` | `FORGIT_CHECKOUT_TAG_GIT_OPTS` |
| `gco` | `FORGIT_CHECKOUT_COMMIT_GIT_OPTS` |
| `grc` | `FORGIT_REVERT_COMMIT_GIT_OPTS` |
| `gss` | `FORGIT_STASH_SHOW_GIT_OPTS` |
| `gsp` | `FORGIT_STASH_PUSH_GIT_OPTS` |
| `gclean` | `FORGIT_CLEAN_GIT_OPTS` |
| `grb` | `FORGIT_REBASE_GIT_OPTS` |
| `gbl` | `FORGIT_BLAME_GIT_OPTS` |
| `gfu` | `FORGIT_FIXUP_GIT_OPTS` |
| `gcp` | `FORGIT_CHERRY_PICK_GIT_OPTS` |
## pagers
Forgit will use the default configured pager from git (`core.pager`,
`pager.show`, `pager.diff`) but can be altered with the following environment
variables:
| Use case | Option | Fallbacks to |
| ------------ | ------------------- | --------------------------------------------- |
| common pager | `FORGIT_PAGER` | `git config core.pager` _or_ `cat` |
| pager on `git show` | `FORGIT_SHOW_PAGER` | `git config pager.show` _or_ `$FORGIT_PAGER` |
| pager on `git diff` | `FORGIT_DIFF_PAGER` | `git config pager.diff` _or_ `$FORGIT_PAGER` |
| pager on `git blame` | `FORGIT_BLAME_PAGER` | `git config pager.blame` _or_ `$FORGIT_PAGER` |
| pager on `gitignore` | `FORGIT_IGNORE_PAGER` | `bat -l gitignore --color always` _or_ `cat` |
| git log format | `FORGIT_GLO_FORMAT` | `%C(auto)%h%d %s %C(black)%C(bold)%cr%reset` |
## fzf options
You can add default fzf options for `forgit`, including keybindings, layout, etc.
(No need to repeat the options already defined in `FZF_DEFAULT_OPTS`)
``` bash
export FORGIT_FZF_DEFAULT_OPTS="
--exact
--border
--cycle
--reverse
--height '80%'
"
```
Customizing fzf options for each command individually is also supported:
| Command | Option |
|----------|-----------------------------------|
| `ga` | `FORGIT_ADD_FZF_OPTS` |
| `glo` | `FORGIT_LOG_FZF_OPTS` |
| `gi` | `FORGIT_IGNORE_FZF_OPTS` |
| `gd` | `FORGIT_DIFF_FZF_OPTS` |
| `grh` | `FORGIT_RESET_HEAD_FZF_OPTS` |
| `gcf` | `FORGIT_CHECKOUT_FILE_FZF_OPTS` |
| `gcb` | `FORGIT_CHECKOUT_BRANCH_FZF_OPTS` |
| `gbd` | `FORGIT_BRANCH_DELETE_FZF_OPTS` |
| `gct` | `FORGIT_CHECKOUT_TAG_FZF_OPTS` |
| `gco` | `FORGIT_CHECKOUT_COMMIT_FZF_OPTS` |
| `grc` | `FORGIT_REVERT_COMMIT_FZF_OPTS` |
| `gss` | `FORGIT_STASH_FZF_OPTS` |
| `gsp` | `FORGIT_STASH_PUSH_FZF_OPTS` |
| `gclean` | `FORGIT_CLEAN_FZF_OPTS` |
| `grb` | `FORGIT_REBASE_FZF_OPTS` |
| `gbl` | `FORGIT_BLAME_FZF_OPTS` |
| `gfu` | `FORGIT_FIXUP_FZF_OPTS` |
| `gcp` | `FORGIT_CHERRY_PICK_FZF_OPTS` |
Complete loading order of fzf options is:
1. `FZF_DEFAULT_OPTS` (fzf global)
2. `FORGIT_FZF_DEFAULT_OPTS` (forgit global)
3. `FORGIT_CMD_FZF_OPTS` (command specific)
Examples:
- `ctrl-d` to drop the selected stash but do not quit fzf (`gss` specific).
```sh
export FORGIT_STASH_FZF_OPTS='
--bind="ctrl-d:reload(git stash drop $(cut -d: -f1 <<<{}) 1>/dev/null && git stash list)"
'
```
- `ctrl-e` to view the logs in a vim buffer (`glo` specific).
```sh
export FORGIT_LOG_FZF_OPTS='
--bind="ctrl-e:execute(echo {} |grep -Eo [a-f0-9]+ |head -1 |xargs git show |vim -)"
'
```
## other options
| Option | Description | Default |
|-----------------------------|-------------------------------------------|-----------------------------------------------|
| `FORGIT_LOG_FORMAT` | git log format | `%C(auto)%h%d %s %C(black)%C(bold)%cr%Creset` |
| `FORGIT_PREVIEW_CONTEXT` | lines of diff context in preview mode | 3 |
| `FORGIT_FULLSCREEN_CONTEXT` | lines of diff context in full-screen mode | 10 |
| `FORGIT_DIR_VIEW` | command used to preview directories | `tree` if available, otherwise `find` |
# 📦 Optional dependencies
- [`delta`](https://github.com/dandavison/delta) / [`diff-so-fancy`](https://github.com/so-fancy/diff-so-fancy): For better human-readable diffs.
- [`bat`](https://github.com/sharkdp/bat.git): Syntax highlighting for `gitignore`.
- [`emoji-cli`](https://github.com/wfxr/emoji-cli): Emoji support for `git log`.
# Completions
## Bash
- Put [`completions/git-forgit.bash`](https://github.com/wfxr/forgit/blob/master/completions/git-forgit.bash) in
`~/.local/share/bash-completion/completions` to have bash tab completion for `git forgit` and configured git aliases.
- Source [`completions/git-forgit.bash`](https://github.com/wfxr/forgit/blob/master/completions/git-forgit.bash) explicitly to have
bash tab completion for forgit shell functions and aliases (e.g., `gcb <tab>` completes branches).
## Zsh
- Put [`completions/_git-forgit`](completions/_git-forgit) in a directory in your `$fpath` (e.g., `/usr/share/zsh/site-functions`) to have zsh tab completion for `git forgit` and configured git aliases, as well as shell command aliases, such as `forgit::add` and `ga`
If you're having issues after updating, and commands such as `forgit::add` or aliases `ga` aren't working, remove your completions cache and restart your shell.
```zsh
> rm ~/.zcompdump
> zsh
```
# 💡 Tips
- Most of the commands accept optional arguments (e.g., `glo develop`, `glo f738479..188a849b -- main.go`, `gco master`).
- `gd` supports specifying revision(e.g., `gd HEAD~`, `gd v1.0 README.md`).
- Call `gi` with arguments to get the wanted `.gitignore` contents directly(e.g., `gi cmake c++`).
# 📃 License
[MIT](https://wfxr.mit-license.org/2017) (c) Wenxuan Zhang
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,59 @@
#
# forgit completions for fish plugin
#
# Place this file inside your <fish_config_dir>/completions/ directory.
# It's usually located at ~/.config/fish/completions/. The file is lazily
# sourced when git-forgit command or forgit subcommand of git is invoked.
function __fish_forgit_needs_subcommand
for subcmd in add blame branch_delete checkout_branch checkout_commit checkout_file checkout_tag \
cherry_pick cherry_pick_from_branch clean diff fixup ignore log rebase reset_head revert_commit \
stash_show stash_push
if contains -- $subcmd (commandline -opc)
return 1
end
end
return 0
end
# Load helper functions in git completion file
not functions -q __fish_git && source $__fish_data_dir/completions/git.fish
# No file completion by default
complete -c git-forgit -x
complete -c git-forgit -n __fish_forgit_needs_subcommand -a add -d 'git add selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a blame -d 'git blame viewer'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a branch_delete -d 'git branch deletion selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a checkout_branch -d 'git checkout branch selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a checkout_commit -d 'git checkout commit selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a checkout_file -d 'git checkout-file selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a checkout_tag -d 'git checkout tag selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a cherry_pick -d 'git cherry-picking'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a cherry_pick_from_branch -d 'git cherry-picking with interactive branch selection'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a clean -d 'git clean selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a diff -d 'git diff viewer'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a fixup -d 'git fixup'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a ignore -d 'git ignore generator'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a log -d 'git commit viewer'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a rebase -d 'git rebase'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a reset_head -d 'git reset HEAD (unstage) selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a revert_commit -d 'git revert commit selector'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_show -d 'git stash viewer'
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_push -d 'git stash push selector'
complete -c git-forgit -n '__fish_seen_subcommand_from add' -a "(complete -C 'git add ')"
complete -c git-forgit -n '__fish_seen_subcommand_from branch_delete' -a "(__fish_git_local_branches)"
complete -c git-forgit -n '__fish_seen_subcommand_from checkout_branch' -a "(complete -C 'git switch ')"
complete -c git-forgit -n '__fish_seen_subcommand_from checkout_commit' -a "(__fish_git_commits)"
complete -c git-forgit -n '__fish_seen_subcommand_from checkout_file' -a "(__fish_git_files modified)"
complete -c git-forgit -n '__fish_seen_subcommand_from checkout_tag' -a "(__fish_git_tags)" -d Tag
complete -c git-forgit -n '__fish_seen_subcommand_from cherry_pick' -a "(complete -C 'git cherry-pick ')"
complete -c git-forgit -n '__fish_seen_subcommand_from clean' -a "(__fish_git_files untracked ignored)"
complete -c git-forgit -n '__fish_seen_subcommand_from fixup' -a "(__fish_git_local_branches)"
complete -c git-forgit -n '__fish_seen_subcommand_from log' -a "(complete -C 'git log ')"
complete -c git-forgit -n '__fish_seen_subcommand_from rebase' -a "(complete -C 'git rebase ')"
complete -c git-forgit -n '__fish_seen_subcommand_from reset_head' -a "(__fish_git_files all-staged)"
complete -c git-forgit -n '__fish_seen_subcommand_from revert_commit' -a "(__fish_git_commits)"
complete -c git-forgit -n '__fish_seen_subcommand_from stash_show' -a "(__fish_git_complete_stashes)"
complete -c git-forgit -n '__fish_seen_subcommand_from stash_push' -a "(__fish_git_files modified deleted modified-staged-deleted)"
@@ -0,0 +1,53 @@
# MIT (c) Chris Apple
set -l install_dir (dirname (status dirname))
set -x FORGIT_INSTALL_DIR "$install_dir/conf.d"
set -x FORGIT "$FORGIT_INSTALL_DIR/bin/git-forgit"
if not test -e "$FORGIT"
set -x FORGIT_INSTALL_DIR "$install_dir/vendor_conf.d"
set -x FORGIT "$FORGIT_INSTALL_DIR/bin/git-forgit"
end
function forgit::warn
printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$argv" >&2
end
# backwards compatibility:
# export all user-defined FORGIT variables to make them available in git-forgit
set unexported_vars 0
set | awk -F ' ' '{ print $1 }' | grep FORGIT_ | while read var
if not set -x | grep -q "^$var\b"
if test $unexported_vars = 0
forgit::warn "Config options have to be exported in future versions of forgit."
forgit::warn "Please update your config accordingly:"
end
forgit::warn " set -x $var \"$$var\""
set unexported_vars (math $unexported_vars + 1)
set -x $var $$var
end
end
# alias `git-forgit` to the full-path of the command
alias git-forgit "$FORGIT"
# register abbreviations
if test -z "$FORGIT_NO_ALIASES"
abbr -a -- (string collect $forgit_add; or string collect "ga") git-forgit add
abbr -a -- (string collect $forgit_reset_head; or string collect "grh") git-forgit reset_head
abbr -a -- (string collect $forgit_log; or string collect "glo") git-forgit log
abbr -a -- (string collect $forgit_diff; or string collect "gd") git-forgit diff
abbr -a -- (string collect $forgit_ignore; or string collect "gi") git-forgit ignore
abbr -a -- (string collect $forgit_checkout_file; or string collect "gcf") git-forgit checkout_file
abbr -a -- (string collect $forgit_checkout_branch; or string collect "gcb") git-forgit checkout_branch
abbr -a -- (string collect $forgit_branch_delete; or string collect "gbd") git-forgit branch_delete
abbr -a -- (string collect $forgit_clean; or string collect "gclean") git-forgit clean
abbr -a -- (string collect $forgit_stash_show; or string collect "gss") git-forgit stash_show
abbr -a -- (string collect $forgit_stash_push; or string collect "gsp") git-forgit stash_push
abbr -a -- (string collect $forgit_cherry_pick; or string collect "gcp") git-forgit cherry_pick_from_branch
abbr -a -- (string collect $forgit_rebase; or string collect "grb") git-forgit rebase
abbr -a -- (string collect $forgit_fixup; or string collect "gfu") git-forgit fixup
abbr -a -- (string collect $forgit_checkout_commit; or string collect "gco") git-forgit checkout_commit
abbr -a -- (string collect $forgit_revert_commit; or string collect "grc") git-forgit revert_commit
abbr -a -- (string collect $forgit_blame; or string collect "gbl") git-forgit blame
abbr -a -- (string collect $forgit_checkout_tag; or string collect "gct") git-forgit checkout_tag
end
+21
View File
@@ -0,0 +1,21 @@
# MIT License
Copyright © `2020` `Patrick`
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+220
View File
@@ -0,0 +1,220 @@
<div align="center">
# fzf.fish 🔍🐟
[![latest release badge][]](https://github.com/patrickf1/fzf.fish/releases)
[![build status badge][]](https://github.com/patrickf1/fzf.fish/actions)
[![awesome badge][]](https://git.io/awsm.fish)
</div>
Augment your [Fish][] command line with mnemonic key bindings to efficiently find what you need using [fzf][].
https://user-images.githubusercontent.com/1967248/197308919-51d04602-2d5f-46aa-a96e-6cf1617e3067.mov
## Search commands
Use `fzf.fish` to interactively find and insert file paths, git commit hashes, and other entities into your command line. <kbd>Tab</kbd> to select multiple entries. If you trigger a search while your cursor is on a word, that word will be used to seed the fzf query and will be replaced by your selection. All searches include a preview of the entity hovered over to help you find what you're looking for.
### 📁 Search Directory
![Search Directory example](../assets/directory.png)
- **Fzf input:** recursive listing of current directory's non-hidden files
- **Output:** relative paths of selected files
- **Key binding and mnemonic:** <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F</kbd> (`F` for file)
- **Preview window:** file with syntax highlighting, directory contents, or file type
- **Remarks**
- directories are inserted with a trailing `/`, so if you select exactly one directory, you can immediately hit <kbd>ENTER</kbd> to [cd into it][cd docs]
- if the current token is a directory with a trailing slash (e.g. `.config/<CURSOR>`), then that directory is searched instead
- [ignores files that are also ignored by git](#fd-gi)
### 🪵 Search Git Log
![Search Git Log example](../assets/git_log.png)
- **Fzf input:** the current repository's formatted `git log`
- **Output:** hashes of selected commits
- **Key binding and mnemonic:** <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>L</kbd> (`L` for log)
- **Preview window:** commit message and diff
### 📝 Search Git Status
![Search Git Status example](../assets/git_status.png)
- **Fzf input:** the current repository's `git status`
- **Output:** relative paths of selected lines
- **Key binding and mnemonic:** <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>S</kbd> (`S` for status)
- **Preview window:** the git diff of the file
### 📜 Search History
![Search History example](../assets/history.png)
- **Fzf input:** Fish's command history
- **Output:** selected commands
- **Key binding and mnemonic:** <kbd>Ctrl</kbd>+<kbd>R</kbd> (`R` for reverse-i-search)
- **Preview window:** the entire command with Fish syntax highlighting
### 🖥️ Search Processes
![Search Processes example](../assets/processes.png)
- **Fzf input:** the pid and command of all running processes, outputted by `ps`
- **Output:** pids of selected processes
- **Key binding and mnemonic:** <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>P</kbd> (`P` for process)
- **Preview window:** the CPU usage, memory usage, start time, and other information about the process
### 💲 Search Variables
![Search Variables example](../assets/variables.png)
- **Fzf input:** all the shell variables currently [in scope][var scope]
- **Output:** selected shell variables
- **Key binding and mnemonic:** <kbd>Ctrl</kbd>+<kbd>V</kbd> (`V` for variable)
- **Preview window:** the variable's scope info and values
- `$history` is excluded for technical reasons so use [Search History](#-search-history) instead to inspect it
## Installation
First, install a proper version of these CLI dependencies:
| CLI | Minimum version required | Description |
| -------- | ------------------------ | --------------------------------------- |
| [fish][] | 3.4.0 | a modern shell |
| [fzf][] | 0.33.0 | fuzzy finder that powers this plugin |
| [fd][] | 8.5.0 | faster, colorized alternative to `find` |
| [bat][] | 0.16.0 | smarter `cat` with syntax highlighting |
[fd][] and [bat][] only need to be installed if you will use [Search Directory][].
Next, because `fzf.fish` is incompatible with other fzf plugins, [check for and remove these two common alternatives](https://github.com/PatrickF1/fzf.fish/wiki/Uninstalling-other-fzf-plugins).
Finally, install this plugin with [Fisher][].
> `fzf.fish` can be installed manually or with other plugin managers but only Fisher is officially supported.
```fish
fisher install PatrickF1/fzf.fish
```
## Configuration
### Customize key bindings
`fzf.fish` includes an ergonomic function for configuring its key bindings. Read [its documentation](/functions/_fzf_configure_bindings_help.fish):
```fish
fzf_configure_bindings --help
```
Call `fzf_configure_bindings` in your `config.fish` in order to persist your custom bindings.
### Change fzf options for all commands
fzf supports global default options via the [FZF_DEFAULT_OPTS and FZF_DEFAULT_OPTS_FILE](https://github.com/junegunn/fzf#environment-variables) environment variables. If neither are set, `fzf.fish` sets its own [default opts whenever it executes fzf](functions/_fzf_wrapper.fish).
### Change fzf options for a specific command
Each command's fzf options can be configured via a variable:
| Command | Variable |
| ----------------- | --------------------- |
| Search Directory | `fzf_directory_opts` |
| Search Git Log | `fzf_git_log_opts` |
| Search Git Status | `fzf_git_status_opts` |
| Search History | `fzf_history_opts` |
| Search Processes | `fzf_processes_opts` |
| Search Variables | `fzf_variables_opts` |
The value of each variable is appended last to fzf's options list. Because fzf uses the last instance of an option if it is specified multiple times, custom options take precedence. Custom fzf options unlock a variety of augmentations:
- add [fzf key bindings](https://www.mankier.com/1/fzf#Key/Event_Bindings) to [open files in Vim](https://github.com/PatrickF1/fzf.fish/pull/273)
- adjust the preview command or window
- [re-populate fzf's input list on demand](https://github.com/junegunn/fzf/issues/1750)
- change the [search mode](https://github.com/junegunn/fzf#search-syntax)
Find more ideas and tips in the [Cookbook](https://github.com/PatrickF1/fzf.fish/wiki/Cookbook).
### Change how Search Directory previews directories and regular files
[Search Directory][], by default, executes `ls` to preview directories and `bat` to preview [regular files](https://stackoverflow.com/questions/6858452).
To use your own directory preview command, set it in `fzf_preview_dir_cmd`:
```fish
set fzf_preview_dir_cmd eza --all --color=always
```
And to use your own file preview command, set it in `fzf_preview_file_cmd`:
```fish
set fzf_preview_file_cmd cat -n
```
Omit the target path for both variables as `fzf.fish` will itself [specify the argument to preview](functions/_fzf_preview_file.fish).
### Change what files are listed by Search Directory
To pass custom options to `fd` when [Search Directory][] executes it to populate the list of files, set them in `fzf_fd_opts`:
```fish
set fzf_fd_opts --hidden --max-depth 5
```
<a id='fd-gi'></a>By default, `fd` hides files listed in `.gitignore`. You can disable this behavior by adding the `--no-ignore` flag to `fzf_fd_opts`.
### Change Search Git Log's commit formatting
[Search Git Log][] executes `git log --format` to format the list of commits. To use your own [commit log format](https://git-scm.com/docs/git-log#Documentation/git-log.txt-emnem), set it in `fzf_git_log_format`. For example, this shows the hash and subject for each commit:
```fish
set fzf_git_log_format "%H %s"
```
The format must be one line per commit and the hash must be the first field, or else Search Git Log will fail to determine which commits you selected.
### Integrate with a diff highlighter
To pipe the git diff previews from [Search Git Log][] and [Search Git Status][] through a highlighter tool (e.g. [delta](https://github.com/dandavison/delta) or [diff-so-fancy](https://github.com/so-fancy/diff-so-fancy)), set a command invoking the highlighter in `fzf_diff_highlighter`. It should not pipe its output to a pager:
```fish
# width=20 so delta decorations don't wrap around small fzf preview pane
set fzf_diff_highlighter delta --paging=never --width=20
# Or, if using DFS
set fzf_diff_highlighter diff-so-fancy
```
### Change Search History's date time format
[Search History][] shows the date time each command was executed. To change how its formatted, set your [strftime format string](https://devhints.io/strftime) in `fzf_history_time_format`. For example, this shows the date time as DD-MM-YY:
```fish
set fzf_history_time_format %d-%m-%y
```
Do not to include the vertical box-drawing character `│` (not to be confused with the pipe character `|`) as it is relied on to delineate the date time from the command.
## Further reading
Find answers to these questions and more in the [project Wiki](https://github.com/PatrickF1/fzf.fish/wiki):
- How does `fzf.fish` [compare](https://github.com/PatrickF1/fzf.fish/wiki/Prior-Art) to other popular fzf plugins for Fish?
- Why isn't this [command working](https://github.com/PatrickF1/fzf.fish/wiki/Troubleshooting)?
- How can I [customize](https://github.com/PatrickF1/fzf.fish/wiki/Cookbook) this command?
- How can I [contribute](https://github.com/PatrickF1/fzf.fish/wiki/Contributing) to this plugin?
[awesome badge]: https://awesome.re/mentioned-badge.svg
[bat]: https://github.com/sharkdp/bat
[build status badge]: https://img.shields.io/github/actions/workflow/status/PatrickF1/fzf.fish/continuous_integration.yml?branch=main
[cd docs]: https://fishshell.com/docs/current/cmds/cd.html
[fd]: https://github.com/sharkdp/fd
[fish]: https://fishshell.com
[fisher]: https://github.com/jorgebucaran/fisher
[fzf]: https://github.com/junegunn/fzf
[latest release badge]: https://img.shields.io/github/v/release/patrickf1/fzf.fish
[search directory]: #-search-directory
[search git log]: #-search-git-log
[search git status]: #-search-git-status
[search history]: #-search-history
[var scope]: https://fishshell.com/docs/current/#variable-scope
@@ -0,0 +1,8 @@
complete fzf_configure_bindings --no-files
complete fzf_configure_bindings --long help --short h --description "Print help" --condition "not __fish_seen_argument --help -h"
complete fzf_configure_bindings --long directory --description "Change the key binding for Search Directory" --condition "not __fish_seen_argument --directory"
complete fzf_configure_bindings --long git_log --description "Change the key binding for Search Git Log" --condition "not __fish_seen_argument --git_log"
complete fzf_configure_bindings --long git_status --description "Change the key binding for Search Git Status" --condition "not __fish_seen_argument --git_status"
complete fzf_configure_bindings --long history --description "Change the key binding for Search History" --condition "not __fish_seen_argument --history"
complete fzf_configure_bindings --long processes --description "Change the key binding for Search Processes" --condition "not __fish_seen_argument --processes"
complete fzf_configure_bindings --long variables --description "Change the key binding for Search Variables" --condition "not __fish_seen_argument --variables"
+28
View File
@@ -0,0 +1,28 @@
# fzf.fish is only meant to be used in interactive mode. If not in interactive mode and not in CI, skip the config to speed up shell startup
if not status is-interactive && test "$CI" != true
exit
end
# Because of scoping rules, to capture the shell variables exactly as they are, we must read
# them before even executing _fzf_search_variables. We use psub to store the
# variables' info in temporary files and pass in the filenames as arguments.
# This variable is global so that it can be referenced by fzf_configure_bindings and in tests
set --global _fzf_search_vars_command '_fzf_search_variables (set --show | psub) (set --names | psub)'
# Install the default bindings, which are mnemonic and minimally conflict with fish's preset bindings
fzf_configure_bindings
# Doesn't erase autoloaded _fzf_* functions because they are not easily accessible once key bindings are erased
function _fzf_uninstall --on-event fzf_uninstall
_fzf_uninstall_bindings
set --erase _fzf_search_vars_command
functions --erase _fzf_uninstall _fzf_migration_message _fzf_uninstall_bindings fzf_configure_bindings
complete --erase fzf_configure_bindings
set_color cyan
echo "fzf.fish uninstalled."
echo "You may need to manually remove fzf_configure_bindings from your config.fish if you were using custom key bindings."
set_color normal
end
@@ -0,0 +1,43 @@
function _fzf_configure_bindings_help --description "Prints the help message for fzf_configure_bindings."
echo "\
USAGE:
fzf_configure_bindings [--COMMAND=[KEY_SEQUENCE]...]
DESCRIPTION
fzf_configure_bindings installs key bindings for fzf.fish's commands and erases any bindings it
previously installed. It installs bindings for both default and insert modes. fzf.fish executes
it without options on fish startup to install the out-of-the-box key bindings.
By default, commands are bound to a mnemonic key sequence, shown below. Each command's binding
can be configured using a namesake corresponding option:
COMMAND | DEFAULT KEY SEQUENCE | CORRESPONDING OPTION
Search Directory | Ctrl+Alt+F (F for file) | --directory
Search Git Log | Ctrl+Alt+L (L for log) | --git_log
Search Git Status | Ctrl+Alt+S (S for status) | --git_status
Search History | Ctrl+R (R for reverse) | --history
Search Processes | Ctrl+Alt+P (P for process) | --processes
Search Variables | Ctrl+V (V for variable) | --variables
Override a command's binding by specifying its corresponding option with the desired key
sequence. Disable a command's binding by specifying its corresponding option with no value.
Because fzf_configure_bindings erases bindings it previously installed, it can be cleanly
executed multiple times. Once the desired fzf_configure_bindings command has been found, add it
to your config.fish in order to persist the customized bindings.
In terms of validation, fzf_configure_bindings fails if passed unknown options. It expects an
equals sign between an option's name and value. However, it does not validate key sequences.
Pass -h or --help to print this help message and exit.
EXAMPLES
Default bindings but bind Search Directory to Ctrl+F and Search Variables to Ctrl+Alt+V
\$ fzf_configure_bindings --directory=\cf --variables=\e\cv
Default bindings but disable Search History
\$ fzf_configure_bindings --history=
An agglomeration of different options
\$ fzf_configure_bindings --git_status=\cg --history=\ch --variables= --processes=
SEE Also
To learn more about fish key bindings, see bind(1) and fish_key_reader(1).
"
end
@@ -0,0 +1,15 @@
# helper function for _fzf_search_variables
function _fzf_extract_var_info --argument-names variable_name set_show_output --description "Extract and reformat lines pertaining to \$variable_name from \$set_show_output."
# Extract only the lines about the variable, all of which begin with either
# $variable_name: ...or... $variable_name[
string match --regex "^\\\$$variable_name(?::|\[).*" <$set_show_output |
# Strip the variable name prefix, including ": " for scope info lines
string replace --regex "^\\\$$variable_name(?:: )?" '' |
# Distill the lines of values, replacing...
# [1]: |value|
# ...with...
# [1] value
string replace --regex ": \|(.*)\|" ' $1'
end
@@ -0,0 +1,49 @@
# helper for _fzf_search_git_status
# arg should be a line from git status --short, e.g.
# MM functions/_fzf_preview_changed_file.fish
# D README.md
# R LICENSE -> "New License"
function _fzf_preview_changed_file --argument-names path_status --description "Show the git diff of the given file."
# remove quotes because they'll be interpreted literally by git diff
# no need to requote when referencing $path because fish does not perform word splitting
# https://fishshell.com/docs/current/fish_for_bash_users.html
set -f path (string unescape (string sub --start 4 $path_status))
# first letter of short format shows index, second letter shows working tree
# https://git-scm.com/docs/git-status/2.35.0#_short_format
set -f index_status (string sub --length 1 $path_status)
set -f working_tree_status (string sub --start 2 --length 1 $path_status)
set -f diff_opts --color=always
if test $index_status = '?'
_fzf_report_diff_type Untracked
_fzf_preview_file $path
else if contains {$index_status}$working_tree_status DD AU UD UA DU AA UU
# Unmerged statuses taken directly from git status help's short format table
# Unmerged statuses are mutually exclusive with other statuses, so if we see
# these, then safe to assume the path is unmerged
_fzf_report_diff_type Unmerged
git diff $diff_opts -- $path
else
if test $index_status != ' '
_fzf_report_diff_type Staged
# renames are only detected in the index, never working tree, so only need to test for it here
# https://stackoverflow.com/questions/73954214
if test $index_status = R
# diff the post-rename path with the original path, otherwise the diff will show the entire file as being added
set -f orig_and_new_path (string split --max 1 -- ' -> ' $path)
git diff --staged $diff_opts -- $orig_and_new_path[1] $orig_and_new_path[2]
# path currently has the form of "original -> current", so we need to correct it before it's used below
set path $orig_and_new_path[2]
else
git diff --staged $diff_opts -- $path
end
end
if test $working_tree_status != ' '
_fzf_report_diff_type Unstaged
git diff $diff_opts -- $path
end
end
end
@@ -0,0 +1,43 @@
# helper function for _fzf_search_directory and _fzf_search_git_status
function _fzf_preview_file --description "Print a preview for the given file based on its file type."
# because there's no way to guarantee that _fzf_search_directory passes the path to _fzf_preview_file
# as one argument, we collect all the arguments into one single variable and treat that as the path
set -f file_path $argv
if test -L "$file_path" # symlink
# notify user and recurse on the target of the symlink, which can be any of these file types
set -l target_path (realpath "$file_path")
set_color yellow
echo "'$file_path' is a symlink to '$target_path'."
set_color normal
_fzf_preview_file "$target_path"
else if test -f "$file_path" # regular file
if set --query fzf_preview_file_cmd
# need to escape quotes to make sure eval receives file_path as a single arg
eval "$fzf_preview_file_cmd '$file_path'"
else
bat --style=numbers --color=always "$file_path"
end
else if test -d "$file_path" # directory
if set --query fzf_preview_dir_cmd
# see above
eval "$fzf_preview_dir_cmd '$file_path'"
else
# -A list hidden files as well, except for . and ..
# -F helps classify files by appending symbols after the file name
command ls -A -F "$file_path"
end
else if test -c "$file_path"
_fzf_report_file_type "$file_path" "character device file"
else if test -b "$file_path"
_fzf_report_file_type "$file_path" "block device file"
else if test -S "$file_path"
_fzf_report_file_type "$file_path" socket
else if test -p "$file_path"
_fzf_report_file_type "$file_path" "named pipe"
else
echo "$file_path doesn't exist." >&2
end
end
@@ -0,0 +1,18 @@
# helper for _fzf_preview_changed_file
# prints out something like
# ╭────────╮
# │ Staged │
# ╰────────╯
function _fzf_report_diff_type --argument-names diff_type --description "Print a distinct colored header meant to preface a git patch."
# number of "-" to draw is the length of the string to box + 2 for padding
set -f repeat_count (math 2 + (string length $diff_type))
set -f line (string repeat --count $repeat_count)
set -f top_border$line
set -f btm_border$line
set_color yellow
echo $top_border
echo "$diff_type"
echo $btm_border
set_color normal
end
@@ -0,0 +1,6 @@
# helper function for _fzf_preview_file
function _fzf_report_file_type --argument-names file_path file_type --description "Explain the file type for a file."
set_color red
echo "Cannot preview '$file_path': it is a $file_type."
set_color normal
end
@@ -0,0 +1,33 @@
function _fzf_search_directory --description "Search the current directory. Replace the current token with the selected file paths."
# Directly use fd binary to avoid output buffering delay caused by a fd alias, if any.
# Debian-based distros install fd as fdfind and the fd package is something else, so
# check for fdfind first. Fall back to "fd" for a clear error message.
set -f fd_cmd (command -v fdfind || command -v fd || echo "fd")
set -f --append fd_cmd --color=always $fzf_fd_opts
set -f fzf_arguments --multi --ansi $fzf_directory_opts
set -f token (commandline --current-token)
# expand any variables or leading tilde (~) in the token
set -f expanded_token (eval echo -- $token)
# unescape token because it's already quoted so backslashes will mess up the path
set -f unescaped_exp_token (string unescape -- $expanded_token)
# If the current token is a directory and has a trailing slash,
# then use it as fd's base directory.
if string match --quiet -- "*/" $unescaped_exp_token && test -d "$unescaped_exp_token"
set --append fd_cmd --base-directory=$unescaped_exp_token
# use the directory name as fzf's prompt to indicate the search is limited to that directory
set --prepend fzf_arguments --prompt="Directory $unescaped_exp_token> " --preview="_fzf_preview_file $expanded_token{}"
set -f file_paths_selected $unescaped_exp_token($fd_cmd 2>/dev/null | _fzf_wrapper $fzf_arguments)
else
set --prepend fzf_arguments --prompt="Directory> " --query="$unescaped_exp_token" --preview='_fzf_preview_file {}'
set -f file_paths_selected ($fd_cmd 2>/dev/null | _fzf_wrapper $fzf_arguments)
end
if test $status -eq 0
commandline --current-token --replace -- (string escape -- $file_paths_selected | string join ' ')
end
commandline --function repaint
end
@@ -0,0 +1,36 @@
function _fzf_search_git_log --description "Search the output of git log and preview commits. Replace the current token with the selected commit hash."
if not git rev-parse --git-dir >/dev/null 2>&1
echo '_fzf_search_git_log: Not in a git repository.' >&2
else
if not set --query fzf_git_log_format
# %h gives you the abbreviated commit hash, which is useful for saving screen space, but we will have to expand it later below
set -f fzf_git_log_format '%C(bold blue)%h%C(reset) - %C(cyan)%ad%C(reset) %C(yellow)%d%C(reset) %C(normal)%s%C(reset) %C(dim normal)[%an]%C(reset)'
end
set -f preview_cmd 'git show --color=always --stat --patch {1}'
if set --query fzf_diff_highlighter
set preview_cmd "$preview_cmd | $fzf_diff_highlighter"
end
set -f selected_log_lines (
git log --no-show-signature --color=always --format=format:$fzf_git_log_format --date=short | \
_fzf_wrapper --ansi \
--multi \
--scheme=history \
--prompt="Git Log> " \
--preview=$preview_cmd \
--query=(commandline --current-token) \
$fzf_git_log_opts
)
if test $status -eq 0
for line in $selected_log_lines
set -f abbreviated_commit_hash (string split --field 1 " " $line)
set -f full_commit_hash (git rev-parse $abbreviated_commit_hash)
set -f --append commit_hashes $full_commit_hash
end
commandline --current-token --replace (string join ' ' $commit_hashes)
end
end
commandline --function repaint
end
@@ -0,0 +1,41 @@
function _fzf_search_git_status --description "Search the output of git status. Replace the current token with the selected file paths."
if not git rev-parse --git-dir >/dev/null 2>&1
echo '_fzf_search_git_status: Not in a git repository.' >&2
else
set -f preview_cmd '_fzf_preview_changed_file {}'
if set --query fzf_diff_highlighter
set preview_cmd "$preview_cmd | $fzf_diff_highlighter"
end
set -f selected_paths (
# Pass configuration color.status=always to force status to use colors even though output is sent to a pipe
git -c color.status=always status --short |
_fzf_wrapper --ansi \
--multi \
--prompt="Git Status> " \
--query=(commandline --current-token) \
--preview=$preview_cmd \
--nth="2.." \
$fzf_git_status_opts
)
if test $status -eq 0
# git status --short automatically escapes the paths of most files for us so not going to bother trying to handle
# the few edges cases of weird file names that should be extremely rare (e.g. "this;needs;escaping")
set -f cleaned_paths
for path in $selected_paths
if test (string sub --length 1 $path) = R
# path has been renamed and looks like "R LICENSE -> LICENSE.md"
# extract the path to use from after the arrow
set --append cleaned_paths (string split -- "-> " $path)[-1]
else
set --append cleaned_paths (string sub --start=4 $path)
end
end
commandline --current-token --replace -- (string join ' ' $cleaned_paths)
end
end
commandline --function repaint
end
@@ -0,0 +1,39 @@
function _fzf_search_history --description "Search command history. Replace the command line with the selected command."
# history merge incorporates history changes from other fish sessions
# it errors out if called in private mode
if test -z "$fish_private_mode"
builtin history merge
end
if not set --query fzf_history_time_format
# Reference https://devhints.io/strftime to understand strftime format symbols
set -f fzf_history_time_format "%m-%d %H:%M:%S"
end
# Delinate time from command in history entries using the vertical box drawing char (U+2502).
# Then, to get raw command from history entries, delete everything up to it. The ? on regex is
# necessary to make regex non-greedy so it won't match into commands containing the char.
set -f time_prefix_regex '^.*? │ '
# Delinate commands throughout pipeline using null rather than newlines because commands can be multi-line
set -f commands_selected (
builtin history --null --show-time="$fzf_history_time_format" |
_fzf_wrapper --read0 \
--print0 \
--multi \
--scheme=history \
--prompt="History> " \
--query=(commandline) \
--preview="string replace --regex '$time_prefix_regex' '' -- {} | fish_indent --ansi" \
--preview-window="bottom:3:wrap" \
$fzf_history_opts |
string split0 |
# remove timestamps from commands selected
string replace --regex $time_prefix_regex ''
)
if test $status -eq 0
commandline --replace -- $commands_selected
end
commandline --function repaint
end
@@ -0,0 +1,32 @@
function _fzf_search_processes --description "Search all running processes. Replace the current token with the pid of the selected process."
# Directly use ps command because it is often aliased to a different command entirely
# or with options that dirty the search results and preview output
set -f ps_cmd (command -v ps || echo "ps")
# use all caps to be consistent with ps default format
# snake_case because ps doesn't seem to allow spaces in the field names
set -f ps_preview_fmt (string join ',' 'pid' 'ppid=PARENT' 'user' '%cpu' 'rss=RSS_IN_KB' 'start=START_TIME' 'command')
set -f processes_selected (
$ps_cmd -A -opid,command | \
_fzf_wrapper --multi \
--prompt="Processes> " \
--query (commandline --current-token) \
--ansi \
# first line outputted by ps is a header, so we need to mark it as so
--header-lines=1 \
# ps uses exit code 1 if the process was not found, in which case show an message explaining so
--preview="$ps_cmd -o '$ps_preview_fmt' -p {1} || echo 'Cannot preview {1} because it exited.'" \
--preview-window="bottom:4:wrap" \
$fzf_processes_opts
)
if test $status -eq 0
for process in $processes_selected
set -f --append pids_selected (string split --no-empty --field=1 -- " " $process)
end
# string join to replace the newlines outputted by string split with spaces
commandline --current-token --replace -- (string join ' ' $pids_selected)
end
commandline --function repaint
end
@@ -0,0 +1,47 @@
# This function expects the following two arguments:
# argument 1 = output of (set --show | psub), i.e. a file with the scope info and values of all variables
# argument 2 = output of (set --names | psub), i.e. a file with all variable names
function _fzf_search_variables --argument-names set_show_output set_names_output --description "Search and preview shell variables. Replace the current token with the selected variable."
if test -z "$set_names_output"
printf '%s\n' '_fzf_search_variables requires 2 arguments.' >&2
commandline --function repaint
return 22 # 22 means invalid argument in POSIX
end
# Exclude the history variable from being piped into fzf because
# 1. it's not included in $set_names_output
# 2. it tends to be a very large value => increases computation time
# 3._fzf_search_history is a much better way to examine history anyway
set -f all_variable_names (string match --invert history <$set_names_output)
set -f current_token (commandline --current-token)
# Use the current token to pre-populate fzf's query. If the current token begins
# with a $, remove it from the query so that it will better match the variable names
set -f cleaned_curr_token (string replace -- '$' '' $current_token)
set -f variable_names_selected (
printf '%s\n' $all_variable_names |
_fzf_wrapper --preview "_fzf_extract_var_info {} $set_show_output" \
--prompt="Variables> " \
--preview-window="wrap" \
--multi \
--query=$cleaned_curr_token \
$fzf_variables_opts
)
if test $status -eq 0
# If the current token begins with a $, do not overwrite the $ when
# replacing the current token with the selected variable.
# Uses brace expansion to prepend $ to each variable name.
commandline --current-token --replace (
if string match --quiet -- '$*' $current_token
string join " " \${$variable_names_selected}
else
string join " " $variable_names_selected
end
)
end
commandline --function repaint
end
@@ -0,0 +1,21 @@
function _fzf_wrapper --description "Prepares some environment variables before executing fzf."
# Make sure fzf uses fish to execute preview commands, some of which
# are autoloaded fish functions so don't exist in other shells.
# Use --function so that it doesn't clobber SHELL outside this function.
set -f --export SHELL (command --search fish)
# If neither FZF_DEFAULT_OPTS nor FZF_DEFAULT_OPTS_FILE are set, then set some sane defaults.
# See https://github.com/junegunn/fzf#environment-variables
set --query FZF_DEFAULT_OPTS FZF_DEFAULT_OPTS_FILE
if test $status -eq 2
# cycle allows jumping between the first and last results, making scrolling faster
# layout=reverse lists results top to bottom, mimicking the familiar layouts of git log, history, and env
# border shows where the fzf window begins and ends
# height=90% leaves space to see the current command and some scrollback, maintaining context of work
# preview-window=wrap wraps long lines in the preview window, making reading easier
# marker=* makes the multi-select marker more distinguishable from the pointer (since both default to >)
set --export FZF_DEFAULT_OPTS '--cycle --layout=reverse --border --height=90% --preview-window=wrap --marker="*"'
end
fzf $argv
end
@@ -0,0 +1,46 @@
# Always installs bindings for insert and default mode for simplicity and b/c it has almost no side-effect
# https://gitter.im/fish-shell/fish-shell?at=60a55915ee77a74d685fa6b1
function fzf_configure_bindings --description "Installs the default key bindings for fzf.fish with user overrides passed as options."
# no need to install bindings if not in interactive mode or running tests
status is-interactive || test "$CI" = true; or return
set -f options_spec h/help 'directory=?' 'git_log=?' 'git_status=?' 'history=?' 'processes=?' 'variables=?'
argparse --max-args=0 --ignore-unknown $options_spec -- $argv 2>/dev/null
if test $status -ne 0
echo "Invalid option or a positional argument was provided." >&2
_fzf_configure_bindings_help
return 22
else if set --query _flag_help
_fzf_configure_bindings_help
return
else
# Initialize with default key sequences and then override or disable them based on flags
# index 1 = directory, 2 = git_log, 3 = git_status, 4 = history, 5 = processes, 6 = variables
set -f key_sequences \e\cf \e\cl \e\cs \cr \e\cp \cv # \c = control, \e = escape
set --query _flag_directory && set key_sequences[1] "$_flag_directory"
set --query _flag_git_log && set key_sequences[2] "$_flag_git_log"
set --query _flag_git_status && set key_sequences[3] "$_flag_git_status"
set --query _flag_history && set key_sequences[4] "$_flag_history"
set --query _flag_processes && set key_sequences[5] "$_flag_processes"
set --query _flag_variables && set key_sequences[6] "$_flag_variables"
# If fzf bindings already exists, uninstall it first for a clean slate
if functions --query _fzf_uninstall_bindings
_fzf_uninstall_bindings
end
for mode in default insert
test -n $key_sequences[1] && bind --mode $mode $key_sequences[1] _fzf_search_directory
test -n $key_sequences[2] && bind --mode $mode $key_sequences[2] _fzf_search_git_log
test -n $key_sequences[3] && bind --mode $mode $key_sequences[3] _fzf_search_git_status
test -n $key_sequences[4] && bind --mode $mode $key_sequences[4] _fzf_search_history
test -n $key_sequences[5] && bind --mode $mode $key_sequences[5] _fzf_search_processes
test -n $key_sequences[6] && bind --mode $mode $key_sequences[6] "$_fzf_search_vars_command"
end
function _fzf_uninstall_bindings --inherit-variable key_sequences
bind --erase -- $key_sequences
bind --erase --mode insert -- $key_sequences
end
end
end
+21
View File
@@ -0,0 +1,21 @@
# MIT License
Copyright © `2020` `Ilan Cosman`
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,13 @@
complete tide --no-files
set -l subcommands bug-report configure reload
complete tide -x -n __fish_use_subcommand -a bug-report -d "Print info for use in bug reports"
complete tide -x -n __fish_use_subcommand -a configure -d "Run the configuration wizard"
complete tide -x -n __fish_use_subcommand -a reload -d "Reload tide configuration"
complete tide -x -n "not __fish_seen_subcommand_from $subcommands" -s h -l help -d "Print help message"
complete tide -x -n "not __fish_seen_subcommand_from $subcommands" -s v -l version -d "Print tide version"
complete tide -x -n '__fish_seen_subcommand_from bug-report' -l clean -d "Run clean Fish instance and install Tide"
complete tide -x -n '__fish_seen_subcommand_from bug-report' -l verbose -d "Print full Tide configuration"
@@ -0,0 +1,44 @@
function _tide_init_install --on-event _tide_init_install
set -U VIRTUAL_ENV_DISABLE_PROMPT true
source (functions --details _tide_sub_configure)
_load_config lean
_tide_finish
if status is-interactive
tide bug-report --check || sleep 4
if contains ilancosman/tide (string lower $_fisher_plugins)
set_color bryellow
echo "ilancosman/tide is a development branch. Please install from a release tag:"
_tide_fish_colorize "fisher install ilancosman/tide@v6"
sleep 3
end
switch (read --prompt-str="Configure tide prompt? [Y/n] " | string lower)
case y ye yes ''
tide configure
case '*'
echo -s \n 'Run ' (_tide_fish_colorize "tide configure") ' to customize your prompt.'
end
end
end
function _tide_init_update --on-event _tide_init_update
# Warn users who install from main branch
if contains ilancosman/tide (string lower $_fisher_plugins)
set_color bryellow
echo "ilancosman/tide is a development branch. Please install from a release tag:"
_tide_fish_colorize "fisher install ilancosman/tide@v6"
sleep 3
end
# Set (disable) the new jobs variable
set -q tide_jobs_number_threshold || set -U tide_jobs_number_threshold 1000
end
function _tide_init_uninstall --on-event _tide_init_uninstall
set -e VIRTUAL_ENV_DISABLE_PROMPT
set -e (set -U --names | string match --entire -r '^_?tide')
functions --erase (functions --all | string match --entire -r '^_?tide')
end
@@ -0,0 +1,19 @@
function _tide_1_line_prompt
set -g add_prefix
_tide_side=left for item in $_tide_left_items
_tide_item_$item
end
set_color $prev_bg_color -b normal
echo $tide_left_prompt_suffix
set -g add_prefix
_tide_side=right for item in $_tide_right_items
_tide_item_$item
end
set_color $prev_bg_color -b normal
echo $tide_right_prompt_suffix
end
function _tide_item_pwd
_tide_print_item pwd @PWD@
end
@@ -0,0 +1,31 @@
function _tide_2_line_prompt
set -g add_prefix
_tide_side=left for item in $_tide_left_items
_tide_item_$item
end
if not set -e add_prefix
set_color $prev_bg_color -b normal
echo $tide_left_prompt_suffix
end
echo
set -g add_prefix
_tide_side=right for item in $_tide_right_items
_tide_item_$item
end
if not set -e add_prefix
set_color $prev_bg_color -b normal
echo $tide_right_prompt_suffix
end
end
function _tide_item_pwd
_tide_print_item pwd @PWD@
end
function _tide_item_newline
set_color $prev_bg_color -b normal
v=tide_"$_tide_side"_prompt_suffix echo $$v
set -g add_prefix
end
@@ -0,0 +1,17 @@
function _tide_cache_variables
# Same-color-separator color
set_color $tide_prompt_color_separator_same_color | read -gx _tide_color_separator_same_color
# git
contains git $_tide_left_items $_tide_right_items && set_color $tide_git_color_branch | read -gx _tide_location_color
# private_mode
if contains private_mode $_tide_left_items $_tide_right_items && test -n "$fish_private_mode"
set -gx _tide_private_mode
else
set -e _tide_private_mode
end
# item padding
test "$tide_prompt_pad_items" = true && set -gx _tide_pad ' ' || set -e _tide_pad
end
@@ -0,0 +1,76 @@
# Outputs icon, color, bg_color
function _tide_detect_os
set -lx defaultColor 080808 CED7CF
switch (uname | string lower)
case darwin
printf %s\n  D6D6D6 333333 # from apple.com header
case freebsd openbsd dragonfly
printf %s\n  FFFFFF AB2B28 # https://freebsdfoundation.org/about-us/about-the-foundation/project/
case 'cygwin*' 'mingw*_nt*' 'msys_nt*'
printf %s\n  FFFFFF 00CCFF # https://answers.microsoft.com/en-us/windows/forum/all/what-is-the-official-windows-8-blue-rgb-or-hex/fd57144b-f69b-42d8-8c21-6ca911646e44
case linux
if test (uname -o) = Android
# https://developer.android.com/distribute/marketing-tools/brand-guidelines
printf %s\n  3DDC84 3C3F41 # fg is from above link, bg is from Android Studio default dark theme
else
_tide_detect_os_linux_cases /etc/os-release ID ||
_tide_detect_os_linux_cases /etc/os-release ID_LIKE ||
_tide_detect_os_linux_cases /etc/lsb-release DISTRIB_ID ||
printf %s\n$defaultColor
end
case '*'
echo -ns '?'
end
end
function _tide_detect_os_linux_cases -a file key
test -e $file || return
set -l split_file (string split '=' <$file)
set -l key_index (contains --index $key $split_file) || return
set -l value (string trim --chars='"' $split_file[(math $key_index + 1)])
# Anything which would have pure white background has been changed to D4D4D4
# It was just too bright otherwise
switch (string lower $value)
case alpine
printf %s\n  FFFFFF 0D597F # from alpine logo
case arch
printf %s\n  1793D1 4D4D4D # from arch wiki header
case centos
printf %s\n000000 D4D4D4 # https://wiki.centos.org/ArtWork/Brand/Logo, monochromatic
case debian
printf %s\n  C70036 D4D4D4 # from debian logo https://www.debian.org/logos/openlogo-nd-100.png
case devuan
printf %s\n$defaultColor # logo is monochromatic
case elementary
printf %s\n000000 D4D4D4 # https://elementary.io/brand, encouraged to be monochromatic
case fedora
printf %s\n  FFFFFF 294172 # from logo https://fedoraproject.org/w/uploads/2/2d/Logo_fedoralogo.png
case gentoo
printf %s\n  FFFFFF 54487A # https://wiki.gentoo.org/wiki/Project:Artwork/Colors
case mageia
printf %s\n  FFFFFF 262F45 # https://wiki.mageia.org/en/Artwork_guidelines
case manjaro
printf %s\n  FFFFFF 35BF5C # from https://gitlab.manjaro.org/artwork/branding/logo/-/blob/master/logo.svg
case mint linuxmint
printf %s\n  FFFFFF 69B53F # extracted from https://linuxmint.com/web/img/favicon.ico
case nixos
printf %s\n  FFFFFF 5277C3 # https://github.com/NixOS/nixos-artwork/tree/master/logo
case opensuse-leap opensuse-tumbleweed opensuse-microos
printf %s\n  73BA25 173f4f # https://en.opensuse.org/openSUSE:Artwork_brand
case raspbian
printf %s\n  FFFFFF A22846 # https://static.raspberrypi.org/files/Raspberry_Pi_Visual_Guidelines_2020.pdf
case rhel
printf %s\n  EE0000 000000 # https://www.redhat.com/en/about/brand/standards/color
case sabayon
printf %s\n$defaultColor # Can't find colors, and they are rebranding anyway
case slackware
printf %s\n$defaultColor # Doesn't really have a logo, and the colors are too close to PWD blue anyway
case ubuntu
printf %s\n  E95420 D4D4D4 # https://design.ubuntu.com/brand/
case void
printf %s\n  FFFFFF 478061 # from https://alpha.de.repo.voidlinux.org/logos/void.svg
case '*'
return 1
end
end
@@ -0,0 +1,3 @@
function _tide_find_and_remove -a name list --no-scope-shadowing
contains --index $name $$list | read -l index && set -e "$list"[$index]
end
@@ -0,0 +1,7 @@
function _tide_fish_colorize
if command -q fish_indent
echo -ns "$argv" | fish_indent --ansi
else
echo -ns "$argv"
end
end
@@ -0,0 +1,11 @@
function _tide_item_aws
# AWS_PROFILE overrides AWS_DEFAULT_PROFILE, AWS_REGION overrides AWS_DEFAULT_REGION
set -q AWS_PROFILE && set -l AWS_DEFAULT_PROFILE $AWS_PROFILE
set -q AWS_REGION && set -l AWS_DEFAULT_REGION $AWS_REGION
if test -n "$AWS_DEFAULT_PROFILE" && test -n "$AWS_DEFAULT_REGION"
_tide_print_item aws $tide_aws_icon' ' "$AWS_DEFAULT_PROFILE/$AWS_DEFAULT_REGION"
else if test -n "$AWS_DEFAULT_PROFILE$AWS_DEFAULT_REGION"
_tide_print_item aws $tide_aws_icon' ' "$AWS_DEFAULT_PROFILE$AWS_DEFAULT_REGION"
end
end
@@ -0,0 +1,6 @@
function _tide_item_bun
if path is $_tide_parent_dirs/bun.lockb
bun --version | string match -qr "(?<v>.*)"
_tide_print_item bun $tide_bun_icon' ' $v
end
end
@@ -0,0 +1,17 @@
function _tide_item_character
test $_tide_status = 0 && set_color $tide_character_color || set_color $tide_character_color_failure
set -q add_prefix || echo -ns ' '
test "$fish_key_bindings" = fish_default_key_bindings && echo -ns $tide_character_icon ||
switch $fish_bind_mode
case insert
echo -ns $tide_character_icon
case default
echo -ns $tide_character_vi_icon_default
case replace replace_one
echo -ns $tide_character_vi_icon_replace
case visual
echo -ns $tide_character_vi_icon_visual
end
end
@@ -0,0 +1,12 @@
function _tide_item_cmd_duration
test $CMD_DURATION -gt $tide_cmd_duration_threshold && t=(
math -s0 "$CMD_DURATION/3600000" # Hours
math -s0 "$CMD_DURATION/60000"%60 # Minutes
math -s$tide_cmd_duration_decimals "$CMD_DURATION/1000"%60) if test $t[1] != 0
_tide_print_item cmd_duration $tide_cmd_duration_icon' ' "$t[1]h $t[2]m $t[3]s"
else if test $t[2] != 0
_tide_print_item cmd_duration $tide_cmd_duration_icon' ' "$t[2]m $t[3]s"
else
_tide_print_item cmd_duration $tide_cmd_duration_icon' ' "$t[3]s"
end
end
@@ -0,0 +1,14 @@
function _tide_item_context
if set -q SSH_TTY
set -fx tide_context_color $tide_context_color_ssh
else if test "$EUID" = 0
set -fx tide_context_color $tide_context_color_root
else if test "$tide_context_always_display" = true
set -fx tide_context_color $tide_context_color_default
else
return
end
string match -qr "^(?<h>(\.?[^\.]*){0,$tide_context_hostname_parts})" @$hostname
_tide_print_item context $USER$h
end
@@ -0,0 +1,6 @@
function _tide_item_crystal
if path is $_tide_parent_dirs/shard.yml
crystal --version | string match -qr "(?<v>[\d.]+)"
_tide_print_item crystal $tide_crystal_icon' ' $v
end
end
@@ -0,0 +1,7 @@
function _tide_item_direnv
set -q DIRENV_DIR || return
direnv status | string match -q 'Found RC allowed false' &&
set -lx tide_direnv_color $tide_direnv_color_denied &&
set -lx tide_direnv_bg_color $tide_direnv_bg_color_denied
_tide_print_item direnv $tide_direnv_icon
end
@@ -0,0 +1,4 @@
function _tide_item_distrobox
test -e /etc/profile.d/distrobox_profile.sh && test -e /run/.containerenv &&
_tide_print_item distrobox $tide_distrobox_icon' ' (string match -rg 'name="(.*)"' </run/.containerenv)
end
@@ -0,0 +1,5 @@
function _tide_item_docker
docker context inspect --format '{{.Name}}' | read -l context
contains -- "$context" $tide_docker_default_contexts ||
_tide_print_item docker $tide_docker_icon' ' $context
end
@@ -0,0 +1,4 @@
function _tide_item_elixir
path is $_tide_parent_dirs/mix.exs &&
_tide_print_item elixir $tide_elixir_icon' ' (elixir --short-version)
end
@@ -0,0 +1,8 @@
function _tide_item_gcloud
set -q CLOUDSDK_CONFIG || set -l CLOUDSDK_CONFIG ~/.config/gcloud
path is $CLOUDSDK_CONFIG/active_config &&
read -l config <$CLOUDSDK_CONFIG/active_config &&
path is $CLOUDSDK_CONFIG/configurations/config_$config &&
string match -qr '^\s*project\s*=\s*(?<project>.*)' <$CLOUDSDK_CONFIG/configurations/config_$config &&
_tide_print_item gcloud $tide_gcloud_icon' ' $project
end
@@ -0,0 +1,72 @@
function _tide_item_git
if git branch --show-current 2>/dev/null | string shorten -"$tide_git_truncation_strategy"m$tide_git_truncation_length | read -l location
git rev-parse --git-dir --is-inside-git-dir | read -fL gdir in_gdir
set location $_tide_location_color$location
else if test $pipestatus[1] != 0
return
else if git tag --points-at HEAD | string shorten -"$tide_git_truncation_strategy"m$tide_git_truncation_length | read location
git rev-parse --git-dir --is-inside-git-dir | read -fL gdir in_gdir
set location '#'$_tide_location_color$location
else
git rev-parse --git-dir --is-inside-git-dir --short HEAD | read -fL gdir in_gdir location
set location @$_tide_location_color$location
end
# Operation
if test -d $gdir/rebase-merge
# Turn ANY into ALL, via double negation
if not path is -v $gdir/rebase-merge/{msgnum,end}
read -f step <$gdir/rebase-merge/msgnum
read -f total_steps <$gdir/rebase-merge/end
end
test -f $gdir/rebase-merge/interactive && set -f operation rebase-i || set -f operation rebase-m
else if test -d $gdir/rebase-apply
if not path is -v $gdir/rebase-apply/{next,last}
read -f step <$gdir/rebase-apply/next
read -f total_steps <$gdir/rebase-apply/last
end
if test -f $gdir/rebase-apply/rebasing
set -f operation rebase
else if test -f $gdir/rebase-apply/applying
set -f operation am
else
set -f operation am/rebase
end
else if test -f $gdir/MERGE_HEAD
set -f operation merge
else if test -f $gdir/CHERRY_PICK_HEAD
set -f operation cherry-pick
else if test -f $gdir/REVERT_HEAD
set -f operation revert
else if test -f $gdir/BISECT_LOG
set -f operation bisect
end
# Git status/stash + Upstream behind/ahead
test $in_gdir = true && set -l _set_dir_opt -C $gdir/..
# Suppress errors in case we are in a bare repo or there is no upstream
set -l stat (git $_set_dir_opt --no-optional-locks status --porcelain 2>/dev/null)
string match -qr '(0|(?<stash>.*))\n(0|(?<conflicted>.*))\n(0|(?<staged>.*))
(0|(?<dirty>.*))\n(0|(?<untracked>.*))(\n(0|(?<behind>.*))\t(0|(?<ahead>.*)))?' \
"$(git $_set_dir_opt stash list 2>/dev/null | count
string match -r ^UU $stat | count
string match -r ^[ADMR] $stat | count
string match -r ^.[ADMR] $stat | count
string match -r '^\?\?' $stat | count
git rev-list --count --left-right @{upstream}...HEAD 2>/dev/null)"
if test -n "$operation$conflicted"
set -g tide_git_bg_color $tide_git_bg_color_urgent
else if test -n "$staged$dirty$untracked"
set -g tide_git_bg_color $tide_git_bg_color_unstable
end
_tide_print_item git $_tide_location_color$tide_git_icon' ' (set_color white; echo -ns $location
set_color $tide_git_color_operation; echo -ns ' '$operation ' '$step/$total_steps
set_color $tide_git_color_upstream; echo -ns ' ⇣'$behind ' ⇡'$ahead
set_color $tide_git_color_stash; echo -ns ' *'$stash
set_color $tide_git_color_conflicted; echo -ns ' ~'$conflicted
set_color $tide_git_color_staged; echo -ns ' +'$staged
set_color $tide_git_color_dirty; echo -ns ' !'$dirty
set_color $tide_git_color_untracked; echo -ns ' ?'$untracked)
end
@@ -0,0 +1,6 @@
function _tide_item_go
if path is $_tide_parent_dirs/go.mod
go version | string match -qr "(?<v>[\d.]+)"
_tide_print_item go $tide_go_icon' ' $v
end
end
@@ -0,0 +1,6 @@
function _tide_item_java
if path is $_tide_parent_dirs/pom.xml
java -version &| string match -qr "(?<v>[\d.]+)"
_tide_print_item java $tide_java_icon' ' $v
end
end
@@ -0,0 +1,7 @@
function _tide_item_jobs
set -q _tide_jobs && if test $_tide_jobs -ge $tide_jobs_number_threshold
_tide_print_item jobs $tide_jobs_icon' ' $_tide_jobs
else
_tide_print_item jobs $tide_jobs_icon
end
end
@@ -0,0 +1,4 @@
function _tide_item_kubectl
kubectl config view --minify --output 'jsonpath={.current-context}/{..namespace}' 2>/dev/null | read -l context &&
_tide_print_item kubectl $tide_kubectl_icon' ' (string replace -r '/(|default)$' '' $context)
end
@@ -0,0 +1,3 @@
function _tide_item_nix_shell
set -q IN_NIX_SHELL && _tide_print_item nix_shell $tide_nix_shell_icon' ' $IN_NIX_SHELL
end
@@ -0,0 +1,6 @@
function _tide_item_node
if path is $_tide_parent_dirs/package.json
node --version | string match -qr "v(?<v>.*)"
_tide_print_item node $tide_node_icon' ' $v
end
end
@@ -0,0 +1,3 @@
function _tide_item_os
_tide_print_item os $tide_os_icon
end
@@ -0,0 +1,6 @@
function _tide_item_php
if path is $_tide_parent_dirs/composer.json
php --version | string match -qr "(?<v>[\d.]+)"
_tide_print_item php $tide_php_icon' ' $v
end
end
@@ -0,0 +1,3 @@
function _tide_item_private_mode
set -q _tide_private_mode && _tide_print_item private_mode $tide_private_mode_icon
end
@@ -0,0 +1,19 @@
function _tide_item_pulumi
if path filter $_tide_parent_dirs/Pulumi.yaml | read -l yaml_path
if command -q sha1sum
echo -n "$yaml_path" | sha1sum | string match -qr "(?<path_hash>.{40})"
else if command -q shasum
echo -n "$yaml_path" | shasum | string match -qr "(?<path_hash>.{40})"
else
return
end
string match -qr 'name: *(?<project_name>.*)' <$yaml_path
set -l workspace_file "$HOME/.pulumi/workspaces/$project_name-$path_hash-workspace.json"
if test -e $workspace_file
string match -qr '"stack": *"(?<stack>.*)"' <$workspace_file
_tide_print_item pulumi $tide_pulumi_icon' ' $stack
end
end
end
@@ -0,0 +1,27 @@
function _tide_item_python
if test -n "$VIRTUAL_ENV"
if command -q python3
python3 --version | string match -qr "(?<v>[\d.]+)"
else
python --version | string match -qr "(?<v>[\d.]+)"
end
string match -qr "^.*/(?<dir>.*)/(?<base>.*)" $VIRTUAL_ENV
# pipenv $VIRTUAL_ENV looks like /home/ilan/.local/share/virtualenvs/pipenv_project-EwRYuc3l
# Detect whether we are using pipenv by looking for 'virtualenvs'. If so, remove the hash at the end.
if test "$dir" = virtualenvs
string match -qr "(?<base>.*)-.*" $base
_tide_print_item python $tide_python_icon' ' "$v ($base)"
else if contains -- "$base" virtualenv venv .venv env # avoid generic names
_tide_print_item python $tide_python_icon' ' "$v ($dir)"
else
_tide_print_item python $tide_python_icon' ' "$v ($base)"
end
else if path is .python-version Pipfile __init__.py pyproject.toml requirements.txt setup.py
if command -q python3
python3 --version | string match -qr "(?<v>[\d.]+)"
else
python --version | string match -qr "(?<v>[\d.]+)"
end
_tide_print_item python $tide_python_icon' ' $v
end
end
@@ -0,0 +1,6 @@
function _tide_item_ruby
if path is $_tide_parent_dirs/{*.gemspec,Gemfile,Rakefile,.ruby-version}
ruby --version | string match -qr "(?<v>[\d.]+)"
_tide_print_item ruby $tide_ruby_icon' ' $v
end
end
@@ -0,0 +1,6 @@
function _tide_item_rustc
if path is $_tide_parent_dirs/Cargo.toml
rustc --version | string match -qr "(?<v>[\d.]+)"
_tide_print_item rustc $tide_rustc_icon' ' $v
end
end
@@ -0,0 +1,4 @@
function _tide_item_shlvl
# Non-interactive shells do not increment SHLVL, so we don't need to subtract 1
test $SHLVL -gt $tide_shlvl_threshold && _tide_print_item shlvl $tide_shlvl_icon' ' $SHLVL
end
@@ -0,0 +1,15 @@
function _tide_item_status
if string match -qv 0 $_tide_pipestatus # If there is a failure anywhere in the pipestatus
if test "$_tide_pipestatus" = 1 # If simple failure
contains character $_tide_left_items || tide_status_bg_color=$tide_status_bg_color_failure \
tide_status_color=$tide_status_color_failure _tide_print_item status $tide_status_icon_failure' ' 1
else
fish_status_to_signal $_tide_pipestatus | string replace SIG '' | string join '|' | read -l out
test $_tide_status = 0 && _tide_print_item status $tide_status_icon' ' $out ||
tide_status_bg_color=$tide_status_bg_color_failure tide_status_color=$tide_status_color_failure \
_tide_print_item status $tide_status_icon_failure' ' $out
end
else if not contains character $_tide_left_items
_tide_print_item status $tide_status_icon
end
end
@@ -0,0 +1,5 @@
function _tide_item_terraform
path is $_tide_parent_dirs/.terraform &&
terraform workspace show | string match -v default | read -l w &&
_tide_print_item terraform $tide_terraform_icon' ' $w
end
@@ -0,0 +1,3 @@
function _tide_item_time
_tide_print_item time (date +$tide_time_format)
end
@@ -0,0 +1,4 @@
function _tide_item_toolbox
test -e /run/.toolboxenv &&
_tide_print_item toolbox $tide_toolbox_icon' ' (string match -rg 'name="(.*)"' </run/.containerenv)
end
@@ -0,0 +1,16 @@
function _tide_item_vi_mode
test "$fish_key_bindings" != fish_default_key_bindings && switch $fish_bind_mode
case default
tide_vi_mode_bg_color=$tide_vi_mode_bg_color_default tide_vi_mode_color=$tide_vi_mode_color_default \
_tide_print_item vi_mode $tide_vi_mode_icon_default
case insert
tide_vi_mode_bg_color=$tide_vi_mode_bg_color_insert tide_vi_mode_color=$tide_vi_mode_color_insert \
_tide_print_item vi_mode $tide_vi_mode_icon_insert
case replace replace_one
tide_vi_mode_bg_color=$tide_vi_mode_bg_color_replace tide_vi_mode_color=$tide_vi_mode_color_replace \
_tide_print_item vi_mode $tide_vi_mode_icon_replace
case visual
tide_vi_mode_bg_color=$tide_vi_mode_bg_color_visual tide_vi_mode_color=$tide_vi_mode_color_visual \
_tide_print_item vi_mode $tide_vi_mode_icon_visual
end
end
@@ -0,0 +1,6 @@
function _tide_item_zig
if path is $_tide_parent_dirs/build.zig
zig version | string match -qr "(?<v>[\d.]+(-dev)?)"
_tide_print_item zig $tide_zig_icon' ' $v
end
end
@@ -0,0 +1,7 @@
function _tide_parent_dirs --on-variable PWD
set -g _tide_parent_dirs (string escape (
for dir in (string split / -- $PWD)
set -la parts $dir
string join / -- $parts
end))
end
@@ -0,0 +1,22 @@
function _tide_print_item -a item
v=tide_"$item"_bg_color set -f item_bg_color $$v
if set -e add_prefix
set_color $item_bg_color -b normal
v=tide_"$_tide_side"_prompt_prefix echo -ns $$v
else if test "$item_bg_color" = "$prev_bg_color"
v=tide_"$_tide_side"_prompt_separator_same_color echo -ns $_tide_color_separator_same_color$$v
else if test $_tide_side = left
set_color $prev_bg_color -b $item_bg_color
echo -ns $tide_left_prompt_separator_diff_color
else
set_color $item_bg_color -b $prev_bg_color
echo -ns $tide_right_prompt_separator_diff_color
end
v=tide_"$item"_color set_color $$v -b $item_bg_color
echo -ns $_tide_pad $argv[2..] $_tide_pad
set -g prev_bg_color $item_bg_color
end
@@ -0,0 +1,42 @@
set_color -o $tide_pwd_color_anchors | read -l color_anchors
set_color $tide_pwd_color_truncated_dirs | read -l color_truncated
set -l reset_to_color_dirs (set_color normal -b $tide_pwd_bg_color; set_color $tide_pwd_color_dirs)
set -l unwritable_icon $tide_pwd_icon_unwritable' '
set -l home_icon $tide_pwd_icon_home' '
set -l pwd_icon $tide_pwd_icon' '
eval "function _tide_pwd
if set -l split_pwd (string replace -r '^$HOME' '~' -- \$PWD | string split /)
test -w . && set -f split_output \"$pwd_icon\$split_pwd[1]\" \$split_pwd[2..] ||
set -f split_output \"$unwritable_icon\$split_pwd[1]\" \$split_pwd[2..]
set split_output[-1] \"$color_anchors\$split_output[-1]$reset_to_color_dirs\"
else
set -f split_output \"$home_icon$color_anchors~\"
end
string join / -- \$split_output | string length -V | read -g _tide_pwd_len
i=1 for dir_section in \$split_pwd[2..-2]
string join -- / \$split_pwd[..\$i] | string replace '~' $HOME | read -l parent_dir # Uses i before increment
math \$i+1 | read i
if path is \$parent_dir/\$dir_section/\$tide_pwd_markers
set split_output[\$i] \"$color_anchors\$dir_section$reset_to_color_dirs\"
else if test \$_tide_pwd_len -gt \$dist_btwn_sides
string match -qr \"(?<trunc>\..|.)\" \$dir_section
set -l glob \$parent_dir/\$trunc*/
set -e glob[(contains -i \$parent_dir/\$dir_section/ \$glob)] # This is faster than inverse string match
while string match -qr \"^\$parent_dir/\$(string escape --style=regex \$trunc)\" \$glob &&
string match -qr \"(?<trunc>\$(string escape --style=regex \$trunc).)\" \$dir_section
end
test -n \"\$trunc\" && set split_output[\$i] \"$color_truncated\$trunc$reset_to_color_dirs\" &&
string join / \$split_output | string length -V | read _tide_pwd_len
end
end
string join -- / \"$reset_to_color_dirs\$split_output[1]\" \$split_output[2..]
end"
@@ -0,0 +1,25 @@
function _tide_remove_unusable_items
# Remove tool-specific items for tools the machine doesn't have installed
set -l removed_items
for item in aws bun crystal direnv distrobox docker elixir gcloud git go java kubectl nix_shell node php pulumi python ruby rustc terraform toolbox zig
contains $item $tide_left_prompt_items $tide_right_prompt_items || continue
set -l cli_names $item
switch $item
case distrobox # there is no 'distrobox' command inside the container
set cli_names distrobox-export # 'distrobox-export' and 'distrobox-host-exec' are available
case nix_shell
set cli_names nix nix-shell
case python
set cli_names python python3
end
type --query $cli_names || set -a removed_items $item
end
set -U _tide_left_items (for item in $tide_left_prompt_items
contains $item $removed_items || echo $item
end)
set -U _tide_right_items (for item in $tide_right_prompt_items
contains $item $removed_items || echo $item
end)
end
@@ -0,0 +1,73 @@
function _tide_sub_bug-report
argparse c/clean v/verbose check -- $argv
set -l fish_path (status fish-path)
if set -q _flag_clean
HOME=(mktemp -d) $fish_path --init-command "curl --silent \
https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish |
source && fisher install ilancosman/tide@v6"
else if set -q _flag_verbose
set --long | string match -r "^_?tide.*" | # Get only tide variables
string match -r --invert "^_tide_prompt_var.*" # Remove _tide_prompt_var
else
$fish_path --version | string match -qr "fish, version (?<fish_version>.*)"
_tide_check_version Fish fish-shell/fish-shell "(?<v>[\d.]+)" $fish_version || return
tide --version | string match -qr "tide, version (?<tide_version>.*)"
_tide_check_version Tide IlanCosman/tide "v(?<v>[\d.]+)" $tide_version || return
if command --query git
test (path sort (git --version) "git version 2.22.0")[1] = "git version 2.22.0"
_tide_check_condition \
"Your git version is too old." \
"Tide requires at least version 2.22." \
"Please update before submitting a bug report." || return
end
# Check that omf is not installed
not functions --query omf
_tide_check_condition \
"Tide does not work with oh-my-fish installed." \
"Please uninstall it before submitting a bug report." || return
if not set -q _flag_check
$fish_path -ic "time $fish_path -c exit" 2>|
string match -rg "Executed in(.*)fish" |
string trim | read -l fish_startup_time
read -l --prompt-str "What operating system are you using? (e.g Ubuntu 20.04): " os
read -l --prompt-str "What terminal emulator are you using? (e.g Kitty): " terminal_emulator
printf '%b\n' "\nPlease copy the following information into the issue:\n" \
"fish version: $fish_version" \
"tide version: $tide_version" \
"term: $TERM" \
"os: $os" \
"terminal emulator: $terminal_emulator" \
"fish startup: $fish_startup_time" \
"fisher plugins: $_fisher_plugins"
end
end
end
function _tide_check_version -a program_name repo_name regex_to_get_v installed_version
curl -sL https://github.com/$repo_name/releases/latest |
string match -qr "https://github.com/$repo_name/releases/tag/$regex_to_get_v"
string match -qr "^$v" "$installed_version" # Allow git versions, e.g 3.3.1-701-gceade1629
_tide_check_condition \
"Your $program_name version is out of date." \
"The latest is $v. You have $installed_version." \
"Please update before submitting a bug report."
end
function _tide_check_condition
if test "$status" != 0
set_color red
printf '%s\n' $argv
set_color normal
return 1
end
return 0
end
@@ -0,0 +1,156 @@
set -g _tide_color_dark_blue 0087AF
set -g _tide_color_dark_green 5FAF00
set -g _tide_color_gold D7AF00
set -g _tide_color_green 5FD700
set -g _tide_color_light_blue 00AFFF
# Create an empty fake function for each item
for func in _fake(functions --all | string match --entire _tide_item)
function $func
end
end
for file in (status dirname)/tide/configure/{choices, functions}/**.fish
source $file
end
function _tide_sub_configure
set -l choices (path basename (status dirname)/tide/configure/choices/**.fish | path change-extension '')
argparse auto $choices= -- $argv
for var in (set -l --names | string match -e _flag)
set -x $var $$var
end
if set -q _flag_auto
set -fx _flag_finish 'Overwrite your current tide config'
else if test $COLUMNS -lt 55 -o $LINES -lt 21
echo 'Terminal size too small; must be at least 55 x 21'
return 1
end
_tide_detect_os | read -g --line os_branding_icon os_branding_color os_branding_bg_color
set -g fake_columns $COLUMNS
test $fake_columns -gt 90 && set fake_columns 90
set -g fake_lines $LINES
set -g _tide_selected_option
_next_choice all/style
end
function _next_choice -a nextChoice
set -q _tide_selected_option || return 0
set -l cmd (string split '/' $nextChoice)[2]
$cmd
end
function _tide_title -a text
set -q _flag_auto && return
command -q clear && clear
set_color -o
string pad --width (math --scale=0 "$fake_columns/2" + (string length $text)/2) $text
set_color normal
set -g _tide_configure_first_option_after_title
end
function _tide_option -a symbol text
set -ga _tide_symbol_list $symbol
set -ga _tide_option_list $text
if not set -q _flag_auto
set -g _tide_configure_first_prompt_after_option
set_color -o
set -e _tide_configure_first_option_after_title || echo
echo "($symbol) $text"
set_color normal
end
end
function _tide_menu -a func
if set -q _flag_auto
set -l flag_var_name _flag_$func
set -g _tide_selected_option $$flag_var_name
if test -z "$_tide_selected_option"
echo "Missing input for choice '$func'"
_tide_exit_configure
else if not contains $_tide_selected_option $_tide_option_list
echo "Invalid input '$_tide_selected_option' for choice '$func'"
_tide_exit_configure
else
set -e _tide_symbol_list
set -e _tide_option_list
end
return
end
argparse no-restart -- $argv # Add no-restart option for first menu
echo
if not set -q _flag_no_restart
set -f r r
echo '(r) Restart from the beginning'
end
echo '(q) Quit and do nothing'\n
while read --nchars 1 --prompt-str \
"$(set_color -o)Choice [$(string join '/' $_tide_symbol_list $r q)] $(set_color normal)" input
switch $input
case r
set -q _flag_no_restart && continue
set -e _tide_symbol_list
set -e _tide_option_list
_next_choice all/style
break
case q
_tide_exit_configure
set -e _tide_symbol_list
set -e _tide_option_list
command -q clear && clear
break
case $_tide_symbol_list
set -g _tide_selected_option $_tide_option_list[(contains -i $input $_tide_symbol_list)]
test "$func" != finish &&
set -a _tide_configure_current_options --$func=(string escape $_tide_selected_option)
set -e _tide_symbol_list
set -e _tide_option_list
break
end
end
end
function _tide_display_prompt
set -q _flag_auto && return
_fake_tide_cache_variables
set -l prompt (_fake_tide_prompt)
set -l bottom_left_prompt_string_length (string length --visible $prompt[-1])
set -l right_prompt_string (string pad --width (math $fake_columns-$bottom_left_prompt_string_length) $prompt[1])
set -l prompt[-1] "$prompt[-1]$right_prompt_string"
if set -q _configure_transient
if contains newline $fake_tide_left_prompt_items
string unescape $prompt[3..]
else
_fake_tide_item_character
echo
end
else
if not set -q _tide_configure_first_prompt_after_option
test "$fake_tide_prompt_add_newline_before" = true && echo
end
string unescape $prompt[2..]
end
set -e _tide_configure_first_prompt_after_option
set_color normal
end
function _tide_exit_configure
set -e _tide_selected_option # Skip through all switch and _next_choice
end
@@ -0,0 +1,3 @@
function _tide_sub_reload
source (functions --details fish_prompt)
end
@@ -0,0 +1 @@
# Disable default vi prompt

Some files were not shown because too many files have changed in this diff Show More