commit c532ebcc713ea4faae1eef43990c3bac578bfb34 Author: Ash (ashisbored) Date: Sun Nov 21 19:42:14 2021 +0000 chore: Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1aaa440 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,265 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fsh" +version = "0.1.0" +dependencies = [ + "ansi_term", + "git2", + "users", +] + +[[package]] +name = "git2" +version = "0.13.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845e007a28f1fcac035715988a234e8ec5458fd825b20a20c7dec74237ef341f" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" + +[[package]] +name = "libgit2-sys" +version = "0.12.25+1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68169ef08d6519b2fe133ecc637408d933c0174b23b80bb2f79828966fbaab" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pkg-config" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fb1f9db --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "fsh" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +git2 = "0.13" +users = "0.11" +ansi_term = "0.12" diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bffe42 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +What do you call a fish with no eyes? + +# fsh + +A `fish` theme based on the following: + +- [queer/p](https://github.com/queer/p) +- [oh-my-zsh's half-life theme](https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/half-life.zsh-theme) +- [Dracula theme](https://draculatheme.com) + +Requires a font with the required icons, I use [Nerd Fonts](https://www.nerdfonts.com/). diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..9d7af32 --- /dev/null +++ b/setup.sh @@ -0,0 +1,13 @@ +#!/bin/bash +echo Compiling with cargo... +cargo build --release +echo Copying to $HOME/.local/bin +cp target/release/fsh $HOME/.local/bin/ + +if [ ! -f $HOME/.config/fish/functions/fish_prompt.fish ]; then + echo "Creating $HOME/.config/fish/functions/fish_prompt.fish" + echo "function fish_prompt + set FSH_LAST_STATUS \$status + fsh \$LAST_STATUS +end" > $HOME/.config/fish/functions/fish_prompt.fish +fi diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..497490f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,174 @@ +use ansi_term::{Colour as AnsiColour, Style}; +use git2::{Repository, Status as GitStatus}; +use std::{fmt::Display, path::PathBuf}; + +struct PromptComponent { + text: String, + style: Style, +} + +enum Colour { + Red, + Green, + Blue, + Yellow, + Pink, + Purple, +} + +fn from_hex(hex: u32) -> AnsiColour { + AnsiColour::RGB( + (hex >> 16 & 0xff) as u8, + (hex >> 8 & 0xff) as u8, + (hex >> 0 & 0xff) as u8, + ) +} + +impl Colour { + fn to_ansi(&self) -> AnsiColour { + match self { + Colour::Red => from_hex(0xff5555), + Colour::Green => from_hex(0x50fa7b), + Colour::Blue => from_hex(0x8be9fd), + Colour::Yellow => from_hex(0xf1fa8c), + Colour::Pink => from_hex(0xff79c6), + Colour::Purple => from_hex(0xbd93f9), + } + } +} + +impl PromptComponent { + fn new(text: &str, style: Style) -> Self { + Self { + text: text.to_string(), + style, + } + } + + fn bold(text: &str, colour: Colour) -> Self { + Self::new(text, colour.to_ansi().bold()) + } + + fn unstyled(text: &str) -> Self { + Self { + text: text.to_string(), + style: Style::default(), + } + } +} + +impl Display for PromptComponent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{}", self.style.paint(&self.text))) + } +} + +fn main() { + let last_status = if let Some(status) = std::env::args().into_iter().skip(1).next() { + status.parse().unwrap() + } else { + 0 + }; + + let mut components = Vec::new(); + + let user = users::get_current_username().map(|s| s.to_string_lossy().into_owned()).unwrap_or_else(|| "unknown".to_string()); + components.push(PromptComponent::bold(&user, Colour::Purple)); + + let home_dir = std::env::var("HOME").unwrap(); + let pwd = std::env::current_dir().unwrap(); + let dir = if pwd.starts_with(&home_dir) { + let mut buf = PathBuf::new(); + buf.push("~"); + if pwd != std::path::Path::new(&home_dir) { + let rel_path = pwd.strip_prefix(&home_dir).unwrap(); + buf.push(rel_path); + } + format!("{}", buf.to_string_lossy()) + } else { + format!("{:?}", pwd.to_string_lossy()) + }; + + let repo = Repository::discover(".").ok(); + + components.push(PromptComponent::unstyled("in")); + components.push(PromptComponent::bold(&dir, Colour::Green)); + + get_git_info(&mut components, &repo); + + if last_status == 0 { + components.push(PromptComponent::bold("\u{f061}", Colour::Yellow)); + } else { + components.push(PromptComponent::bold(&format!("{} \u{f061}", last_status), Colour::Red)); + } + + for component in components { + print!("{} ", component); + } +} + +fn get_git_info(components: &mut Vec, repo: &Option) { + match repo { + Some(repo) => { + let head = match repo.head() { + Ok(head) => Some(head.shorthand().unwrap().to_string()), + Err(ref e) if e.code() == git2::ErrorCode::NotFound => { + None + } + Err(ref e) if e.code() == git2::ErrorCode::UnbornBranch => { + // https://github.com/starship/starship/commit/489838e6a24ea1c08be6abe56d066724a1d59abd#diff-d6346fd7d17270b1282142aeeda9c4bc2b7d8fd0f37b24a1c871a9257f0ed0aaR324-R336 + let mut head_path = repo.path().to_path_buf(); + head_path.push("HEAD"); + + std::fs::read_to_string(&head_path) + .ok().unwrap() + .lines() + .next().unwrap() + .trim() + .split('/') + .last() + .map(|r| r.to_owned()) + } + Err(e) => Err(e).unwrap(), + }; + + let head = head.unwrap_or("(no HEAD)".to_string()); + + components.push(PromptComponent::bold(&format!("\u{e725} {}", head), Colour::Blue)); + + let mut unstaged = false; + let mut staged = false; + for status in repo.statuses(None).unwrap().iter() { + let status = status.status(); + if status.intersects(GitStatus::WT_DELETED | GitStatus::WT_MODIFIED | GitStatus::WT_NEW | GitStatus::WT_RENAMED | GitStatus::WT_TYPECHANGE) { + unstaged = true; + } + if status.intersects(GitStatus::INDEX_DELETED | GitStatus::INDEX_MODIFIED | GitStatus::INDEX_NEW | GitStatus::INDEX_RENAMED | GitStatus::INDEX_TYPECHANGE) { + staged = true; + } + } + + let action = match repo.state() { + git2::RepositoryState::Merge => Some(PromptComponent::bold("merge", Colour::Pink)), + git2::RepositoryState::Revert | git2::RepositoryState::RevertSequence => Some(PromptComponent::bold("revert", Colour::Pink)), + git2::RepositoryState::CherryPick | git2::RepositoryState::CherryPickSequence => Some(PromptComponent::bold("cherry pick", Colour::Pink)), + git2::RepositoryState::Rebase | git2::RepositoryState::RebaseInteractive | git2::RepositoryState::RebaseMerge => Some(PromptComponent::bold("rebase", Colour::Pink)), + _ => None, + }; + + if let Some(action) = action { + components.push(PromptComponent::unstyled("performing a")); + components.push(action); + } + + if staged { + components.push(PromptComponent::bold("+", Colour::Green)); + } + + if unstaged { + components.push(PromptComponent::bold("●", Colour::Red)); + } + }, + None => { }, + } +}