+2
-2
fish/config.fish
+2
-2
fish/config.fish
···
60
61
# ghcup-env
62
set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX $HOME
63
-
set -gx PATH $HOME/.cabal/bin $PATH /Users/boltless/.ghcup/bin
64
65
# pnpm
66
-
set -gx PNPM_HOME "/Users/boltless/Library/pnpm"
67
if not string match -q -- $PNPM_HOME $PATH
68
set -gx PATH "$PNPM_HOME" $PATH
69
end
···
60
61
# ghcup-env
62
set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX $HOME
63
+
set -gx PATH $HOME/.cabal/bin $PATH $HOME/.ghcup/bin
64
65
# pnpm
66
+
set -gx PNPM_HOME "$HOME/Library/pnpm"
67
if not string match -q -- $PNPM_HOME $PATH
68
set -gx PATH "$PNPM_HOME" $PATH
69
end
+1
-1
ghostty/config
+1
-1
ghostty/config
+4
-4
git/config
+4
-4
git/config
···
2
defaultBranch = main
3
[user]
4
name = Seongmin Lee
5
-
email = boltlessengineer@proton.me
6
signingkey = ~/.ssh/git-sign-key.pub
7
[commit]
8
gpgsign = true
9
[core]
10
-
excludesFile = ~/.config/git/ignore
11
[gpg]
12
format = ssh
13
[gpg "ssh"]
···
15
[url "git@github.com:"]
16
insteadOf = https://github.com/
17
[url "git@github.com:boltlessengineer/"]
18
-
insteadOf = self:
19
[rerere]
20
enabled = true
21
[alias]
22
-
st = status
23
lg = log --branches --remotes --tags --graph --decorate
24
ll = log --branches --remotes --tags --graph --decorate --oneline
25
find = !sh -c 'git-repo-search \"$@\" | fzf | awk \"{print \\$NF}\" --
···
2
defaultBranch = main
3
[user]
4
name = Seongmin Lee
5
+
email = git@boltless.me
6
signingkey = ~/.ssh/git-sign-key.pub
7
[commit]
8
gpgsign = true
9
[core]
10
+
excludesFile = ~/.config/git/ignore
11
[gpg]
12
format = ssh
13
[gpg "ssh"]
···
15
[url "git@github.com:"]
16
insteadOf = https://github.com/
17
[url "git@github.com:boltlessengineer/"]
18
+
insteadOf = self:
19
[rerere]
20
enabled = true
21
[alias]
22
+
st = status
23
lg = log --branches --remotes --tags --graph --decorate
24
ll = log --branches --remotes --tags --graph --decorate --oneline
25
find = !sh -c 'git-repo-search \"$@\" | fzf | awk \"{print \\$NF}\" --
+1
git/ignore
+1
git/ignore
+18
-2
jj/config.toml
+18
-2
jj/config.toml
···
1
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
2
3
[ui]
4
-
default-command = ["log", "--no-pager"]
5
should-sign-off = true
6
# diff-editor = ["nvim", "-c", "DiffEditor $left $right $output"]
7
# merge-editor = "vimdiff"
8
# conflict-marker-style = "git"
9
10
[user]
11
name = "Seongmin Lee"
12
-
email = "boltlessengineer@proton.me"
13
14
[signing]
15
behavior = "own"
···
18
19
[git]
20
write-change-id-header = true
21
22
[templates]
23
draft_commit_description = '''
···
1
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
2
3
+
[aliases]
4
+
su = ["squash"]
5
+
tug = ["bookmark", "move", "--from", "heads(::@- & bookmarks())", "--to", "@-"]
6
+
# mega-merge pull
7
+
mp = ["rebase", "-b", "MM", "-d", "heads(MM- | trunk())"]
8
+
9
[ui]
10
+
default-command = ["log", "--no-pager", "-r", "@ | coalesce(mega_merge(), default()) | trunk()"]
11
should-sign-off = true
12
+
diff-formatter = ["difft", "--color=always", "--syntax-highlight=off", "$left", "$right"]
13
# diff-editor = ["nvim", "-c", "DiffEditor $left $right $output"]
14
# merge-editor = "vimdiff"
15
# conflict-marker-style = "git"
16
17
[user]
18
name = "Seongmin Lee"
19
+
email = "git@boltless.me"
20
21
[signing]
22
behavior = "own"
···
25
26
[git]
27
write-change-id-header = true
28
+
private-commits = "description(glob:'wip:*') | description(glob:'private:*')"
29
+
30
+
[revset-aliases]
31
+
'MM' = "description('[mega-merge]')"
32
+
'FP' = "fork_point(MM-)"
33
+
'wip' = "description(glob:'wip:*')"
34
+
'default()' = "@ | ancestors(immutable_heads().., 2) | trunk()"
35
+
'show(x)' = "@ | ancestors(..(x::) ~ immutable(), 2) | trunk()"
36
+
'mega_merge()' = "ancestors(..(MM::) ~ immutable(), 2)"
37
38
[templates]
39
draft_commit_description = '''
+48
-2
nix/darwin.nix
+48
-2
nix/darwin.nix
···
3
# $ nix-env -qaP | grep wget
4
environment.systemPackages =
5
[
6
-
pkgs.brave
7
pkgs.fzf
8
pkgs.tmux
9
pkgs.tree
10
pkgs.vim
11
# I don't want to touch this due to current neovim settings.
12
# remove it when lux.nvim is out
13
(pkgs-unstable.lua5_1.withPackages (ps: with ps; [luarocks]))
14
];
15
16
# Necessary for using flakes on this system.
17
nix.settings.experimental-features = "nix-command flakes";
18
# Enable rosetta binaries
19
nix.settings.extra-platforms = "x86_64-darwin aarch64-darwin";
20
21
# Enable alternative shell support in nix-darwin.
22
# programs.fish.enable = true;
23
···
45
autohide-time-modifier = 0.6;
46
mru-spaces = false;
47
show-recents = false;
48
# Disable bottom-right corner action
49
wvous-br-corner = 1;
50
# persistent-apps = [];
···
58
KeyRepeat = 1;
59
# Use f1, f2, etc. keys as standard function keys.
60
"com.apple.keyboard.fnState" = true;
61
};
62
63
# reference https://medium.com/@zmre/nix-darwin-quick-tip-activate-your-preferences-f69942a93236
···
165
enable = true;
166
casks = [
167
"discord"
168
"ghostty"
169
"raycast"
170
# there are also pkgs.sensible-side-buttons (original version) which is unfree license
···
173
"sanesidebuttons"
174
];
175
masApps = {
176
Things3 = 904280696;
177
-
Pages = 409201541;
178
};
179
};
180
}
···
3
# $ nix-env -qaP | grep wget
4
environment.systemPackages =
5
[
6
pkgs.fzf
7
pkgs.tmux
8
pkgs.tree
9
pkgs.vim
10
+
pkgs.git
11
+
pkgs.ollama
12
+
# used for remote linux deployment
13
+
pkgs.nixos-rebuild
14
# I don't want to touch this due to current neovim settings.
15
# remove it when lux.nvim is out
16
(pkgs-unstable.lua5_1.withPackages (ps: with ps; [luarocks]))
17
];
18
19
+
launchd.user.agents = {
20
+
# reference:
21
+
# - https://www.danielcorin.com/til/nix-darwin/launch-agents/
22
+
# - https://medium.com/@anand34577/setting-up-ollama-as-a-background-service-on-macos-66f7492b5cc8
23
+
ollama-serve = {
24
+
environment = {
25
+
OLLAMA_HOST = "0.0.0.0";
26
+
};
27
+
serviceConfig = {
28
+
Label = "com.ollama.serve";
29
+
ProgramArguments = [ "${pkgs.ollama}/bin/ollama" "serve" ];
30
+
KeepAlive = true;
31
+
RunAtLoad = true;
32
+
# TODO: ensure /var/log/ollama exist
33
+
# StandardOutPath = "/var/log/ollama/stdout.log";
34
+
# StandardErrorPath = "/var/log/ollama/stderr.log";
35
+
StandardOutPath = "/tmp/ollama_boltless.log";
36
+
StandardErrorPath = "/tmp/ollama_boltless.err";
37
+
};
38
+
};
39
+
};
40
+
41
# Necessary for using flakes on this system.
42
nix.settings.experimental-features = "nix-command flakes";
43
# Enable rosetta binaries
44
nix.settings.extra-platforms = "x86_64-darwin aarch64-darwin";
45
46
+
nix.settings.trusted-users = [ "@admin" ];
47
+
nix.settings.extra-trusted-users = [ "boltless" ];
48
+
49
+
nix.linux-builder = {
50
+
enable = true;
51
+
maxJobs = 4;
52
+
config.virtualisation = {
53
+
cores = 6;
54
+
};
55
+
};
56
+
57
# Enable alternative shell support in nix-darwin.
58
# programs.fish.enable = true;
59
···
81
autohide-time-modifier = 0.6;
82
mru-spaces = false;
83
show-recents = false;
84
+
orientation = "left";
85
# Disable bottom-right corner action
86
wvous-br-corner = 1;
87
# persistent-apps = [];
···
95
KeyRepeat = 1;
96
# Use f1, f2, etc. keys as standard function keys.
97
"com.apple.keyboard.fnState" = true;
98
+
};
99
+
100
+
system.defaults.controlcenter = {
101
+
NowPlaying = false;
102
+
Sound = true;
103
};
104
105
# reference https://medium.com/@zmre/nix-darwin-quick-tip-activate-your-preferences-f69942a93236
···
207
enable = true;
208
casks = [
209
"discord"
210
+
"docker"
211
"ghostty"
212
"raycast"
213
# there are also pkgs.sensible-side-buttons (original version) which is unfree license
···
216
"sanesidebuttons"
217
];
218
masApps = {
219
+
Numbers = 409203825;
220
+
Pages = 409201541;
221
+
Tampermonkey = 6738342400;
222
Things3 = 904280696;
223
+
# Xcode = 497799835;
224
};
225
};
226
}
+9
-9
nix/flake.lock
+9
-9
nix/flake.lock
···
7
]
8
},
9
"locked": {
10
-
"lastModified": 1748373722,
11
-
"narHash": "sha256-qi6aDGP2W6GyAUNEhg+slQWEpUiJ8LNIrQkmxHpzadI=",
12
"owner": "nix-darwin",
13
"repo": "nix-darwin",
14
-
"rev": "75b99daa12b1fffd646d6c3cf13b06f1fa5cef63",
15
"type": "github"
16
},
17
"original": {
···
23
},
24
"nixpkgs": {
25
"locked": {
26
-
"lastModified": 1748784767,
27
-
"narHash": "sha256-XDFqk5jdf//OwpbftRK/8tmJHVjTFhjoHOPoIS9n9Gg=",
28
"owner": "NixOS",
29
"repo": "nixpkgs",
30
-
"rev": "e8c6caf5a492a7757d161c29d80c308462e896d3",
31
"type": "github"
32
},
33
"original": {
···
39
},
40
"nixpkgs-unstable": {
41
"locked": {
42
-
"lastModified": 1749369410,
43
-
"narHash": "sha256-P1eYm8bewiyWg3QaPCHrOp6iWg/7ESi/aGHT4yilyNo=",
44
"owner": "NixOS",
45
"repo": "nixpkgs",
46
-
"rev": "8207ad0d501dd3590e0e81a7c56b386a5c4342c9",
47
"type": "github"
48
},
49
"original": {
···
7
]
8
},
9
"locked": {
10
+
"lastModified": 1749744770,
11
+
"narHash": "sha256-MEM9XXHgBF/Cyv1RES1t6gqAX7/tvayBC1r/KPyK1ls=",
12
"owner": "nix-darwin",
13
"repo": "nix-darwin",
14
+
"rev": "536f951efb1ccda9b968e3c9dee39fbeb6d3fdeb",
15
"type": "github"
16
},
17
"original": {
···
23
},
24
"nixpkgs": {
25
"locked": {
26
+
"lastModified": 1756659129,
27
+
"narHash": "sha256-ZFCE+pWMvOUoqMSWXyFZIjsaIH53mkGMtY9RHrAQhJw=",
28
"owner": "NixOS",
29
"repo": "nixpkgs",
30
+
"rev": "11e13914dbd6715d20bbe0637e32f3feefb5ce0e",
31
"type": "github"
32
},
33
"original": {
···
39
},
40
"nixpkgs-unstable": {
41
"locked": {
42
+
"lastModified": 1756696532,
43
+
"narHash": "sha256-6FWagzm0b7I/IGigOv9pr6LL7NQ86mextfE8g8Q6HBg=",
44
"owner": "NixOS",
45
"repo": "nixpkgs",
46
+
"rev": "58dcbf1ec551914c3756c267b8b9c8c86baa1b2f",
47
"type": "github"
48
},
49
"original": {
+61
-5
nix/flake.nix
+61
-5
nix/flake.nix
···
19
system = "aarch64-darwin";
20
pkgs = import nixpkgs {
21
inherit system;
22
-
overlays = [ (import ./overlays/jpm.nix) ];
23
};
24
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
25
in
···
28
# $ darwin-rebuild build --flake ~/.config/nix
29
# $ darwin-rebuild switch --flake ~/.config/nix
30
darwinConfigurations."boltless-MacBook-Air" = nix-darwin.lib.darwinSystem {
31
modules = [
32
./darwin.nix
33
];
···
41
packages.${system}.default = pkgs.buildEnv {
42
name = "my-env";
43
paths = [
44
-
pkgs.anki-bin
45
pkgs.bacon
46
pkgs.btop
47
pkgs.dust
48
pkgs.eza
49
pkgs.fd
50
pkgs.gh
51
pkgs.hello
52
-
pkgs.iina
53
pkgs.janet
54
pkgs.jpm
55
pkgs.meld
56
pkgs.nodejs_24
57
pkgs.ripgrep
58
pkgs.sumneko-lua-language-server
59
pkgs.unixtools.watch
60
-
pkgs.qmk
61
pkgs-unstable.jujutsu
62
pkgs-unstable.neovim
63
# TODO: automate this with nix:
64
# $ ln -sfn ~/.nix-profile/Applications ~/Applications/Nix\ User\ Apps
65
-
];
66
};
67
};
68
}
69
# vim: et:ts=2
···
19
system = "aarch64-darwin";
20
pkgs = import nixpkgs {
21
inherit system;
22
+
config.allowUnfree = true;
23
+
overlays = [
24
+
(import ./overlays/jpm.nix)
25
+
(import ./overlays/bleve.nix)
26
+
(final: prev: {
27
+
kotlin-lsp = final.callPackage ./overlays/kotlin-lsp.nix {};
28
+
})
29
+
];
30
};
31
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
32
in
···
35
# $ darwin-rebuild build --flake ~/.config/nix
36
# $ darwin-rebuild switch --flake ~/.config/nix
37
darwinConfigurations."boltless-MacBook-Air" = nix-darwin.lib.darwinSystem {
38
+
modules = [
39
+
./darwin.nix
40
+
];
41
+
specialArgs = {
42
+
inherit self pkgs-unstable;
43
+
};
44
+
};
45
+
darwinConfigurations."boltless-Studio" = nix-darwin.lib.darwinSystem {
46
modules = [
47
./darwin.nix
48
];
···
56
packages.${system}.default = pkgs.buildEnv {
57
name = "my-env";
58
paths = [
59
pkgs.bacon
60
+
pkgs.bleve-cli
61
pkgs.btop
62
+
pkgs.difftastic
63
+
pkgs.direnv
64
pkgs.dust
65
pkgs.eza
66
pkgs.fd
67
+
pkgs.fzf
68
pkgs.gh
69
+
pkgs.git
70
+
pkgs.go
71
+
pkgs.gopls
72
pkgs.hello
73
pkgs.janet
74
+
pkgs.yarn
75
pkgs.jpm
76
+
pkgs.kotlin-lsp
77
+
pkgs.ktfmt
78
pkgs.meld
79
+
pkgs.mprocs
80
+
pkgs.nil
81
+
pkgs.nixfmt-classic
82
pkgs.nodejs_24
83
+
pkgs.pandoc
84
+
pkgs.qmk
85
pkgs.ripgrep
86
+
pkgs.stack
87
pkgs.sumneko-lua-language-server
88
+
pkgs.tmux
89
+
pkgs.tree
90
+
pkgs.typescript-language-server
91
+
pkgs.typst
92
pkgs.unixtools.watch
93
+
pkgs.vim
94
+
pkgs.zellij
95
pkgs-unstable.jujutsu
96
pkgs-unstable.neovim
97
+
(pkgs-unstable.lua5_1.withPackages (ps: with ps; [luarocks]))
98
+
(pkgs.buildEnv {
99
+
name = "mailing";
100
+
paths = [
101
+
pkgs.neomutt
102
+
# pkgs.mutt-wizard
103
+
# pkgs.isync
104
+
# pkgs.msmtp
105
+
# pkgs.pass
106
+
# pkgs.cacert
107
+
# pkgs.gettext
108
+
];
109
+
})
110
+
] ++ (if pkgs.stdenv.isDarwin then [
111
# TODO: automate this with nix:
112
# $ ln -sfn ~/.nix-profile/Applications ~/Applications/Nix\ User\ Apps
113
+
pkgs.anki-bin
114
+
pkgs.brave
115
+
pkgs.iina
116
+
pkgs.macmon
117
+
pkgs.docker
118
+
pkgs.slack
119
+
] else []);
120
};
121
+
# This can be just `imports = [ ./templates/templates.nix ]` when using flake-parts
122
+
templates = ((import ./templates/templates.nix) { inherit inputs; }).flake.templates;
123
};
124
}
125
# vim: et:ts=2
+25
nix/overlays/bleve.nix
+25
nix/overlays/bleve.nix
···
···
1
+
self: super: {
2
+
bleve-cli = super.buildGoModule {
3
+
pname = "bleve-cli";
4
+
version = "2.5.2";
5
+
6
+
src = super.fetchFromGitHub {
7
+
owner = "blevesearch";
8
+
repo = "bleve";
9
+
rev = "v2.5.2";
10
+
sha256 = "sha256-QycDP5IRuZsLo0cDbsQoX4Qb4yjsV3PDns96+dMajoE=";
11
+
};
12
+
13
+
subPackages = [ "cmd/bleve" ];
14
+
15
+
vendorHash = "sha256-RRIDViTRRKSN6QJhUSvTCoXJ2R5E3DWT4ygk9U/Q1dA=";
16
+
17
+
doCheck = false;
18
+
19
+
meta = with super.lib; {
20
+
description = "A modern text/numeric/geo-spatial/vector indexing library for go";
21
+
homepage = "https://github.com/blevesearch/bleve";
22
+
license = licenses.asl20;
23
+
};
24
+
};
25
+
}
+56
nix/overlays/kotlin-lsp.nix
+56
nix/overlays/kotlin-lsp.nix
···
···
1
+
{
2
+
lib,
3
+
stdenv,
4
+
fetchzip,
5
+
openjdk,
6
+
makeWrapper,
7
+
}:
8
+
9
+
stdenv.mkDerivation rec {
10
+
pname = "kotlin-lsp";
11
+
version = "0.253.10629";
12
+
src = fetchzip {
13
+
stripRoot = false;
14
+
url = "https://download-cdn.jetbrains.com/kotlin-lsp/${version}/kotlin-${version}.zip";
15
+
hash = "sha256-LCLGo3Q8/4TYI7z50UdXAbtPNgzFYtmUY/kzo2JCln0=";
16
+
};
17
+
18
+
dontBuild = true;
19
+
20
+
installPhase = ''
21
+
runHook preInstall
22
+
23
+
mkdir -p $out/lib
24
+
mkdir -p $out/native
25
+
mkdir -p $out/bin
26
+
cp -r lib/* $out/lib
27
+
cp -r native/* $out/native
28
+
chmod +x kotlin-lsp.sh
29
+
cp "kotlin-lsp.sh" "$out/kotlin-lsp.sh"
30
+
ln -s $out/kotlin-lsp.sh $out/bin/kotlin-lsp
31
+
32
+
runHook postInstall
33
+
'';
34
+
35
+
nativeBuildInputs = [
36
+
makeWrapper
37
+
];
38
+
39
+
postFixup = ''
40
+
wrapProgram "$out/bin/kotlin-lsp" --set JAVA_HOME ${openjdk} --prefix PATH : ${
41
+
lib.strings.makeBinPath [
42
+
openjdk
43
+
]
44
+
}
45
+
'';
46
+
47
+
meta = {
48
+
description = "LSP implementation for Kotlin code completion, linting";
49
+
homepage = "https://github.com/Kotlin/kotlin-lsp";
50
+
changelog = "https://github.com/Kotlin/kotlin-lsp/blob/kotlin-lsp/v${version}/RELEASES.md";
51
+
license = lib.licenses.asl20;
52
+
platforms = lib.platforms.unix;
53
+
sourceProvenance = [ lib.sourceTypes.binaryBytecode ];
54
+
mainProgram = "kotlin-lsp";
55
+
};
56
+
}
+48
nix/templates/python/flake.nix
+48
nix/templates/python/flake.nix
···
···
1
+
{
2
+
description = "python flake";
3
+
inputs = {
4
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
5
+
flake-parts.url = "github:hercules-ci/flake-parts";
6
+
};
7
+
outputs = inputs @ { flake-parts, ... }:
8
+
flake-parts.lib.mkFlake { inherit inputs; } {
9
+
systems = [
10
+
"x86_64-linux"
11
+
"aarch64-linux"
12
+
"aarch64-darwin"
13
+
];
14
+
perSystem = { pkgs, system, ... }: let
15
+
commonPackages = [
16
+
pkgs.python313Full
17
+
pkgs.pyright
18
+
pkgs.uv
19
+
pkgs.curl
20
+
pkgs.wget
21
+
pkgs.cmake
22
+
] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
23
+
pkgs.gcc
24
+
pkgs.stdenv.cc.cc.lib
25
+
];
26
+
in {
27
+
devShells.default = pkgs.mkShell {
28
+
buildInputs = commonPackages;
29
+
shellHook = ''
30
+
# Set up the Python virtual environment with uv
31
+
test -d .venv || ${pkgs.uv}/bin/uv venv .venv
32
+
export VIRTUAL_ENV="$(pwd)/.venv"
33
+
export PATH="$VIRTUAL_ENV/bin:$PATH"
34
+
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath commonPackages}:$LD_LIBRARY_PATH
35
+
36
+
source "$VIRTUAL_ENV/bin/activate"
37
+
38
+
if uv sync --frozen; then
39
+
package_count=$(uv pip list --format=freeze | wc -l)
40
+
echo "Done. $package_count pip packages installed."
41
+
else
42
+
echo "Warning: An error occurred during uv sync."
43
+
fi
44
+
'';
45
+
};
46
+
};
47
+
};
48
+
}
+18
nix/templates/templates.nix
+18
nix/templates/templates.nix
···
···
1
+
{ inputs, ... }:
2
+
let templatesDir = ./.;
3
+
in {
4
+
flake.templates = let
5
+
mkTemplate = name: val:
6
+
let
7
+
path = templatesDir + "/${name}";
8
+
flake = import (path + "/flake.nix");
9
+
in {
10
+
inherit path;
11
+
description = flake.description;
12
+
};
13
+
templateEntities = let
14
+
op = _: val: val == "directory";
15
+
as = builtins.readDir templatesDir;
16
+
in inputs.nixpkgs.lib.filterAttrs op as;
17
+
in builtins.mapAttrs mkTemplate templateEntities;
18
+
}
+7
-2
tmux/.tmux.conf
+7
-2
tmux/.tmux.conf
···
26
bind-key > resize-pane -R 5
27
bind-key < resize-pane -L 5
28
bind-key o switch-client -l
29
30
# bind ctrl-ed version of other mappings
31
# e.g. `n` and `p` for navigating windows
32
33
-
bind-key r source-file ~/.tmux.conf
34
35
set-window-option -g mode-keys vi
36
bind-key -T copy-mode-vi v send-keys -X begin-selection
37
bind-key -T copy-mode-vi V send-keys -X select-line
38
-
bind-key -T copy-mode-vi y send-keys -X copy-selection
39
bind-key -T copy-mode-vi r send-keys -X rectangle-toggle
40
bind-key -T copy-mode-vi i send-keys -X cancel
41
bind-key -T copy-mode-vi C-c send-keys -X cancel
42
43
bind-key C-e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer"
44
bind-key e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer"
···
26
bind-key > resize-pane -R 5
27
bind-key < resize-pane -L 5
28
bind-key o switch-client -l
29
+
bind-key C-n next-window
30
+
bind-key C-p previous-window
31
32
# bind ctrl-ed version of other mappings
33
# e.g. `n` and `p` for navigating windows
34
35
+
bind-key r source-file ~/.tmux.conf \; display-message "sourced"
36
37
set-window-option -g mode-keys vi
38
bind-key -T copy-mode-vi v send-keys -X begin-selection
39
bind-key -T copy-mode-vi V send-keys -X select-line
40
+
# bind-key -T copy-mode-vi y send-keys -X copy-selection
41
+
bind-key -T copy-mode-vi y send-keys -X copy-pipe "pbcopy"
42
bind-key -T copy-mode-vi r send-keys -X rectangle-toggle
43
bind-key -T copy-mode-vi i send-keys -X cancel
44
bind-key -T copy-mode-vi C-c send-keys -X cancel
45
46
bind-key C-e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer"
47
bind-key e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer"
48
+
bind-key C-i switch-client -l
49
+
bind-key i switch-client -l
+10
-2
zsh/.zprofile
+10
-2
zsh/.zprofile
···
4
eval "$(/opt/homebrew/bin/brew shellenv)"
5
6
# Rust
7
-
. "$HOME/.cargo/env"
8
9
# bob-nvim
10
export PATH=$HOME/.local/share/bob/nvim-bin:$PATH
···
25
# HACK: python binary path. honestly I don't like this hard-coded method
26
export PATH="$HOME/Library/Python/3.9/bin:$PATH"
27
28
29
30
################ BOTTOM ################
31
# load .zshrc when interactive (ssh)
32
-
[[ -o interactive ]] && source ~/.zshrc
···
4
eval "$(/opt/homebrew/bin/brew shellenv)"
5
6
# Rust
7
+
if [ -f "$HOME/.cargo/env" ]; then
8
+
. "$HOME/.cargo/env"
9
+
fi
10
11
# bob-nvim
12
export PATH=$HOME/.local/share/bob/nvim-bin:$PATH
···
27
# HACK: python binary path. honestly I don't like this hard-coded method
28
export PATH="$HOME/Library/Python/3.9/bin:$PATH"
29
30
+
export PATH="$HOME/dot/bin:$PATH"
31
+
32
+
# android development setup
33
+
# https://developer.android.com/tools/variables
34
+
export ANDROID_HOME=$HOME/Library/Android/sdk
35
+
export PATH="$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools"
36
37
38
################ BOTTOM ################
39
# load .zshrc when interactive (ssh)
40
+
# [[ -o interactive ]] && source ~/.zshrc
+17
-4
zsh/.zshrc
+17
-4
zsh/.zshrc
···
7
alias gs="git status"
8
alias ta="tmux attach"
9
alias nn="NVIM_APPNAME=nativevim nvim"
10
11
## Custom prompt
12
-
export PROMPT='%F{green}%n@%m %F{blue}%~%f %# '
13
-
14
-
## Custom prompt (with asychronous rich data rendering)
15
-
source "$HOME/projects/dotfiles/zsh/prompt.zsh"
16
17
## Editor
18
export EDITOR="$(command -v nvim || command -v vim)"
···
24
zle -N edit-command-line
25
bindkey '^Xe' edit-command-line
26
bindkey '^X^E' edit-command-line
···
7
alias gs="git status"
8
alias ta="tmux attach"
9
alias nn="NVIM_APPNAME=nativevim nvim"
10
+
alias nd="nix develop -c zsh -l"
11
+
alias zj="zellij"
12
13
## Custom prompt
14
+
setopt prompt_subst
15
+
export PS1='%F{green}%n@%m %F{blue}%~%f${IN_NIX_SHELL:+ nix:($IN_NIX_SHELL)} %# '
16
17
## Editor
18
export EDITOR="$(command -v nvim || command -v vim)"
···
24
zle -N edit-command-line
25
bindkey '^Xe' edit-command-line
26
bindkey '^X^E' edit-command-line
27
+
28
+
if [[ -n "$IN_NIX_SHELL" ]]; then
29
+
export SHELL=$(which zsh)
30
+
fi
31
+
32
+
eval "$(direnv hook zsh)"
33
+
34
+
# https://github.com/nix-community/home-manager/issues/2562
35
+
# TODO: load nix profile shell functions
36
+
37
+
# Added by LM Studio CLI (lms)
38
+
export PATH="$PATH:$HOME/.lmstudio/bin"
39
+
# End of LM Studio CLI section
-672
zsh/async.zsh
-672
zsh/async.zsh
···
1
-
#!/usr/bin/env zsh
2
-
3
-
#
4
-
# zsh-async
5
-
#
6
-
# version: v1.8.6
7
-
# author: Mathias Fredriksson
8
-
# url: https://github.com/mafredri/zsh-async
9
-
#
10
-
11
-
typeset -g ASYNC_VERSION=1.8.6
12
-
# Produce debug output from zsh-async when set to 1.
13
-
typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0}
14
-
15
-
# Execute commands that can manipulate the environment inside the async worker. Return output via callback.
16
-
_async_eval() {
17
-
local ASYNC_JOB_NAME
18
-
# Rename job to _async_eval and redirect all eval output to cat running
19
-
# in _async_job. Here, stdout and stderr are not separated for
20
-
# simplicity, this could be improved in the future.
21
-
{
22
-
eval "$@"
23
-
} &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'command -p cat')
24
-
}
25
-
26
-
# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time
27
-
_async_job() {
28
-
# Disable xtrace as it would mangle the output.
29
-
setopt localoptions noxtrace
30
-
31
-
# Store start time for job.
32
-
float -F duration=$EPOCHREALTIME
33
-
34
-
# Run the command and capture both stdout (`eval`) and stderr (`cat`) in
35
-
# separate subshells. When the command is complete, we grab write lock
36
-
# (mutex token) and output everything except stderr inside the command
37
-
# block, after the command block has completed, the stdin for `cat` is
38
-
# closed, causing stderr to be appended with a $'\0' at the end to mark the
39
-
# end of output from this job.
40
-
local jobname=${ASYNC_JOB_NAME:-$1} out
41
-
out="$(
42
-
local stdout stderr ret tok
43
-
{
44
-
stdout=$(eval "$@")
45
-
ret=$?
46
-
duration=$(( EPOCHREALTIME - duration )) # Calculate duration.
47
-
48
-
print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration
49
-
} 2> >(stderr=$(command -p cat) && print -r -n - " "${(q)stderr}$'\0')
50
-
)"
51
-
if [[ $out != $'\0'*$'\0' ]]; then
52
-
# Corrupted output (aborted job?), skipping.
53
-
return
54
-
fi
55
-
56
-
# Grab mutex lock, stalls until token is available.
57
-
read -r -k 1 -p tok || return 1
58
-
59
-
# Return output (<job_name> <return_code> <stdout> <duration> <stderr>).
60
-
print -r -n - "$out"
61
-
62
-
# Unlock mutex by inserting a token.
63
-
print -n -p $tok
64
-
}
65
-
66
-
# The background worker manages all tasks and runs them without interfering with other processes
67
-
_async_worker() {
68
-
# Reset all options to defaults inside async worker.
69
-
emulate -R zsh
70
-
71
-
# Make sure monitor is unset to avoid printing the
72
-
# pids of child processes.
73
-
unsetopt monitor
74
-
75
-
# Redirect stderr to `/dev/null` in case unforseen errors produced by the
76
-
# worker. For example: `fork failed: resource temporarily unavailable`.
77
-
# Some older versions of zsh might also print malloc errors (know to happen
78
-
# on at least zsh 5.0.2 and 5.0.8) likely due to kill signals.
79
-
exec 2>/dev/null
80
-
81
-
# When a zpty is deleted (using -d) all the zpty instances created before
82
-
# the one being deleted receive a SIGHUP, unless we catch it, the async
83
-
# worker would simply exit (stop working) even though visible in the list
84
-
# of zpty's (zpty -L). This has been fixed around the time of Zsh 5.4
85
-
# (not released).
86
-
if ! is-at-least 5.4.1; then
87
-
TRAPHUP() {
88
-
return 0 # Return 0, indicating signal was handled.
89
-
}
90
-
fi
91
-
92
-
local -A storage
93
-
local unique=0
94
-
local notify_parent=0
95
-
local parent_pid=0
96
-
local coproc_pid=0
97
-
local processing=0
98
-
99
-
local -a zsh_hooks zsh_hook_functions
100
-
zsh_hooks=(chpwd periodic precmd preexec zshexit zshaddhistory)
101
-
zsh_hook_functions=(${^zsh_hooks}_functions)
102
-
unfunction $zsh_hooks &>/dev/null # Deactivate all zsh hooks inside the worker.
103
-
unset $zsh_hook_functions # And hooks with registered functions.
104
-
unset zsh_hooks zsh_hook_functions # Cleanup.
105
-
106
-
close_idle_coproc() {
107
-
local -a pids
108
-
pids=(${${(v)jobstates##*:*:}%\=*})
109
-
110
-
# If coproc (cat) is the only child running, we close it to avoid
111
-
# leaving it running indefinitely and cluttering the process tree.
112
-
if (( ! processing )) && [[ $#pids = 1 ]] && [[ $coproc_pid = $pids[1] ]]; then
113
-
coproc :
114
-
coproc_pid=0
115
-
fi
116
-
}
117
-
118
-
child_exit() {
119
-
close_idle_coproc
120
-
121
-
# On older version of zsh (pre 5.2) we notify the parent through a
122
-
# SIGWINCH signal because `zpty` did not return a file descriptor (fd)
123
-
# prior to that.
124
-
if (( notify_parent )); then
125
-
# We use SIGWINCH for compatibility with older versions of zsh
126
-
# (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could
127
-
# cause a deadlock in the shell under certain circumstances.
128
-
kill -WINCH $parent_pid
129
-
fi
130
-
}
131
-
132
-
# Register a SIGCHLD trap to handle the completion of child processes.
133
-
trap child_exit CHLD
134
-
135
-
# Process option parameters passed to worker.
136
-
while getopts "np:uz" opt; do
137
-
case $opt in
138
-
n) notify_parent=1;;
139
-
p) parent_pid=$OPTARG;;
140
-
u) unique=1;;
141
-
z) notify_parent=0;; # Uses ZLE watcher instead.
142
-
esac
143
-
done
144
-
145
-
# Terminate all running jobs, note that this function does not
146
-
# reinstall the child trap.
147
-
terminate_jobs() {
148
-
trap - CHLD # Ignore child exits during kill.
149
-
coproc : # Quit coproc.
150
-
coproc_pid=0 # Reset pid.
151
-
152
-
if is-at-least 5.4.1; then
153
-
trap '' HUP # Catch the HUP sent to this process.
154
-
kill -HUP -$$ # Send to entire process group.
155
-
trap - HUP # Disable HUP trap.
156
-
else
157
-
# We already handle HUP for Zsh < 5.4.1.
158
-
kill -HUP -$$ # Send to entire process group.
159
-
fi
160
-
}
161
-
162
-
killjobs() {
163
-
local tok
164
-
local -a pids
165
-
pids=(${${(v)jobstates##*:*:}%\=*})
166
-
167
-
# No need to send SIGHUP if no jobs are running.
168
-
(( $#pids == 0 )) && continue
169
-
(( $#pids == 1 )) && [[ $coproc_pid = $pids[1] ]] && continue
170
-
171
-
# Grab lock to prevent half-written output in case a child
172
-
# process is in the middle of writing to stdin during kill.
173
-
(( coproc_pid )) && read -r -k 1 -p tok
174
-
175
-
terminate_jobs
176
-
trap child_exit CHLD # Reinstall child trap.
177
-
}
178
-
179
-
local request do_eval=0
180
-
local -a cmd
181
-
while :; do
182
-
# Wait for jobs sent by async_job.
183
-
read -r -d $'\0' request || {
184
-
# Unknown error occurred while reading from stdin, the zpty
185
-
# worker is likely in a broken state, so we shut down.
186
-
terminate_jobs
187
-
188
-
# Stdin is broken and in case this was an unintended
189
-
# crash, we try to report it as a last hurrah.
190
-
print -r -n $'\0'"'[async]'" $(( 127 + 3 )) "''" 0 "'$0:$LINENO: zpty fd died, exiting'"$'\0'
191
-
192
-
# We use `return` to abort here because using `exit` may
193
-
# result in an infinite loop that never exits and, as a
194
-
# result, high CPU utilization.
195
-
return $(( 127 + 1 ))
196
-
}
197
-
198
-
# We need to clean the input here because sometimes when a zpty
199
-
# has died and been respawned, messages will be prefixed with a
200
-
# carraige return (\r, or \C-M).
201
-
request=${request#$'\C-M'}
202
-
203
-
# Check for non-job commands sent to worker
204
-
case $request in
205
-
_killjobs) killjobs; continue;;
206
-
_async_eval*) do_eval=1;;
207
-
esac
208
-
209
-
# Parse the request using shell parsing (z) to allow commands
210
-
# to be parsed from single strings and multi-args alike.
211
-
cmd=("${(z)request}")
212
-
213
-
# Name of the job (first argument).
214
-
local job=$cmd[1]
215
-
216
-
# Check if a worker should perform unique jobs, unless
217
-
# this is an eval since they run synchronously.
218
-
if (( !do_eval )) && (( unique )); then
219
-
# Check if a previous job is still running, if yes,
220
-
# skip this job and let the previous one finish.
221
-
for pid in ${${(v)jobstates##*:*:}%\=*}; do
222
-
if [[ ${storage[$job]} == $pid ]]; then
223
-
continue 2
224
-
fi
225
-
done
226
-
fi
227
-
228
-
# Guard against closing coproc from trap before command has started.
229
-
processing=1
230
-
231
-
# Because we close the coproc after the last job has completed, we must
232
-
# recreate it when there are no other jobs running.
233
-
if (( ! coproc_pid )); then
234
-
# Use coproc as a mutex for synchronized output between children.
235
-
coproc command -p cat
236
-
coproc_pid="$!"
237
-
# Insert token into coproc
238
-
print -n -p "t"
239
-
fi
240
-
241
-
if (( do_eval )); then
242
-
shift cmd # Strip _async_eval from cmd.
243
-
_async_eval $cmd
244
-
else
245
-
# Run job in background, completed jobs are printed to stdout.
246
-
_async_job $cmd &
247
-
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
248
-
storage[$job]="$!"
249
-
fi
250
-
251
-
processing=0 # Disable guard.
252
-
253
-
if (( do_eval )); then
254
-
do_eval=0
255
-
256
-
# When there are no active jobs we can't rely on the CHLD trap to
257
-
# manage the coproc lifetime.
258
-
close_idle_coproc
259
-
fi
260
-
done
261
-
}
262
-
263
-
#
264
-
# Get results from finished jobs and pass it to the to callback function. This is the only way to reliably return the
265
-
# job name, return code, output and execution time and with minimal effort.
266
-
#
267
-
# If the async process buffer becomes corrupt, the callback will be invoked with the first argument being `[async]` (job
268
-
# name), non-zero return code and fifth argument describing the error (stderr).
269
-
#
270
-
# usage:
271
-
# async_process_results <worker_name> <callback_function>
272
-
#
273
-
# callback_function is called with the following parameters:
274
-
# $1 = job name, e.g. the function passed to async_job
275
-
# $2 = return code
276
-
# $3 = resulting stdout from execution
277
-
# $4 = execution time, floating point e.g. 2.05 seconds
278
-
# $5 = resulting stderr from execution
279
-
# $6 = has next result in buffer (0 = buffer empty, 1 = yes)
280
-
#
281
-
async_process_results() {
282
-
setopt localoptions unset noshwordsplit noksharrays noposixidentifiers noposixstrings
283
-
284
-
local worker=$1
285
-
local callback=$2
286
-
local caller=$3
287
-
local -a items
288
-
local null=$'\0' data
289
-
integer -l len pos num_processed has_next
290
-
291
-
typeset -gA ASYNC_PROCESS_BUFFER
292
-
293
-
# Read output from zpty and parse it if available.
294
-
while zpty -r -t $worker data 2>/dev/null; do
295
-
ASYNC_PROCESS_BUFFER[$worker]+=$data
296
-
len=${#ASYNC_PROCESS_BUFFER[$worker]}
297
-
pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter).
298
-
299
-
# Keep going until we find a NULL-character.
300
-
if (( ! len )) || (( pos > len )); then
301
-
continue
302
-
fi
303
-
304
-
while (( pos <= len )); do
305
-
# Take the content from the beginning, until the NULL-character and
306
-
# perform shell parsing (z) and unquoting (Q) as an array (@).
307
-
items=("${(@Q)${(z)ASYNC_PROCESS_BUFFER[$worker][1,$pos-1]}}")
308
-
309
-
# Remove the extracted items from the buffer.
310
-
ASYNC_PROCESS_BUFFER[$worker]=${ASYNC_PROCESS_BUFFER[$worker][$pos+1,$len]}
311
-
312
-
len=${#ASYNC_PROCESS_BUFFER[$worker]}
313
-
if (( len > 1 )); then
314
-
pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter).
315
-
fi
316
-
317
-
has_next=$(( len != 0 ))
318
-
if (( $#items == 5 )); then
319
-
items+=($has_next)
320
-
$callback "${(@)items}" # Send all parsed items to the callback.
321
-
(( num_processed++ ))
322
-
elif [[ -z $items ]]; then
323
-
# Empty items occur between results due to double-null ($'\0\0')
324
-
# caused by commands being both pre and suffixed with null.
325
-
else
326
-
# In case of corrupt data, invoke callback with *async* as job
327
-
# name, non-zero exit status and an error message on stderr.
328
-
$callback "[async]" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next
329
-
fi
330
-
done
331
-
done
332
-
333
-
(( num_processed )) && return 0
334
-
335
-
# Avoid printing exit value when `setopt printexitvalue` is active.`
336
-
[[ $caller = trap || $caller = watcher ]] && return 0
337
-
338
-
# No results were processed
339
-
return 1
340
-
}
341
-
342
-
# Watch worker for output
343
-
_async_zle_watcher() {
344
-
setopt localoptions noshwordsplit
345
-
typeset -gA ASYNC_PTYS ASYNC_CALLBACKS
346
-
local worker=$ASYNC_PTYS[$1]
347
-
local callback=$ASYNC_CALLBACKS[$worker]
348
-
349
-
if [[ -n $2 ]]; then
350
-
# from man zshzle(1):
351
-
# `hup' for a disconnect, `nval' for a closed or otherwise
352
-
# invalid descriptor, or `err' for any other condition.
353
-
# Systems that support only the `select' system call always use
354
-
# `err'.
355
-
356
-
# this has the side effect to unregister the broken file descriptor
357
-
async_stop_worker $worker
358
-
359
-
if [[ -n $callback ]]; then
360
-
$callback '[async]' 2 "" 0 "$0:$LINENO: error: fd for $worker failed: zle -F $1 returned error $2" 0
361
-
fi
362
-
return
363
-
fi;
364
-
365
-
if [[ -n $callback ]]; then
366
-
async_process_results $worker $callback watcher
367
-
fi
368
-
}
369
-
370
-
_async_send_job() {
371
-
setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
372
-
373
-
local caller=$1
374
-
local worker=$2
375
-
shift 2
376
-
377
-
zpty -t $worker &>/dev/null || {
378
-
typeset -gA ASYNC_CALLBACKS
379
-
local callback=$ASYNC_CALLBACKS[$worker]
380
-
381
-
if [[ -n $callback ]]; then
382
-
$callback '[async]' 3 "" 0 "$0:$LINENO: error: no such worker: $worker" 0
383
-
else
384
-
print -u2 "$caller: no such async worker: $worker"
385
-
fi
386
-
return 1
387
-
}
388
-
389
-
zpty -w $worker "$@"$'\0'
390
-
}
391
-
392
-
#
393
-
# Start a new asynchronous job on specified worker, assumes the worker is running.
394
-
#
395
-
# Note if you are using a function for the job, it must have been defined before the worker was
396
-
# started or you will get a `command not found` error.
397
-
#
398
-
# usage:
399
-
# async_job <worker_name> <my_function> [<function_params>]
400
-
#
401
-
async_job() {
402
-
setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
403
-
404
-
local worker=$1; shift
405
-
406
-
local -a cmd
407
-
cmd=("$@")
408
-
if (( $#cmd > 1 )); then
409
-
cmd=(${(q)cmd}) # Quote special characters in multi argument commands.
410
-
fi
411
-
412
-
_async_send_job $0 $worker "$cmd"
413
-
}
414
-
415
-
#
416
-
# Evaluate a command (like async_job) inside the async worker, then worker environment can be manipulated. For example,
417
-
# issuing a cd command will change the PWD of the worker which will then be inherited by all future async jobs.
418
-
#
419
-
# Output will be returned via callback, job name will be [async/eval].
420
-
#
421
-
# usage:
422
-
# async_worker_eval <worker_name> <my_function> [<function_params>]
423
-
#
424
-
async_worker_eval() {
425
-
setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
426
-
427
-
local worker=$1; shift
428
-
429
-
local -a cmd
430
-
cmd=("$@")
431
-
if (( $#cmd > 1 )); then
432
-
cmd=(${(q)cmd}) # Quote special characters in multi argument commands.
433
-
fi
434
-
435
-
# Quote the cmd in case RC_EXPAND_PARAM is set.
436
-
_async_send_job $0 $worker "_async_eval $cmd"
437
-
}
438
-
439
-
# This function traps notification signals and calls all registered callbacks
440
-
_async_notify_trap() {
441
-
setopt localoptions noshwordsplit
442
-
443
-
local k
444
-
for k in ${(k)ASYNC_CALLBACKS}; do
445
-
async_process_results $k ${ASYNC_CALLBACKS[$k]} trap
446
-
done
447
-
}
448
-
449
-
#
450
-
# Register a callback for completed jobs. As soon as a job is finnished, async_process_results will be called with the
451
-
# specified callback function. This requires that a worker is initialized with the -n (notify) option.
452
-
#
453
-
# usage:
454
-
# async_register_callback <worker_name> <callback_function>
455
-
#
456
-
async_register_callback() {
457
-
setopt localoptions noshwordsplit nolocaltraps
458
-
459
-
typeset -gA ASYNC_PTYS ASYNC_CALLBACKS
460
-
local worker=$1; shift
461
-
462
-
ASYNC_CALLBACKS[$worker]="$*"
463
-
464
-
# Enable trap when the ZLE watcher is unavailable, allows
465
-
# workers to notify (via -n) when a job is done.
466
-
if [[ ! -o interactive ]] || [[ ! -o zle ]]; then
467
-
trap '_async_notify_trap' WINCH
468
-
elif [[ -o interactive ]] && [[ -o zle ]]; then
469
-
local fd w
470
-
for fd w in ${(@kv)ASYNC_PTYS}; do
471
-
if [[ $w == $worker ]]; then
472
-
zle -F $fd _async_zle_watcher # Register the ZLE handler.
473
-
break
474
-
fi
475
-
done
476
-
fi
477
-
}
478
-
479
-
#
480
-
# Unregister the callback for a specific worker.
481
-
#
482
-
# usage:
483
-
# async_unregister_callback <worker_name>
484
-
#
485
-
async_unregister_callback() {
486
-
typeset -gA ASYNC_CALLBACKS
487
-
488
-
unset "ASYNC_CALLBACKS[$1]"
489
-
}
490
-
491
-
#
492
-
# Flush all current jobs running on a worker. This will terminate any and all running processes under the worker, use
493
-
# with caution.
494
-
#
495
-
# usage:
496
-
# async_flush_jobs <worker_name>
497
-
#
498
-
async_flush_jobs() {
499
-
setopt localoptions noshwordsplit
500
-
501
-
local worker=$1; shift
502
-
503
-
# Check if the worker exists
504
-
zpty -t $worker &>/dev/null || return 1
505
-
506
-
# Send kill command to worker
507
-
async_job $worker "_killjobs"
508
-
509
-
# Clear the zpty buffer.
510
-
local junk
511
-
if zpty -r -t $worker junk '*'; then
512
-
(( ASYNC_DEBUG )) && print -n "async_flush_jobs $worker: ${(V)junk}"
513
-
while zpty -r -t $worker junk '*'; do
514
-
(( ASYNC_DEBUG )) && print -n "${(V)junk}"
515
-
done
516
-
(( ASYNC_DEBUG )) && print
517
-
fi
518
-
519
-
# Finally, clear the process buffer in case of partially parsed responses.
520
-
typeset -gA ASYNC_PROCESS_BUFFER
521
-
unset "ASYNC_PROCESS_BUFFER[$worker]"
522
-
}
523
-
524
-
#
525
-
# Start a new async worker with optional parameters, a worker can be told to only run unique tasks and to notify a
526
-
# process when tasks are complete.
527
-
#
528
-
# usage:
529
-
# async_start_worker <worker_name> [-u] [-n] [-p <pid>]
530
-
#
531
-
# opts:
532
-
# -u unique (only unique job names can run)
533
-
# -n notify through SIGWINCH signal
534
-
# -p pid to notify (defaults to current pid)
535
-
#
536
-
async_start_worker() {
537
-
setopt localoptions noshwordsplit noclobber
538
-
539
-
local worker=$1; shift
540
-
local -a args
541
-
args=("$@")
542
-
zpty -t $worker &>/dev/null && return
543
-
544
-
typeset -gA ASYNC_PTYS
545
-
typeset -h REPLY
546
-
typeset has_xtrace=0
547
-
548
-
if [[ -o interactive ]] && [[ -o zle ]]; then
549
-
# Inform the worker to ignore the notify flag and that we're
550
-
# using a ZLE watcher instead.
551
-
args+=(-z)
552
-
553
-
if (( ! ASYNC_ZPTY_RETURNS_FD )); then
554
-
# When zpty doesn't return a file descriptor (on older versions of zsh)
555
-
# we try to guess it anyway.
556
-
integer -l zptyfd
557
-
exec {zptyfd}>&1 # Open a new file descriptor (above 10).
558
-
exec {zptyfd}>&- # Close it so it's free to be used by zpty.
559
-
fi
560
-
fi
561
-
562
-
# Workaround for stderr in the main shell sometimes (incorrectly) being
563
-
# reassigned to /dev/null by the reassignment done inside the async
564
-
# worker.
565
-
# See https://github.com/mafredri/zsh-async/issues/35.
566
-
integer errfd=-1
567
-
568
-
# Redirect of errfd is broken on zsh 5.0.2.
569
-
if is-at-least 5.0.8; then
570
-
exec {errfd}>&2
571
-
fi
572
-
573
-
# Make sure async worker is started without xtrace
574
-
# (the trace output interferes with the worker).
575
-
[[ -o xtrace ]] && {
576
-
has_xtrace=1
577
-
unsetopt xtrace
578
-
}
579
-
580
-
if (( errfd != -1 )); then
581
-
zpty -b $worker _async_worker -p $$ $args 2>&$errfd
582
-
else
583
-
zpty -b $worker _async_worker -p $$ $args
584
-
fi
585
-
local ret=$?
586
-
587
-
# Re-enable it if it was enabled, for debugging.
588
-
(( has_xtrace )) && setopt xtrace
589
-
(( errfd != -1 )) && exec {errfd}>& -
590
-
591
-
if (( ret )); then
592
-
async_stop_worker $worker
593
-
return 1
594
-
fi
595
-
596
-
if ! is-at-least 5.0.8; then
597
-
# For ZSH versions older than 5.0.8 we delay a bit to give
598
-
# time for the worker to start before issuing commands,
599
-
# otherwise it will not be ready to receive them.
600
-
sleep 0.001
601
-
fi
602
-
603
-
if [[ -o interactive ]] && [[ -o zle ]]; then
604
-
if (( ! ASYNC_ZPTY_RETURNS_FD )); then
605
-
REPLY=$zptyfd # Use the guessed value for the file desciptor.
606
-
fi
607
-
608
-
ASYNC_PTYS[$REPLY]=$worker # Map the file desciptor to the worker.
609
-
fi
610
-
}
611
-
612
-
#
613
-
# Stop one or multiple workers that are running, all unfetched and incomplete work will be lost.
614
-
#
615
-
# usage:
616
-
# async_stop_worker <worker_name_1> [<worker_name_2>]
617
-
#
618
-
async_stop_worker() {
619
-
setopt localoptions noshwordsplit
620
-
621
-
local ret=0 worker k v
622
-
for worker in $@; do
623
-
# Find and unregister the zle handler for the worker
624
-
for k v in ${(@kv)ASYNC_PTYS}; do
625
-
if [[ $v == $worker ]]; then
626
-
zle -F $k
627
-
unset "ASYNC_PTYS[$k]"
628
-
fi
629
-
done
630
-
async_unregister_callback $worker
631
-
zpty -d $worker 2>/dev/null || ret=$?
632
-
633
-
# Clear any partial buffers.
634
-
typeset -gA ASYNC_PROCESS_BUFFER
635
-
unset "ASYNC_PROCESS_BUFFER[$worker]"
636
-
done
637
-
638
-
return $ret
639
-
}
640
-
641
-
#
642
-
# Initialize the required modules for zsh-async. To be called before using the zsh-async library.
643
-
#
644
-
# usage:
645
-
# async_init
646
-
#
647
-
async_init() {
648
-
(( ASYNC_INIT_DONE )) && return
649
-
typeset -g ASYNC_INIT_DONE=1
650
-
651
-
zmodload zsh/zpty
652
-
zmodload zsh/datetime
653
-
654
-
# Load is-at-least for reliable version check.
655
-
autoload -Uz is-at-least
656
-
657
-
# Check if zsh/zpty returns a file descriptor or not,
658
-
# shell must also be interactive with zle enabled.
659
-
typeset -g ASYNC_ZPTY_RETURNS_FD=0
660
-
[[ -o interactive ]] && [[ -o zle ]] && {
661
-
typeset -h REPLY
662
-
zpty _async_test :
663
-
(( REPLY )) && ASYNC_ZPTY_RETURNS_FD=1
664
-
zpty -d _async_test
665
-
}
666
-
}
667
-
668
-
async() {
669
-
async_init
670
-
}
671
-
672
-
async "$@"
···
-84
zsh/prompt.zsh
-84
zsh/prompt.zsh
···
1
-
source "$HOME/projects/dotfiles/zsh/async.zsh"
2
-
3
-
# init zsh-async
4
-
async_init
5
-
6
-
jj_status() {
7
-
template="' (' ++ change_id.shortest(8) ++ ')'"
8
-
if repo_root=$(jj root --ignore-working-copy 2>/dev/null); then
9
-
jj log -r @ -T $template --no-graph --no-pager --color never --ignore-working-copy -R $repo_root
10
-
fi
11
-
}
12
-
13
-
fetch_data() {
14
-
# refetch data
15
-
date_string=$(date +"%Y-%m-%d %H:%M:%S")
16
-
JJ_ST=$(jj_status)
17
-
}
18
-
reset_prompt() {
19
-
# reset prompt
20
-
PROMPT="%F{green}%n@%m %F{blue}%~%f$JJ_ST %# "
21
-
# re-render prompt
22
-
zle reset-prompt
23
-
}
24
-
25
-
RUNNING=
26
-
update_prompt() {
27
-
if [[ -n $RUNNING ]]; then
28
-
return 0
29
-
fi
30
-
RUNNING=1
31
-
32
-
fetch_data
33
-
34
-
reset_prompt
35
-
36
-
# NOTE: don't make recursive loop. use TMOUT & TRAPALRM for 1s throttling
37
-
# # call fetch job again
38
-
# async_job prompt_worker
39
-
RUNNING=
40
-
}
41
-
42
-
# define status_worker to run asynchronously
43
-
async_start_worker prompt_worker -n
44
-
async_register_callback prompt_worker update_prompt
45
-
async_job prompt_worker
46
-
47
-
# reset_prompt() {
48
-
# # reset prompt
49
-
# PROMPT="%F{green}%n@%m %F{blue}%~%f$JJ_ST %# "
50
-
# # re-render prompt
51
-
# zle reset-prompt
52
-
#
53
-
# async_job prompt_render_worker
54
-
# }
55
-
# async_start_worker prompt_render_worker -n
56
-
# async_register_callback prompt_render_worker reset_prompt
57
-
# async_job prompt_render_worker
58
-
59
-
# call do_update on every seconds
60
-
TMOUT=1
61
-
TRAPALRM() {
62
-
async_job prompt_worker
63
-
}
64
-
65
-
# NOTE: don't do heavy jobs here. It will make prompt slower
66
-
precmd() {
67
-
PROMPT="%F{green}%n@%m %F{blue}%~%f$JJ_ST %# "
68
-
# call fetch job again
69
-
async_job prompt_worker
70
-
}
71
-
72
-
zle_reset_prompt_widget() {
73
-
# async_job prompt_worker
74
-
}
75
-
# register it under the name โzle_reset_prompt_widgetโ
76
-
zle -N zle_reset_prompt_widget
77
-
autoload -Uz add-zle-hook-widget
78
-
add-zle-hook-widget line-init zle_reset_prompt_widget
79
-
80
-
PROMPT="%n@%m %~%f$JJ_ST %# "
81
-
82
-
# references:
83
-
# - https://medium.com/@henrebotha/how-to-write-an-asynchronous-zsh-prompt-b53e81720d32
84
-
# - https://void-shana.moe/posts/customize-your-zsh-prompt
···