├── fish_theme
├── conf.d
│ ├── fish_greeting.fish
│ ├── fish_title.fish
│ ├── fish_prompt.fish
│ └── fish_right_prompt.fish
├── LICENSE
└── README.md
├── bruh.zsh-theme
└── src
├── main.zig
├── cwd.zig
└── time_format.zig
/fish_theme/conf.d/fish_greeting.fish:
--------------------------------------------------------------------------------
1 | function fish_greeting
2 | end
3 |
--------------------------------------------------------------------------------
/fish_theme/conf.d/fish_title.fish:
--------------------------------------------------------------------------------
1 | function fish_title
2 | # Customize terminal window title
3 | end
4 |
--------------------------------------------------------------------------------
/fish_theme/conf.d/fish_prompt.fish:
--------------------------------------------------------------------------------
1 | function fish_prompt
2 | set -l last_status $status
3 |
4 | echo -en " "
5 |
6 | if test -n "$SSH_CONNECTION"
7 | echo -en (whoami)"@"(hostname)" "
8 | end
9 |
10 | if [ $last_status != 0 ]
11 | set_color red
12 | echo -en "$last_status "
13 | set_color normal
14 | end
15 |
16 | if test -n "$duration"
17 | and test $duration -ne 0
18 | set_color --bold green
19 | echo -en (bruh tf $duration)" "
20 | set_color normal
21 | end
22 |
23 | echo -en
24 |
25 | echo -en (bruh cwd)" "
26 | end
27 |
28 | function fish_command_timer_postexec -e fish_postexec
29 | set -g duration (math -s3 "$CMD_DURATION/1000")
30 | end
31 |
32 | function fish_mode_prompt
33 | switch $fish_bind_mode
34 | case default
35 | set_color --bold blue
36 | echo -ne ' n'
37 | set_color normal
38 | case visual
39 | set_color --bold purple
40 | echo -ne ' v'
41 | set_color normal
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/fish_theme/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Haze Booth
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/fish_theme/conf.d/fish_right_prompt.fish:
--------------------------------------------------------------------------------
1 | function fish_right_prompt
2 | show_git_info
3 | end
4 |
5 | function show_dirty_sign
6 | set -l have_changes (git diff-index --quiet HEAD -- 2>/dev/null)
7 | if [ $status -ne 0 ]
8 | set_color green
9 | echo -ne '+ '
10 | set_color normal
11 | end
12 | end
13 |
14 | function show_branch_name
15 | set -l branch_name (git rev-parse --abbrev-ref HEAD 2>/dev/null)
16 | if test -n $branch_name
17 | echo -ne $branch_name" "
18 | end
19 | end
20 |
21 | function show_hash
22 | set -l hash (git rev-parse --short HEAD 2>/dev/null)
23 | if test -n $hash
24 | set_color green
25 | echo -ne $hash" "
26 | set_color normal
27 | end
28 | end
29 |
30 | function show_last_commit_age
31 | set -l last_commit_age (git show -s --format=%ct 2>/dev/null)
32 | set -l git_show_status $status
33 | if test -n $last_commit_age
34 | and [ $git_show_status -ne 128 ]
35 | echo -ne (bruh lca (date +%s) $last_commit_age)" "
36 | end
37 | end
38 |
39 | function show_git_info
40 | set -l git_root (git rev-parse --show-toplevel -q 2>/dev/null)
41 | if [ $status -eq 0 ]
42 | show_dirty_sign
43 | show_branch_name
44 | show_hash
45 | show_last_commit_age
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/fish_theme/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | #### bruh
4 | > A theme for [Oh My Fish][omf-link].
5 |
6 | [](/LICENSE)
7 | [](https://fishshell.com)
8 | [](https://www.github.com/oh-my-fish/oh-my-fish)
9 |
10 |
11 |
12 |
13 | ## Install
14 |
15 | ```fish
16 | $ omf install bruh
17 | ```
18 |
19 |
20 | ## Features
21 |
22 | * Lorem ipsum dolor sit amet.
23 | * Consectetur adipisicing elit.
24 |
25 |
26 | ## Screenshot
27 |
28 |
29 |
30 |
31 |
32 |
33 | # License
34 |
35 | [MIT][mit] © [Haze Booth][author] et [al][contributors]
36 |
37 |
38 | [mit]: https://opensource.org/licenses/MIT
39 | [author]: https://github.com/{{USER}}
40 | [contributors]: https://github.com/{{USER}}/theme-bruh/graphs/contributors
41 | [omf-link]: https://www.github.com/oh-my-fish/oh-my-fish
42 |
43 | [license-badge]: https://img.shields.io/badge/license-MIT-007EC7.svg?style=flat-square
44 |
--------------------------------------------------------------------------------
/bruh.zsh-theme:
--------------------------------------------------------------------------------
1 | # bruh theme
2 |
3 | # setup
4 | setopt prompt_subst
5 | hl='%F{green}'
6 | reset='%F{fb_default_code}'
7 |
8 | function preexec() {
9 | before=$EPOCHREALTIME
10 | }
11 |
12 | function precmd() {
13 | git_commit_age=''
14 | if [ -z "$SSH_CONNECTION" ]; then
15 | connection=''
16 | else
17 | user="$(whoami)"
18 | hostname="$(hostname)"
19 | connection="$user@$hostname "
20 | fi
21 | if [ -z "${before}" ]; then
22 | elapsed=''
23 | else
24 | after=$EPOCHREALTIME
25 | raw_elapsed=$(echo "$after-$before" | bc)
26 | elapsed="$hl$(bruh tf $raw_elapsed)$reset "
27 | before=''
28 | fi
29 | git_dirty=''
30 | git_top_level="$(git rev-parse --show-toplevel -q 2>/dev/null)"
31 | if [ $? -eq 0 ]
32 | then
33 | git_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null) "
34 | git_shorthash="$hl$(git rev-parse --short HEAD 2>/dev/null)$reset "
35 | if [ -d "$PWD/.git" ]; then
36 | git_raw_last_commit_age="$(git show -s --format=%ct 2>/dev/null)"
37 | if [ $? -eq 0 ]; then
38 | git_commit_age="$(bruh lca $(date +%s) $git_raw_last_commit_age)"
39 | fi
40 | else
41 | fi
42 |
43 | $(git diff-index --quiet HEAD -- 2>/dev/null)
44 | do_we_have_changes=$?
45 | if [ $do_we_have_changes -ne 0 ]; then
46 | git_dirty="$hl+$reset "
47 | fi
48 | else
49 | git_branch=''
50 | git_shorthash=''
51 | git_commit_age=''
52 | fi
53 | }
54 |
55 | PROMPT=' $connection$elapsed$(bruh cwd) '
56 | RPROMPT='$git_dirty$git_branch$git_shorthash$git_commit_age'
57 |
--------------------------------------------------------------------------------
/src/main.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const cwd = @import("cwd.zig");
3 | const tf = @import("time_format.zig");
4 | pub const lib_log = std.log.scoped(.bruh);
5 |
6 | pub const Config = struct {
7 | dir_cutoff_len: ?usize = cwd.DEFAULT_DIR_CUTOFF_LEN,
8 | early_dir_cutoff_len: ?usize = cwd.DEFAULT_EARLY_DIR_CUTOFF_LEN,
9 | home_dir: ?[]const u8 = null,
10 |
11 | fn fromEnv(env_map: std.BufMap) !Config {
12 | var conf = Config{};
13 | if (env_map.get(cwd.BRUH_DIR_CUTOFF_ENV_KEY)) |cutoff_len| {
14 | conf.dir_cutoff_len = try std.fmt.parseUnsigned(usize, cutoff_len, 10);
15 | }
16 | if (env_map.get(cwd.BRUH_EARLY_DIR_CUTOFF_ENV_KEY)) |cutoff_len| {
17 | conf.early_dir_cutoff_len = try std.fmt.parseUnsigned(usize, cutoff_len, 10);
18 | }
19 | if (env_map.get("HOME")) |home_dir| {
20 | conf.home_dir = home_dir;
21 | }
22 | return conf;
23 | }
24 | };
25 |
26 | pub fn main() !void {
27 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
28 | var allocator = &arena.allocator;
29 |
30 | var env_map = try std.process.getEnvMap(allocator);
31 |
32 | var config = try Config.fromEnv(env_map);
33 |
34 | var arg_iter = std.process.args();
35 | // skip executable
36 | _ = arg_iter.skip();
37 |
38 | if (arg_iter.next(allocator)) |maybe_arg| {
39 | const arg = try maybe_arg;
40 | if (std.mem.eql(u8, arg, "cwd")) {
41 | cwd.printCollapsedWorkingDir(allocator, config);
42 | } else if (std.mem.eql(u8, arg, "lca")) {
43 | const time_a_raw = try arg_iter.next(allocator) orelse {
44 | lib_log.err("No source seconds given to lca", .{});
45 | return;
46 | };
47 | const time_b_raw = try arg_iter.next(allocator) orelse {
48 | lib_log.err("No dest seconds given to lca", .{});
49 | return;
50 | };
51 | const time_a = try std.fmt.parseFloat(f64, time_a_raw);
52 | const time_b = try std.fmt.parseFloat(f64, time_b_raw);
53 | const diff = std.math.absFloat(time_a - time_b);
54 | tf.printFormattedTime(diff);
55 | } else if (std.mem.eql(u8, arg, "tf")) {
56 | if (arg_iter.next(allocator)) |maybe_seconds| {
57 | const seconds_arg = try maybe_seconds;
58 | tf.printFormattedTime(try std.fmt.parseFloat(f64, seconds_arg));
59 | } else {
60 | lib_log.err("No seconds given to time_format", .{});
61 | }
62 | } else lib_log.err("Invalid subcommand '{s}'", .{arg});
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/cwd.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | const bruh = @import("main.zig");
3 |
4 | /// Any paths longer than this variable will be cut off
5 | pub const DEFAULT_DIR_CUTOFF_LEN: usize = 5;
6 | pub const DEFAULT_EARLY_DIR_CUTOFF_LEN: usize = 3;
7 | pub const BRUH_DIR_CUTOFF_ENV_KEY = "BRUH_DIR_CUTOFF_LEN";
8 | pub const BRUH_EARLY_DIR_CUTOFF_ENV_KEY = "BRUH_EARLY_DIR_CUTOFF_LEN";
9 | pub const SEPARATING_CHARS = "-_ ";
10 |
11 | fn createCollapsedWorkingDir(allocator: *std.mem.Allocator, config: bruh.Config) ![]const u8 {
12 | var in_home_dir = false;
13 | var current_working_dir = try std.process.getCwdAlloc(allocator);
14 | if (config.home_dir) |home_dir| {
15 | if (std.mem.startsWith(u8, current_working_dir, home_dir)) {
16 | in_home_dir = true;
17 | current_working_dir = current_working_dir[home_dir.len..];
18 | }
19 | }
20 | var new_working_dir = try std.ArrayList(u8).initCapacity(allocator, current_working_dir.len);
21 | if (in_home_dir) {
22 | try new_working_dir.append('~');
23 | try new_working_dir.append(std.fs.path.sep);
24 | } else try new_working_dir.append('/');
25 | // TODO(haze): this is kinda naive
26 | const num_paths: usize = std.mem.count(u8, current_working_dir, std.fs.path.sep_str);
27 | var path_iterator = std.mem.tokenize(u8, current_working_dir, std.fs.path.sep_str);
28 | var path_idx: usize = 0;
29 | while (path_iterator.next()) |path_segment| {
30 | try appendPathHelper(&new_working_dir, path_idx, num_paths, path_iterator, config, path_segment);
31 | path_idx += 1;
32 | }
33 | return new_working_dir.toOwnedSlice();
34 | }
35 |
36 | fn appendElipses(buf: *std.ArrayList(u8)) !void {
37 | if (@import("builtin").os.tag == .windows)
38 | try buf.appendSlice("...")
39 | else
40 | try buf.appendSlice("…");
41 | }
42 |
43 | fn appendPathHelper(buf: *std.ArrayList(u8), path_idx: usize, total_num_paths: usize, path_iterator: std.mem.TokenIterator(u8), config: bruh.Config, path_segment: []const u8) !void {
44 | const is_last_path = path_iterator.rest().len == 0;
45 | var print_without_truncating = true;
46 | if (!is_last_path) {
47 | var is_early_cutoff = path_idx >= (total_num_paths / 2);
48 | var segment_head: usize = 0;
49 | // how many chars can we add before we need to shorten?
50 | var count: usize = 0;
51 | while (segment_head < path_segment.len) : (segment_head += 1) {
52 | if (config.early_dir_cutoff_len) |cutoff_len| {
53 | if (count > cutoff_len) {
54 | if (is_early_cutoff) {
55 | try buf.appendSlice(path_segment[0..count]);
56 | try appendElipses(buf);
57 | } else try buf.append(path_segment[0]);
58 | print_without_truncating = false;
59 | break;
60 | }
61 | }
62 | // if we find a separating char at our head, reset the count
63 | if (std.mem.indexOfScalar(u8, SEPARATING_CHARS, path_segment[segment_head]) != null) {
64 | count = 0;
65 | continue;
66 | }
67 | count += 1;
68 | }
69 | }
70 | if (print_without_truncating)
71 | try buf.appendSlice(path_segment);
72 | if (!is_last_path)
73 | try buf.append(std.fs.path.sep);
74 | }
75 |
76 | pub fn printCollapsedWorkingDir(allocator: *std.mem.Allocator, config: bruh.Config) void {
77 | const stdOut = std.io.getStdOut().writer();
78 | const shortened_cwd = createCollapsedWorkingDir(allocator, config) catch |shortened_cwd_err| {
79 | stdOut.print("ERR({s})\n", .{@errorName(shortened_cwd_err)}) catch |stdout_err| {
80 | bruh.lib_log.err("Failed to write to stdout: {s}", .{@errorName(stdout_err)});
81 | };
82 | return;
83 | };
84 | stdOut.print("{s}\n", .{shortened_cwd}) catch |stdout_err_prime| {
85 | bruh.lib_log.err("Failed to write to stdout: {s}", .{@errorName(stdout_err_prime)});
86 | };
87 | }
88 |
--------------------------------------------------------------------------------
/src/time_format.zig:
--------------------------------------------------------------------------------
1 | // This file is responsible for creaeting nice looking time formats
2 | const std = @import("std");
3 | const bruh = @import("main.zig");
4 |
5 | // Unsigned Duration type, can only be positive
6 | // 5 seconds - 1 day = 0 nanos
7 | const Duration = struct {
8 | const Unit = enum {
9 | nanos,
10 | micros,
11 | millis,
12 | seconds,
13 | minutes,
14 | hours,
15 | days,
16 |
17 | fn qualifier(self: Unit) []const u8 {
18 | return switch (self) {
19 | .nanos => "ns",
20 | .micros => "µs",
21 | .millis => "ms",
22 | .seconds => "s",
23 | .minutes => "m",
24 | .hours => "hr",
25 | .days => "d",
26 | };
27 | }
28 |
29 | fn toNanos(self: Unit, scalar: f64) f64 {
30 | return switch (self) {
31 | .nanos => scalar,
32 | .micros => scalar * std.time.ns_per_us,
33 | .millis => scalar * std.time.ns_per_ms,
34 | .seconds => scalar * std.time.ns_per_s,
35 | .minutes => scalar * std.time.ns_per_min,
36 | .hours => scalar * std.time.ns_per_hour,
37 | .days => scalar * std.time.ns_per_day,
38 | };
39 | }
40 | };
41 | ns: f64,
42 | largest_unit: Unit,
43 |
44 | fn from(unit: Unit, scalar: f64) Duration {
45 | var dur: Duration = undefined;
46 | dur.ns = unit.toNanos(scalar);
47 | dur.largest_unit = dur.getLargestUnit();
48 | return dur;
49 | }
50 |
51 | fn getLargestUnit(self: Duration) Unit {
52 | if (self.days() >= 1) {
53 | return .days;
54 | } else if (self.hours() >= 1) {
55 | return .hours;
56 | } else if (self.minutes() >= 1) {
57 | return .minutes;
58 | } else if (self.seconds() >= 1) {
59 | return .seconds;
60 | } else if (self.millis() >= 1) {
61 | return .millis;
62 | } else if (self.micros() >= 1) {
63 | return .micros;
64 | } else {
65 | return .nanos;
66 | }
67 | }
68 |
69 | fn nanos(self: Duration) f64 {
70 | return self.ns;
71 | }
72 |
73 | fn micros(self: Duration) f64 {
74 | return self.nanos() / std.time.ns_per_us;
75 | }
76 |
77 | fn millis(self: Duration) f64 {
78 | return self.nanos() / std.time.ns_per_ms;
79 | }
80 |
81 | fn seconds(self: Duration) f64 {
82 | return self.nanos() / std.time.ns_per_s;
83 | }
84 |
85 | fn minutes(self: Duration) f64 {
86 | return self.nanos() / std.time.ns_per_min;
87 | }
88 |
89 | fn hours(self: Duration) f64 {
90 | return self.nanos() / std.time.ns_per_hour;
91 | }
92 |
93 | fn days(self: Duration) f64 {
94 | return self.nanos() / std.time.ns_per_day;
95 | }
96 |
97 | fn as(self: Duration, unit: Unit) f64 {
98 | return switch (unit) {
99 | .nanos => self.ns,
100 | .millis => self.millis(),
101 | .days => self.days(),
102 | .hours => self.hours(),
103 | .minutes => self.minutes(),
104 | .seconds => self.seconds(),
105 | .micros => self.micros(),
106 | };
107 | }
108 |
109 | pub fn format(
110 | self: Duration,
111 | comptime fmt: []const u8,
112 | _: std.fmt.FormatOptions,
113 | out_stream: anytype,
114 | ) !void {
115 | _ = fmt;
116 | // if it has a fractional part, print with up to 2 places of precision
117 | if (std.math.modf(self.as(self.largest_unit)).fpart > 0) {
118 | // if we are seconds and above, print with more precision (we only want 2 for millis and below)
119 | if (@enumToInt(self.largest_unit) > @enumToInt(Unit.seconds)) {
120 | try std.fmt.format(out_stream, "{d:.2}{s}", .{ self.as(self.largest_unit), Unit.qualifier(self.largest_unit) });
121 | } else {
122 | try std.fmt.format(out_stream, "{d:.2}{s}", .{ self.as(self.largest_unit), Unit.qualifier(self.largest_unit) });
123 | }
124 | } else {
125 | // otherwise, print with none
126 | try std.fmt.format(out_stream, "{d}{s}", .{ self.as(self.largest_unit), Unit.qualifier(self.largest_unit) });
127 | }
128 | }
129 | };
130 |
131 | pub fn printFormattedTime(seconds: f64) void {
132 | var stdout = std.io.getStdOut().writer();
133 | stdout.print("{}\n", .{Duration.from(.seconds, seconds)}) catch |stdout_err| {
134 | bruh.lib_log.err("Failed to print calculated formatted time: {s}", .{@errorName(stdout_err)});
135 | };
136 | }
137 |
--------------------------------------------------------------------------------