mirror of
https://github.com/Smaug123/nix-dotfiles
synced 2025-10-06 06:58:41 +00:00
Compare commits
151 Commits
update-ext
...
more-earth
Author | SHA1 | Date | |
---|---|---|---|
|
f413ff7918 | ||
|
7de8ba96f8 | ||
|
ab4af6d488 | ||
|
02f757bf57 | ||
|
6d3804a5ee | ||
|
6c3bdcc4b2 | ||
|
f9172d159b | ||
|
233771f07d | ||
|
2123c53980 | ||
|
7f712faf2c | ||
|
5b8b64b0a2 | ||
|
73efe7a18c | ||
|
f6dd2e686a | ||
|
df071387b2 | ||
|
f83285dec2 | ||
|
9fbcd75842 | ||
|
52c08e4183 | ||
|
d7221c2af1 | ||
|
8969b0c347 | ||
|
a40777c034 | ||
|
dbe797cf03 | ||
|
e60a954838 | ||
|
0e00b6250f | ||
|
fd1452cc8f | ||
|
8ef0e6ef37 | ||
|
8e7abf90c2 | ||
|
8d888029bd | ||
|
d891582dfe | ||
|
fa88755137 | ||
|
544fc635eb | ||
|
6a493ab214 | ||
|
86b6269229 | ||
|
89e1aa4d7e | ||
|
76dbd82446 | ||
|
ceaead9b63 | ||
|
88f4111744 | ||
|
3ae9cebd58 | ||
|
5c872e2c4a | ||
|
ede76d2bc2 | ||
|
19e8024a13 | ||
|
43c7842fac | ||
|
df22898e58 | ||
|
ad9621acd3 | ||
|
dec7aff312 | ||
|
def75585dd | ||
|
8c67cd2e0a | ||
|
9649e2e37b | ||
|
162b58abc4 | ||
|
29d80dec16 | ||
|
1fc72d0288 | ||
|
8752e0a720 | ||
|
cde0abe5d9 | ||
|
5536c97493 | ||
|
ad6a4548c6 | ||
|
b361bbcbcb | ||
|
02ceae3e22 | ||
|
e7f68f24a3 | ||
|
3208bf16c5 | ||
|
ccaa90d392 | ||
|
fd71527762 | ||
|
a210ee4301 | ||
|
d3ec6b02c3 | ||
|
aa3d08745a | ||
|
bf1dfe3d6d | ||
|
d867348640 | ||
|
7fb26eb707 | ||
|
f723b64486 | ||
|
7b94e76589 | ||
|
68d57ea7cb | ||
|
c6879ac254 | ||
|
59e1e8637c | ||
|
6256ad908f | ||
|
d783fe475e | ||
|
77d7d402c3 | ||
|
b69b9248f9 | ||
|
e91fb514fe | ||
|
07b3034bc0 | ||
|
a734e7f73f | ||
|
010498edce | ||
|
348d913a49 | ||
|
87492c2abe | ||
|
e668501cad | ||
|
15e603063a | ||
|
e160ff5704 | ||
|
028817765e | ||
|
7a1e5eb445 | ||
|
7b14690664 | ||
|
5647f009fb | ||
|
47fc4dfd39 | ||
|
ae0713074f | ||
|
db633836f1 | ||
|
4554ea1a90 | ||
|
75cebeaa7a | ||
|
b94098e5af | ||
|
371af97527 | ||
|
742a708c0c | ||
|
ddbd6718b5 | ||
|
824e35351d | ||
|
8336993820 | ||
|
49f2b9be5c | ||
|
289e430d52 | ||
|
d54f38cf68 | ||
|
14981d3401 | ||
|
3e9b26c093 | ||
|
47b5993422 | ||
|
93b5fc92e9 | ||
|
e493cdbcdd | ||
|
78b2d62ce2 | ||
|
1e4c4c525f | ||
|
31e8d08da3 | ||
|
21bb1ebd3d | ||
|
b111a9509d | ||
|
ef64007cc9 | ||
|
b9bb4c49fa | ||
|
75c77e99c0 | ||
|
d2718febf8 | ||
|
ef6d3d4445 | ||
|
b3eb9adb9f | ||
|
ada02e558b | ||
|
ae96250101 | ||
|
5e3f257ce6 | ||
|
a3a8a5598a | ||
|
134b0dc8a7 | ||
|
a290279914 | ||
|
bea4ca1220 | ||
|
6af765d032 | ||
|
02c87e8ea4 | ||
|
0339c1f051 | ||
|
3743aead94 | ||
|
8f956b631c | ||
|
142e7f2244 | ||
|
166ef06a35 | ||
|
b07f725d3d | ||
|
4b3f3f886b | ||
|
028225eca7 | ||
|
b343f6bb01 | ||
|
2cd767ae5b | ||
|
44856ee782 | ||
|
36171014c8 | ||
|
df297beff1 | ||
|
4e6c9b03af | ||
|
460af9d007 | ||
|
b72a716083 | ||
|
f1d917d4ec | ||
|
f4678f910f | ||
|
cf0dbe7f44 | ||
|
c45a0d9df6 | ||
|
d22ad837a0 | ||
|
005de0c62a | ||
|
c9dd6b03ec | ||
|
5fda4e0165 |
8
.github/dependabot.yml
vendored
Normal file
8
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
28
.github/workflows/lint.yaml
vendored
Normal file
28
.github/workflows/lint.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
flake-check:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Install Nix"
|
||||
uses: "cachix/install-nix-action@v30"
|
||||
with: { "extra_nix_config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" }
|
||||
- name: "Check flake"
|
||||
run: "nix flake check --all-systems"
|
||||
|
||||
all-required-checks-complete:
|
||||
if: ${{ always() }}
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: Smaug123/all-required-checks-complete-action@05b40a8c47ef0b175ea326e9abb09802cb67b44e
|
||||
with:
|
||||
needs-context: ${{ toJSON(needs) }}
|
||||
needs: [ "flake-check" ]
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ result
|
||||
.idea/
|
||||
bin/
|
||||
obj/
|
||||
.DS_Store
|
||||
|
@@ -1,9 +1,6 @@
|
||||
This repository currently has no licence applied to it, except for the NeoVim configuration.
|
||||
That configuration is in large part derived from https://github.com/amix/vimrc and is therefore provided under the following licence.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Amir Salihefendic
|
||||
Copyright (c) 2024 Patrick Stevens
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -81,14 +81,14 @@ type NixFile =
|
||||
|
||||
static member Parse (s : string) : NixFile =
|
||||
let pre, post =
|
||||
s.Split "\n] ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [\n"
|
||||
s.Split "++ pkgs.vscode-utils.extensionsFromVscodeMarketplace ["
|
||||
|> function
|
||||
| [| pre ; post |] -> pre, post
|
||||
| _ -> failwith "Unexpected number of '++'"
|
||||
|
||||
let verbatim, skipped =
|
||||
match pre.Split "\n" |> List.ofArray with
|
||||
| "{ pkgs }:" :: "" :: "with pkgs.vscode-extensions; [" :: rest ->
|
||||
match pre.Split "\n" |> Seq.filter (fun s -> s <> "") |> List.ofSeq with
|
||||
| pkgsStr :: "with pkgs.vscode-extensions; [" :: rest when pkgsStr.Replace(" ", "") = "{pkgs}:" ->
|
||||
rest
|
||||
|> List.map (fun s ->
|
||||
if s.StartsWith '#' then Choice2Of2 (s.[2..].Trim()) else Choice1Of2 (s.Trim())
|
||||
@@ -146,7 +146,9 @@ let upgradeExtension (client : HttpClient) (e : Extension) : Extension Async =
|
||||
|> sprintf "[%s]"
|
||||
|> fun s -> JsonSerializer.Deserialize<Version array> (s, options)
|
||||
|> Seq.head
|
||||
return { e with Version = latestVersion.Version }
|
||||
if latestVersion.Version <> e.Version then
|
||||
return { e with Version = latestVersion.Version ; Sha256 = "sha256-/000+cQBqzb6QB5+AizlyIcjqNpZ86o2at885hOcro0=" }
|
||||
else return e
|
||||
}
|
||||
|
||||
let upgrade (nixFile : NixFile) : NixFile =
|
||||
@@ -170,7 +172,7 @@ module Program =
|
||||
File.ReadAllText sourceFile
|
||||
|> NixFile.Parse
|
||||
|> upgrade
|
||||
|> sprintf "%O"
|
||||
|> string<NixFile>
|
||||
|> fun s -> File.WriteAllText (sourceFile, s)
|
||||
|
||||
0
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -1,13 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
install_nix () {
|
||||
install_nix() {
|
||||
echo "Installing Nix..."
|
||||
diskutil list > /dev/null || export PATH=/usr/sbin:$PATH
|
||||
diskutil list >/dev/null || export PATH="/usr/sbin:$PATH"
|
||||
curl -L https://nixos.org/nix/install | sh -s -- --darwin-use-unencrypted-nix-store-volume --daemon || exit 1
|
||||
echo "Nix installed."
|
||||
}
|
||||
|
||||
install_darwin_build () {
|
||||
install_darwin_build() {
|
||||
echo "Installing nix-darwin..."
|
||||
nix-build https://github.com/LnL7/nix-darwin/archive/master.tar.gz -A installer || exit 1
|
||||
./result/bin/darwin-installer || exit 1
|
||||
@@ -23,4 +23,4 @@ nix-channel --update || exit 1
|
||||
|
||||
darwin-rebuild changelog || install_darwin_build || exit 1
|
||||
|
||||
NIX_PATH="darwin-config=$HOME/.nixpkgs/darwin-configuration.nix:/nix/var/nix/profiles/per-user/Patrick/channels:$NIX_PATH" darwin-rebuild switch || exit 1
|
||||
NIX_PATH="darwin-config=$HOME/.nixpkgs/darwin-configuration.nix:/nix/var/nix/profiles/per-user/patrick/channels:$NIX_PATH" darwin-rebuild switch || exit 1
|
||||
|
@@ -1,43 +1,131 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
let python = import ./python.nix { inherit pkgs; }; in
|
||||
|
||||
{
|
||||
{pkgs, ...}: let
|
||||
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||
in {
|
||||
nix.useDaemon = true;
|
||||
|
||||
# List packages installed in system profile. To search by name, run:
|
||||
# $ nix-env -qaP | grep wget
|
||||
|
||||
environment.systemPackages =
|
||||
[
|
||||
pkgs.alacritty
|
||||
pkgs.rustup
|
||||
pkgs.libiconv
|
||||
pkgs.clang
|
||||
#pkgs.keepassxc
|
||||
python
|
||||
];
|
||||
environment.systemPackages = [
|
||||
pkgs.alacritty
|
||||
pkgs.rustup
|
||||
pkgs.libiconv
|
||||
pkgs.clang
|
||||
pkgs.python3
|
||||
];
|
||||
|
||||
users.users.patrick = {
|
||||
home = "/Users/patrick";
|
||||
name = "patrick";
|
||||
};
|
||||
|
||||
# This line is required; otherwise, on shell startup, you won't have Nix stuff in the PATH.
|
||||
programs.zsh.enable = true;
|
||||
programs.gnupg.agent.enable = true;
|
||||
|
||||
# Use a custom configuration.nix location.
|
||||
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
|
||||
environment.darwinConfig = "$HOME/.nixpkgs/darwin-configuration.nix";
|
||||
|
||||
launchd.agents = {
|
||||
mbsync-btinternet = {
|
||||
command = "${mbsync}/bin/mbsync BTInternet > /tmp/mbsync.btinternet.log 2>/tmp/mbsync.btinternet.2.log";
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 60;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
mbsync-proton = {
|
||||
command = "${mbsync}/bin/mbsync Proton > /tmp/mbsync.proton.1.log 2>/tmp/mbsync.proton.2.log";
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 60;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
mbsync-gmail = {
|
||||
command = "${mbsync}/bin/mbsync Gmail > /tmp/mbsync.gmail.1.log 2>/tmp/mbsync.gmail.2.log";
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
# Refresh token is 60min long, so do this more often than that!
|
||||
StartInterval = 30;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
backup-calendar = {
|
||||
command = ''${pkgs.bash}/bin/bash -c "mkdir -p '/Users/patrick/Library/Application Support/RadicaleBackups' && if [ ! -d '/Users/patrick/Library/Application Support/RadicaleBackups/.git' ] ; then ${pkgs.git}/bin/git clone root@patrickstevens.co.uk:/preserve/radicale/data/.git '/Users/patrick/Library/Application Support/RadicaleBackups' >/tmp/radicale.out.log 2>/tmp/radicale.err.log; fi && ${pkgs.git}/bin/git --git-dir '/Users/patrick/Library/Application Support/RadicaleBackups/.git' --work-tree '/Users/patrick/Library/Application Support/RadicaleBackups/' pull 2>>/tmp/radicale.err.log"'';
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 3600;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
sync-nixpkgs = {
|
||||
command = ''${pkgs.bash}/bin/bash -c "if [ -d /Users/patrick/Documents/GitHub/nixpkgs ] ; then ${pkgs.git}/bin/git --git-dir /Users/patrick/Documents/GitHub/nixpkgs/.git --work-tree '/Users/patrick/Documents/GitHub/nixpkgs/' fetch origin ; fi"'';
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 36000;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
sync-dotnet-api-docs = {
|
||||
command = ''${pkgs.bash}/bin/bash -c "if [ -d /Users/patrick/Documents/GitHub/dotnet-api-docs ] ; then ${pkgs.git}/bin/git --git-dir /Users/patrick/Documents/GitHub/dotnet-api-docs/.git --work-tree '/Users/patrick/Documents/GitHub/dotnet-api-docs' fetch origin ; fi"'';
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 36000;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
sync-dotnet-docs = {
|
||||
command = ''${pkgs.bash}/bin/bash -c "if [ -d /Users/patrick/Documents/GitHub/dotnet-docs ] ; then ${pkgs.git}/bin/git --git-dir /Users/patrick/Documents/GitHub/dotnet-docs/.git --work-tree '/Users/patrick/Documents/GitHub/dotnet-docs' fetch origin ; fi"'';
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 36000;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
|
||||
nix-store-optimise = {
|
||||
command = ''${pkgs.nix}/bin/nix store optimise'';
|
||||
serviceConfig = {
|
||||
KeepAlive = false;
|
||||
UserName = "patrick";
|
||||
StartInterval = 72000;
|
||||
RunAtLoad = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Auto upgrade nix package and the daemon service.
|
||||
services.nix-daemon.enable = true;
|
||||
nix.package = pkgs.nixFlakes;
|
||||
nix.package = pkgs.nixVersions.stable;
|
||||
nix.gc.automatic = true;
|
||||
|
||||
# Sandbox causes failure: https://github.com/NixOS/nix/issues/4119
|
||||
nix.useSandbox = false;
|
||||
nix.settings.sandbox = false;
|
||||
|
||||
# Optimising store leads to transient build failures https://github.com/NixOS/nix/issues/7273
|
||||
nix.extraOptions = ''
|
||||
auto-optimise-store = true
|
||||
auto-optimise-store = false
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = ca-derivations
|
||||
max-jobs = auto # Allow building multiple derivations in parallel
|
||||
keep-outputs = true # Do not garbage-collect build time-only dependencies (e.g. clang)
|
||||
keep-derivations = true
|
||||
# Allow fetching build results from the Lean Cachix cache
|
||||
trusted-substituters = https://lean4.cachix.org/
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= lean4.cachix.org-1:mawtxSxcaiWE24xCXXgh3qnvlTkyU7evRRnGeAhD4Wk=
|
||||
|
0
firmware/copy-from-host.txt
Normal file
0
firmware/copy-from-host.txt
Normal file
198
flake.lock
generated
198
flake.lock
generated
@@ -1,5 +1,25 @@
|
||||
{
|
||||
"nodes": {
|
||||
"apple-silicon": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731473366,
|
||||
"narHash": "sha256-sE2WfD3YyNrCROfRZKqMDR77g3KV4FXUaJ7NWe+A7ro=",
|
||||
"owner": "tpwrules",
|
||||
"repo": "nixos-apple-silicon",
|
||||
"rev": "3eee753e4b074790342fadb1c4e7183d037ddac4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tpwrules",
|
||||
"repo": "nixos-apple-silicon",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"darwin": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -7,11 +27,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1645007752,
|
||||
"narHash": "sha256-FQZMiVP/1vgR7x+TWonMf0NZczrZ4ZjhSTj3rM+kglY=",
|
||||
"lastModified": 1731454423,
|
||||
"narHash": "sha256-TtwvgFxUa0wyptLhQbKaixgNW1UXf3+TDqfX3Kp63oM=",
|
||||
"owner": "lnl7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "c944b5ee82a829ddf7fa6bd9300bc2fe3d005fa1",
|
||||
"rev": "6c71c49e2448e51ad830ed211024e6d0edc50116",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -22,12 +42,18 @@
|
||||
}
|
||||
},
|
||||
"emacs": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1644143491,
|
||||
"narHash": "sha256-ld5MzYesjOE8Ml5uvjFUFaxAozJEl48svwWQ3i7oggc=",
|
||||
"lastModified": 1731604406,
|
||||
"narHash": "sha256-CUkO4CXaDcGyUqQ+/ArvekL3hlfgass7LjrnG6m2+g8=",
|
||||
"owner": "nix-community",
|
||||
"repo": "emacs-overlay",
|
||||
"rev": "4c7e0980a5f23684ca3ea772d4f62b0da4f55e3c",
|
||||
"rev": "f6c94b95f529cfbd29848c12816111a2471a5293",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -36,6 +62,39 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"locked": {
|
||||
"lastModified": 1688025799,
|
||||
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flake-compat",
|
||||
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -43,11 +102,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1645140957,
|
||||
"narHash": "sha256-WTJzLSCDLBI537o2L/3kRyqEV5YRT7+1QSGryeKReHE=",
|
||||
"lastModified": 1731604581,
|
||||
"narHash": "sha256-Qq2YZZaDTB3FZLWU/Hgh1uuWlUBl3cMLGB99bm7rFUM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "4f4165a8b9108818ab0193bbd1a252106870b2a2",
|
||||
"rev": "1d0862ee2d7c6f6cd720d6f32213fa425004be10",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -56,28 +115,141 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"model": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-aMuDhcvEaioTWn+LUcnxfAgs2VFbM8xBVVfdzx2Cu8I=",
|
||||
"type": "file",
|
||||
"url": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin?download=true"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin?download=true"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1644972330,
|
||||
"narHash": "sha256-hEDWZcTDopnz4jRDhCmnH9VztdKSfR6mrudSM8+Jfco",
|
||||
"lastModified": 1731139594,
|
||||
"narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1a5ff5ea0297dc8ec5a33b87dfb4fc78687068e6",
|
||||
"rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1731386116,
|
||||
"narHash": "sha256-lKA770aUmjPHdTaJWnP3yQ9OI1TigenUqVC3wweqZuI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "689fed12a013f56d4c4d3f612489634267d86529",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1731531548,
|
||||
"narHash": "sha256-sz8/v17enkYmfpgeeuyzniGJU0QQBfmAjlemAUYhfy8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "24f0d4acd634792badd6470134c387a3b039dace",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1695033101,
|
||||
"narHash": "sha256-RQ4m+ycjdLdass7Hr4+Lzwnjw7wGhcUkKqWiJS3YxPM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d941d9491804e0ca01e03468dbf6f8d3a7919a16",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"apple-silicon": "apple-silicon",
|
||||
"darwin": "darwin",
|
||||
"emacs": "emacs",
|
||||
"home-manager": "home-manager",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"whisper": "whisper"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1686795910,
|
||||
"narHash": "sha256-jDa40qRZ0GRQtP9EMZdf+uCbvzuLnJglTUI2JoHfWDc=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "5c2b97c0a9bc5217fc3dfb1555aae0fb756d99f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"whisper": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"model": "model",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1704121968,
|
||||
"narHash": "sha256-N8FJb+ohJ4Qt/m5RoAbwm3RP4VRjl+hA6PUCfjPhZo8=",
|
||||
"owner": "Smaug123",
|
||||
"repo": "whisper.cpp",
|
||||
"rev": "04f8e0cdc73abe7c593b2c9405f0f590c51de95a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Smaug123",
|
||||
"ref": "nix",
|
||||
"repo": "whisper.cpp",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
179
flake.nix
179
flake.nix
@@ -2,45 +2,186 @@
|
||||
description = "Patrick's Darwin Nix setup";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
nixpkgs = {
|
||||
url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
};
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
darwin = {
|
||||
url = "github:lnl7/nix-darwin/master";
|
||||
# url = "github:Smaug123/nix-darwin/extract";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
emacs = {
|
||||
url = "github:nix-community/emacs-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
apple-silicon = {
|
||||
url = "github:tpwrules/nixos-apple-silicon";
|
||||
};
|
||||
whisper = {
|
||||
url = "github:Smaug123/whisper.cpp/nix";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, darwin, emacs, nixpkgs, home-manager, ... }@inputs:
|
||||
let system = "aarch64-darwin"; in
|
||||
let config = {
|
||||
allowUnfreePredicate = pkg: builtins.elem (nixpkgs.lib.getName pkg) [
|
||||
"vscode"
|
||||
];
|
||||
}; in
|
||||
let overlays = [ emacs.overlay ] ++ import ./overlays.nix; in
|
||||
let pkgs = (import nixpkgs { inherit system config overlays; }); in
|
||||
{
|
||||
darwinConfigurations = {
|
||||
nixpkgs = pkgs;
|
||||
patrick = darwin.lib.darwinSystem {
|
||||
system = system;
|
||||
modules = [
|
||||
./darwin-configuration.nix
|
||||
home-manager.darwinModules.home-manager
|
||||
outputs = {
|
||||
darwin,
|
||||
emacs,
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
apple-silicon,
|
||||
whisper,
|
||||
...
|
||||
}: let
|
||||
config = {
|
||||
# contentAddressedByDefault = true;
|
||||
allowUnfree = true;
|
||||
};
|
||||
systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"];
|
||||
in let
|
||||
overlays = [emacs.overlay];
|
||||
recursiveMerge = attrList: let
|
||||
f = attrPath:
|
||||
builtins.zipAttrsWith (n: values:
|
||||
if builtins.tail values == []
|
||||
then builtins.head values
|
||||
else if builtins.all builtins.isList values
|
||||
then nixpkgs.lib.unique (builtins.concatLists values)
|
||||
else if builtins.all builtins.isAttrs values
|
||||
then f (attrPath ++ [n]) values
|
||||
else builtins.last values);
|
||||
in
|
||||
f [] attrList;
|
||||
in {
|
||||
nixosConfigurations = {
|
||||
capybara = let
|
||||
system = "x86_64-linux";
|
||||
in let
|
||||
pkgs = import nixpkgs {inherit system config overlays;};
|
||||
in
|
||||
nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = {
|
||||
username = "patrick";
|
||||
dotnet = pkgs.dotnet-sdk_8;
|
||||
};
|
||||
modules = let
|
||||
args = {
|
||||
nixpkgs = pkgs;
|
||||
username = "patrick";
|
||||
dotnet = pkgs.dotnet-sdk_8;
|
||||
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||
secretsPath = "/home/patrick/.secrets/";
|
||||
machinename = "capybara";
|
||||
};
|
||||
in [
|
||||
./home-manager/capybara-config.nix
|
||||
home-manager.nixosModules.home-manager
|
||||
{
|
||||
home-manager.useGlobalPkgs = true;
|
||||
home-manager.useUserPackages = true;
|
||||
home-manager.users.Patrick = import ./home.nix { nixpkgs = pkgs; };
|
||||
home-manager.users.patrick = recursiveMerge [(import ./home-manager/linux.nix args) (import ./home-manager/home.nix args)];
|
||||
}
|
||||
];
|
||||
};
|
||||
earthworm = let
|
||||
system = "aarch64-linux";
|
||||
in let
|
||||
pkgs = import nixpkgs {inherit system config overlays;};
|
||||
in
|
||||
nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
modules = let
|
||||
args = {
|
||||
nixpkgs = pkgs;
|
||||
username = "patrick";
|
||||
dotnet = pkgs.dotnet-sdk_8;
|
||||
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||
secretsPath = "/home/patrick/.secrets/";
|
||||
machinename = "earthworm";
|
||||
};
|
||||
in [
|
||||
./home-manager/earthworm-config.nix
|
||||
apple-silicon.nixosModules.default
|
||||
home-manager.nixosModules.home-manager
|
||||
{
|
||||
home-manager.useGlobalPkgs = true;
|
||||
home-manager.useUserPackages = true;
|
||||
home-manager.users.patrick = recursiveMerge [(import ./home-manager/linux.nix args) (import ./home-manager/home.nix args)];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
darwinConfigurations = let
|
||||
system = "aarch64-darwin";
|
||||
in let
|
||||
pkgs = import nixpkgs {inherit system config overlays;};
|
||||
in {
|
||||
nixpkgs = pkgs;
|
||||
patrick = darwin.lib.darwinSystem {
|
||||
system = system;
|
||||
modules = let
|
||||
args = {
|
||||
nixpkgs = pkgs;
|
||||
username = "patrick";
|
||||
dotnet = pkgs.dotnet-sdk_8;
|
||||
whisper = whisper.packages.${system};
|
||||
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||
secretsPath = "/Users/patrick/.secrets/";
|
||||
machinename = "darwin";
|
||||
};
|
||||
in [
|
||||
./darwin-configuration.nix
|
||||
home-manager.darwinModules.home-manager
|
||||
{
|
||||
home-manager.useGlobalPkgs = true;
|
||||
home-manager.useUserPackages = true;
|
||||
home-manager.users.patrick = recursiveMerge [(import ./home-manager/darwin.nix args) (import ./home-manager/home.nix args)];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
checks = let
|
||||
fmt-check = system: let
|
||||
pkgs = import nixpkgs {inherit config system;};
|
||||
in
|
||||
pkgs.stdenvNoCC.mkDerivation {
|
||||
name = "fmt-check";
|
||||
src = ./.;
|
||||
nativeBuildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.shfmt pkgs.stylua];
|
||||
checkPhase = ''
|
||||
find . -type f -name '*.sh' | xargs shfmt -d -s -i 2 -ci
|
||||
alejandra -c .
|
||||
find . -type f -name '*.sh' -exec shellcheck -x {} \;
|
||||
find . -type f -name '*.lua' -exec stylua --check {} \;
|
||||
'';
|
||||
installPhase = "mkdir $out";
|
||||
dontBuild = true;
|
||||
doCheck = true;
|
||||
};
|
||||
in
|
||||
builtins.listToAttrs (builtins.map (system: {
|
||||
name = system;
|
||||
value = {fmt-check = fmt-check system;};
|
||||
})
|
||||
systems);
|
||||
devShells = let
|
||||
devShell = system: (
|
||||
let
|
||||
pkgs = import nixpkgs {inherit config system;};
|
||||
in {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.stylua];
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
builtins.listToAttrs (builtins.map (system: {
|
||||
name = system;
|
||||
value = devShell system;
|
||||
})
|
||||
systems);
|
||||
};
|
||||
}
|
||||
|
43
hardware/capybara.nix
Normal file
43
hardware/capybara.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod"];
|
||||
boot.initrd.kernelModules = [];
|
||||
boot.kernelModules = ["kvm-amd"];
|
||||
boot.extraModulePackages = [];
|
||||
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-uuid/63c5394d-55ce-48a9-8d7c-2b68f3b5f834";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-uuid/9248-31C6";
|
||||
fsType = "vfat";
|
||||
options = ["fmask=0022" "dmask=0022"];
|
||||
};
|
||||
|
||||
swapDevices = [];
|
||||
|
||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||
# still possible to use this option, but it's recommended to use it in conjunction
|
||||
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
# networking.interfaces.eno1.useDHCP = lib.mkDefault true;
|
||||
# networking.interfaces.wlp9s0.useDHCP = lib.mkDefault true;
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
}
|
41
hardware/earthworm.nix
Normal file
41
hardware/earthworm.nix
Normal file
@@ -0,0 +1,41 @@
|
||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = ["usb_storage" "sdhci_pci"];
|
||||
boot.initrd.kernelModules = [];
|
||||
boot.kernelModules = [];
|
||||
boot.extraModulePackages = [];
|
||||
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-uuid/49068d2d-13e6-43b5-a740-f23db6fbd6b2";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-uuid/43ED-0FE7";
|
||||
fsType = "vfat";
|
||||
};
|
||||
|
||||
swapDevices = [];
|
||||
|
||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||
# still possible to use this option, but it's recommended to use it in conjunction
|
||||
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
# networking.interfaces.wlan0.useDHCP = lib.mkDefault true;
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
|
||||
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
|
||||
}
|
88
home-manager/capybara-config.nix
Normal file
88
home-manager/capybara-config.nix
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
imports = [
|
||||
../hardware/capybara.nix
|
||||
];
|
||||
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
enable32Bit = true;
|
||||
};
|
||||
|
||||
security.rtkit.enable = true;
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
};
|
||||
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.loader.grub.useOSProber = true;
|
||||
|
||||
boot.kernelParams = [
|
||||
"video=DP-1:2560x1440@144"
|
||||
"video=HDMI-A-1:1920x1080@144"
|
||||
];
|
||||
|
||||
boot.extraModulePackages = [config.boot.kernelPackages.rtl8821au];
|
||||
|
||||
networking = {
|
||||
hostName = "capybara";
|
||||
networkmanager.enable = true;
|
||||
};
|
||||
|
||||
time.timeZone = "Europe/London";
|
||||
|
||||
programs.sway.enable = true;
|
||||
programs.zsh.enable = true;
|
||||
|
||||
# TODO: work out secrets management for password, then set mutableUsers to false
|
||||
users.mutableUsers = true;
|
||||
users.users.patrick = {
|
||||
isNormalUser = true;
|
||||
extraGroups = ["wheel" "networkManager"];
|
||||
};
|
||||
|
||||
services.syncthing = {
|
||||
enable = true;
|
||||
user = "patrick";
|
||||
dataDir = "/home/patrick/syncthing";
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.git
|
||||
pkgs.vim
|
||||
pkgs.wget
|
||||
pkgs.tmux
|
||||
pkgs.home-manager
|
||||
pkgs.firefox
|
||||
];
|
||||
|
||||
environment.loginShellInit = ''
|
||||
[[ "$(tty)" == /dev/tty1 ]] && sway --unsupported-gpu
|
||||
'';
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
system.stateVersion = "23.11";
|
||||
nix.settings.experimental-features = ["nix-command" "flakes" "ca-derivations"];
|
||||
|
||||
nix.gc.automatic = true;
|
||||
nix.extraOptions = ''
|
||||
auto-optimise-store = true
|
||||
max-jobs = auto
|
||||
keep-outputs = true
|
||||
keep-derivations = true
|
||||
'';
|
||||
|
||||
programs.steam = {
|
||||
enable = true;
|
||||
remotePlay.openFirewall = true;
|
||||
};
|
||||
}
|
43
home-manager/darwin.nix
Normal file
43
home-manager/darwin.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
nixpkgs,
|
||||
username,
|
||||
dotnet,
|
||||
whisper,
|
||||
...
|
||||
}: {
|
||||
imports = [./rider];
|
||||
|
||||
rider = {
|
||||
enable = true;
|
||||
username = username;
|
||||
dotnet = dotnet;
|
||||
};
|
||||
|
||||
home.packages = [
|
||||
whisper.default
|
||||
whisper.normalize
|
||||
# "Damaged and can't be opened"
|
||||
#nixpkgs.bcompare
|
||||
#nixpkgs.gdb
|
||||
#nixpkgs.handbrake
|
||||
];
|
||||
|
||||
programs.vscode = {
|
||||
userSettings = {
|
||||
"lean.leanpkgPath" = "/Users/${username}/.elan/toolchains/stable/bin/leanpkg";
|
||||
"lean.executablePath" = "/Users/${username}/.elan/toolchains/stable/bin/lean";
|
||||
"lean.memoryLimit" = 16384;
|
||||
"latex-workshop.view.pdf.viewer" = "tab";
|
||||
"lean4.toolchainPath" = "/Users/${username}/.elan/toolchains/leanprover--lean4---nightly-2022-12-16";
|
||||
};
|
||||
};
|
||||
|
||||
programs.zsh = {
|
||||
shellAliases = {
|
||||
cmake = "cmake -DCMAKE_MAKE_PROGRAM=${nixpkgs.gnumake}/bin/make -DCMAKE_AR=${nixpkgs.darwin.cctools}/bin/ar -DCMAKE_RANLIB=${nixpkgs.darwin.cctools}/bin/ranlib -DGMP_INCLUDE_DIR=${nixpkgs.gmp.dev}/include/ -DGMP_LIBRARIES=${nixpkgs.gmp}/lib/libgmp.10.dylib";
|
||||
ar = "${nixpkgs.darwin.cctools}/bin/ar";
|
||||
};
|
||||
};
|
||||
|
||||
home.file.".ssh/config".source = ./ssh.config;
|
||||
}
|
81
home-manager/earthworm-config.nix
Normal file
81
home-manager/earthworm-config.nix
Normal file
@@ -0,0 +1,81 @@
|
||||
{pkgs, ...}: {
|
||||
imports = [
|
||||
../hardware/earthworm.nix
|
||||
];
|
||||
|
||||
hardware.asahi.peripheralFirmwareDirectory = ./../firmware;
|
||||
hardware.asahi = {
|
||||
useExperimentalGPUDriver = true;
|
||||
experimentalGPUInstallMode = "overlay";
|
||||
setupAsahiSound = true;
|
||||
withRust = true;
|
||||
};
|
||||
hardware.graphics.enable = true;
|
||||
|
||||
programs.light.enable = true;
|
||||
services.actkbd = {
|
||||
enable = true;
|
||||
bindings = [
|
||||
{
|
||||
keys = [225];
|
||||
events = ["key"];
|
||||
command = "${pkgs.light}/bin/light -A 10";
|
||||
}
|
||||
{
|
||||
keys = [224];
|
||||
events = ["key"];
|
||||
command = "${pkgs.light}/bin/light -U 10";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
boot.extraModprobeConfig = ''
|
||||
options hid_apple iso_layout=0
|
||||
'';
|
||||
|
||||
networking = {
|
||||
hostName = "earthworm";
|
||||
networkmanager.enable = true;
|
||||
wireless.iwd = {
|
||||
enable = true;
|
||||
settings.General.EnableNetworkConfiguration = true;
|
||||
};
|
||||
};
|
||||
|
||||
time.timeZone = "Europe/London";
|
||||
|
||||
programs.sway.enable = true;
|
||||
programs.zsh.enable = true;
|
||||
|
||||
# TODO: work out secrets management for password, then set mutableUsers to false
|
||||
users.mutableUsers = true;
|
||||
users.users.patrick = {
|
||||
isNormalUser = true;
|
||||
extraGroups = ["wheel" "networkManager"];
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.vim
|
||||
pkgs.wget
|
||||
pkgs.mesa-asahi-edge
|
||||
];
|
||||
|
||||
environment.loginShellInit = ''
|
||||
[[ "$(tty)" == /dev/tty1 ]] && export WLR_RENDER_NO_EXPLICIT_SYNC=1 && sway
|
||||
'';
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
system.stateVersion = "23.11";
|
||||
nix.settings.experimental-features = ["nix-command" "flakes" "ca-derivations"];
|
||||
|
||||
nix.gc.automatic = true;
|
||||
nix.extraOptions = ''
|
||||
auto-optimise-store = true
|
||||
max-jobs = auto
|
||||
keep-outputs = true
|
||||
keep-derivations = true
|
||||
'';
|
||||
}
|
425
home-manager/home.nix
Normal file
425
home-manager/home.nix
Normal file
@@ -0,0 +1,425 @@
|
||||
{
|
||||
nixpkgs,
|
||||
machinename,
|
||||
username,
|
||||
mbsync,
|
||||
dotnet,
|
||||
secretsPath,
|
||||
...
|
||||
}: {
|
||||
# Let Home Manager install and manage itself.
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# Home Manager needs a bit of information about you and the
|
||||
# paths it should manage.
|
||||
home.username = username;
|
||||
|
||||
# This value determines the Home Manager release that your
|
||||
# configuration is compatible with. This helps avoid breakage
|
||||
# when a new Home Manager release introduces backwards
|
||||
# incompatible changes.
|
||||
#
|
||||
# You can update Home Manager without changing this value. See
|
||||
# the Home Manager release notes for a list of state version
|
||||
# changes in each release.
|
||||
home.stateVersion = "22.05";
|
||||
|
||||
fonts.fontconfig.enable = true;
|
||||
|
||||
imports = [
|
||||
# ./modules/agda.nix
|
||||
# ./modules/emacs.nix
|
||||
./modules/direnv.nix
|
||||
./modules/tmux.nix
|
||||
./modules/zsh.nix
|
||||
./modules/ripgrep.nix
|
||||
./modules/alacritty.nix
|
||||
./modules/rust.nix
|
||||
(import ./modules/mail.nix
|
||||
{
|
||||
inherit mbsync secretsPath;
|
||||
pkgs = nixpkgs;
|
||||
})
|
||||
];
|
||||
|
||||
programs.fzf = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
programs.git = {
|
||||
package = nixpkgs.gitAndTools.gitFull;
|
||||
enable = true;
|
||||
userName = "Smaug123";
|
||||
userEmail = "3138005+Smaug123@users.noreply.github.com";
|
||||
aliases = {
|
||||
co = "checkout";
|
||||
st = "status";
|
||||
};
|
||||
difftastic.enable = true;
|
||||
extraConfig = {
|
||||
commit.gpgsign = true;
|
||||
gpg.program = "${nixpkgs.gnupg}/bin/gpg";
|
||||
user.signingkey =
|
||||
if machinename == "darwin"
|
||||
then "7C97D679CF3BC4F9"
|
||||
else if machinename == "earthworm"
|
||||
then "6E8B1BA1148AD7C9"
|
||||
else if machinename == "capybara"
|
||||
then "AE90453E879DBCFA"
|
||||
else throw "unrecognised machine name!";
|
||||
core = {
|
||||
autocrlf = "input";
|
||||
};
|
||||
rerere = {
|
||||
enabled = true;
|
||||
};
|
||||
push = {
|
||||
default = "current";
|
||||
autoSetupRemote = true;
|
||||
};
|
||||
pull = {
|
||||
rebase = false;
|
||||
};
|
||||
init = {
|
||||
defaultBranch = "main";
|
||||
};
|
||||
advice = {
|
||||
addIgnoredFile = false;
|
||||
};
|
||||
"filter \"lfs\"" = {
|
||||
clean = "${nixpkgs.git-lfs} clean -- %f";
|
||||
smudge = "${nixpkgs.git-lfs}/bin/git-lfs smudge --skip -- %f";
|
||||
process = "${nixpkgs.git-lfs}/bin/git-lfs filter-process";
|
||||
required = true;
|
||||
};
|
||||
pull = {
|
||||
twohead = "ort";
|
||||
};
|
||||
merge = {
|
||||
conflictStyle = "diff3";
|
||||
};
|
||||
diff = {
|
||||
colorMoved = "default";
|
||||
};
|
||||
"protocol.file" = {
|
||||
allow = "always";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
programs.vscode = {
|
||||
enable = true;
|
||||
enableExtensionUpdateCheck = true;
|
||||
enableUpdateCheck = true;
|
||||
package = nixpkgs.vscode;
|
||||
extensions = import ./vscode-extensions.nix {pkgs = nixpkgs;};
|
||||
userSettings = {
|
||||
workbench.colorTheme = "Default";
|
||||
"files.Exclude" = {
|
||||
"**/.git" = true;
|
||||
"**/.DS_Store" = true;
|
||||
"**/Thumbs.db" = true;
|
||||
"**/*.olean" = true;
|
||||
"**/result" = true;
|
||||
};
|
||||
"git.path" = "${nixpkgs.git}/bin/git";
|
||||
"update.mode" = "none";
|
||||
"explorer.confirmDelete" = false;
|
||||
};
|
||||
};
|
||||
|
||||
services.syncthing = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
programs.neovim = let
|
||||
debugPyEnv = nixpkgs.python3.withPackages (ps: [ps.debugpy]);
|
||||
in {
|
||||
enable = true;
|
||||
plugins = [
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.nvim-web-devicons;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.mini-nvim;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.satellite-nvim;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.nvim-lightbulb;
|
||||
type = "lua";
|
||||
config = builtins.readFile ./nvim/nvim-lightbulb.lua;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.lean-nvim;
|
||||
type = "lua";
|
||||
config = builtins.readFile ./nvim/lean.lua;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.which-key-nvim;
|
||||
type = "lua";
|
||||
config = builtins.readFile ./nvim/which-key.lua;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.tokyonight-nvim;
|
||||
config = builtins.readFile ./nvim/tokyonight.lua;
|
||||
type = "lua";
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.nvim-treesitter.withAllGrammars;
|
||||
config = builtins.readFile ./nvim/treesitter.lua;
|
||||
type = "lua";
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.nvim-lspconfig;
|
||||
config = builtins.readFile ./nvim/lspconfig.lua;
|
||||
type = "lua";
|
||||
}
|
||||
nixpkgs.vimPlugins.telescope-nvim
|
||||
nixpkgs.vimPlugins.tagbar
|
||||
nixpkgs.vimPlugins.fzf-vim
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.roslyn-nvim;
|
||||
config = builtins.readFile ./nvim/roslyn-nvim.lua;
|
||||
type = "lua";
|
||||
}
|
||||
{
|
||||
plugin = let
|
||||
name = "coq.artifacts";
|
||||
rev = "9c5067a471322c6bb866545e88e5b28c82511865";
|
||||
in
|
||||
nixpkgs.vimUtils.buildVimPlugin {
|
||||
name = name;
|
||||
src = nixpkgs.fetchFromGitHub {
|
||||
owner = "ms-jpq";
|
||||
repo = name;
|
||||
rev = rev;
|
||||
hash = "sha256-BHm7U3pINtYamY7m26I4lQee7ccJ6AcHmYx7j1MRFDA=";
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
plugin = let
|
||||
name = "venv-selector.nvim";
|
||||
rev = "2ad34f36d498ff5193ea10f79c87688bd5284172";
|
||||
in
|
||||
nixpkgs.vimUtils.buildVimPlugin {
|
||||
name = name;
|
||||
src = nixpkgs.fetchFromGitHub {
|
||||
owner = "linux-cultist";
|
||||
repo = name;
|
||||
rev = rev;
|
||||
hash = "sha256-aOga7kJ1y3T2vDyYFl/XHOwk35ZqeUcfPUk+Pr1mIeo=";
|
||||
};
|
||||
};
|
||||
config = builtins.readFile ./nvim/venv-selector.lua;
|
||||
type = "lua";
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.Ionide-vim;
|
||||
type = "lua";
|
||||
config = builtins.readFile ./nvim/ionide-vim.lua;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.chadtree;
|
||||
config = builtins.readFile ./nvim/chadtree.lua;
|
||||
type = "lua";
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.coq_nvim;
|
||||
config = ''let g:coq_settings = { 'auto_start': 'shut-up', 'xdg': v:true }'';
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.rustaceanvim;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.LanguageClient-neovim;
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.nvim-dap;
|
||||
config = builtins.readFile ./nvim/nvim-dap.lua;
|
||||
type = "lua";
|
||||
}
|
||||
{
|
||||
plugin = nixpkgs.vimPlugins.nvim-dap-python;
|
||||
config = builtins.replaceStrings ["%PYTHONENV%"] ["${debugPyEnv}"] (builtins.readFile ./nvim/nvim-dap-python.lua);
|
||||
type = "lua";
|
||||
}
|
||||
];
|
||||
viAlias = true;
|
||||
vimAlias = true;
|
||||
vimdiffAlias = true;
|
||||
withPython3 = true;
|
||||
extraPython3Packages = ps: [
|
||||
ps.pip
|
||||
ps.pynvim
|
||||
ps.pynvim-pp
|
||||
ps.pyyaml
|
||||
ps.std2
|
||||
];
|
||||
withRuby = true;
|
||||
|
||||
extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + (builtins.replaceStrings ["_CURL_"] ["${nixpkgs.curl}/bin/curl"] (builtins.readFile ./nvim/dotnet.lua)) + "\n" + builtins.readFile ./nvim/init.lua + "\n" + builtins.readFile ./nvim/python.lua;
|
||||
};
|
||||
|
||||
home.packages =
|
||||
[
|
||||
nixpkgs.jq
|
||||
nixpkgs.difftastic
|
||||
nixpkgs.syncthing
|
||||
nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs
|
||||
nixpkgs.nodePackages_latest.bash-language-server
|
||||
nixpkgs.nodePackages_latest.vscode-json-languageserver
|
||||
nixpkgs.nodePackages_latest.vscode-langservers-extracted
|
||||
nixpkgs.hadolint
|
||||
nixpkgs.yaml-language-server
|
||||
nixpkgs.csharp-ls
|
||||
nixpkgs.netcoredbg
|
||||
nixpkgs.nil
|
||||
nixpkgs.fsautocomplete
|
||||
nixpkgs.wget
|
||||
nixpkgs.yt-dlp
|
||||
nixpkgs.cmake
|
||||
nixpkgs.gnumake
|
||||
nixpkgs.gcc
|
||||
nixpkgs.lldb
|
||||
nixpkgs.hledger
|
||||
nixpkgs.hledger-web
|
||||
dotnet
|
||||
nixpkgs.elan
|
||||
nixpkgs.coreutils-prefixed
|
||||
nixpkgs.shellcheck
|
||||
nixpkgs.universal-ctags
|
||||
nixpkgs.asciinema
|
||||
nixpkgs.git-lfs
|
||||
nixpkgs.imagemagick
|
||||
nixpkgs.nixpkgs-fmt
|
||||
nixpkgs.lnav
|
||||
nixpkgs.age
|
||||
nixpkgs.nodejs
|
||||
nixpkgs.pyright
|
||||
nixpkgs.woodpecker-agent
|
||||
nixpkgs.lynx
|
||||
nixpkgs.alejandra
|
||||
nixpkgs.ffmpeg
|
||||
nixpkgs.bat
|
||||
nixpkgs.pandoc
|
||||
nixpkgs.fd
|
||||
nixpkgs.sumneko-lua-language-server
|
||||
nixpkgs.gnupg
|
||||
nixpkgs.gh
|
||||
nixpkgs.clang-tools
|
||||
nixpkgs.deno
|
||||
nixpkgs.yazi
|
||||
nixpkgs.font-awesome
|
||||
nixpkgs.gopls
|
||||
nixpkgs.go
|
||||
]
|
||||
++ (
|
||||
if nixpkgs.stdenv.isLinux
|
||||
then [
|
||||
nixpkgs.protonmail-bridge
|
||||
nixpkgs.pinentry
|
||||
nixpkgs.signal-desktop
|
||||
nixpkgs.keepassxc
|
||||
]
|
||||
else []
|
||||
)
|
||||
++ (
|
||||
if machinename == "capybara"
|
||||
then [
|
||||
nixpkgs.steam-run
|
||||
nixpkgs.discord
|
||||
nixpkgs.anki-bin
|
||||
]
|
||||
else []
|
||||
);
|
||||
|
||||
home.file.".ideavimrc".source = ./ideavimrc;
|
||||
home.file.".config/yt-dlp/config".source = ./youtube-dl.conf;
|
||||
|
||||
programs.firefox = {
|
||||
enable = true;
|
||||
package = nixpkgs.firefox-wayland;
|
||||
profiles = {
|
||||
patrick = {
|
||||
isDefault = true;
|
||||
name = "patrick";
|
||||
search = {
|
||||
default = "Google";
|
||||
force = true;
|
||||
};
|
||||
settings = {
|
||||
# see https://github.com/TLATER/dotfiles/blob/b39af91fbd13d338559a05d69f56c5a97f8c905d/home-config/config/graphical-applications/firefox.nix
|
||||
# see https://www.ghacks.net/2015/08/18/a-comprehensive-list-of-firefox-privacy-and-security-settings/
|
||||
"browser.search.isUS" = false;
|
||||
"browser.search.region" = "GB";
|
||||
"gfx.webrender.all" = true; # enable GPU acceleration
|
||||
"media.ffmpeg.vaapi.enabled" = true;
|
||||
"widget.dmabuf.force-enabled" = true;
|
||||
"privacy.webrtc.legacyGlobalIndicator" = false;
|
||||
"app.shield.optoutstudies.enabled" = false;
|
||||
"app.update.enabled" = false;
|
||||
"app.update.auto" = false;
|
||||
"app.update.silent" = false;
|
||||
"app.update.service.enabled" = false;
|
||||
"app.update.staging.enabled" = false;
|
||||
"browser.discovery.enabled" = false;
|
||||
"browser.laterrun.enabled" = false;
|
||||
"browser.shell.checkDefaultBrowser" = false;
|
||||
"browser.rights.3.shown" = true;
|
||||
"browser.search.update" = false;
|
||||
"extensions.update.enabled" = false;
|
||||
"extensions.update.autoUpdateDefault" = false;
|
||||
"extensions.getAddons.cache.enabled" = false;
|
||||
"dom.ipc.plugins.reportCrashURL" = false;
|
||||
"extensions.webservice.discoverURL" = "http://127.0.0.1";
|
||||
"toolkit.telemetry.unified" = false;
|
||||
"toolkit.telemetry.unifiedIsOptIn" = true;
|
||||
"toolkit.telemetry.enabled" = false;
|
||||
"toolkit.telemetry.server" = "";
|
||||
"toolkit.telemetry.archive.enabled" = false;
|
||||
"lightweightThemes.update.enabled" = false;
|
||||
"startup.homepage_welcome_url" = "";
|
||||
"startup.homepage_welcome_url.additional" = "";
|
||||
"startup.homepage_override_url" = "";
|
||||
"datareporting.healthreport.uploadEnabled" = false;
|
||||
"datareporting.healthreport.documentServerURI" = "";
|
||||
"datareporting.healthreport.service.enabled" = false;
|
||||
"datareporting.healthreport.about.reportUrl" = "data:text/plain,";
|
||||
"toolkit.telemetry.cachedClientID" = "";
|
||||
"browser.selfsupport.url" = "";
|
||||
"browser.selfsupport.enabled" = false;
|
||||
"experiments.enabled" = false;
|
||||
"experiments.supported" = false;
|
||||
"experiments.activeExperiment" = false;
|
||||
"experiments.manifest.uri" = "";
|
||||
"network.allow-experiments" = false;
|
||||
"breakpad.reportURL" = "";
|
||||
"browser.tabs.crashReporting.sendReport" = false;
|
||||
"browser.newtab.preload" = false;
|
||||
"browser.newtabpage.directory.ping" = "data:text/plain,";
|
||||
"browser.newtabpage.directory.source" = "data:text/plain,";
|
||||
"browser.newtabpage.enabled" = false;
|
||||
"browser.newtabpage.enhanced" = false;
|
||||
"browser.newtabpage.introShown" = true;
|
||||
"browser.aboutHomeSnippets.updateUrl" = "https://127.0.0.1";
|
||||
"extensions.pocket.enabled" = false;
|
||||
"extensions.pocket.api" = "";
|
||||
"extensions.pocket.site" = "";
|
||||
"extensions.pocket.oAuthConsumerKey" = "";
|
||||
"social.whitelist" = "";
|
||||
"social.toast-notifications.enabled" = false;
|
||||
"social.shareDirectory" = "";
|
||||
"social.remote-install.enabled" = false;
|
||||
"social.directories" = "";
|
||||
"social.share.activationPanelEnabled" = false;
|
||||
"social.enabled" = false;
|
||||
"dom.flyweb.enabled" = false;
|
||||
"services.sync.enabled" = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
211
home-manager/linux.nix
Normal file
211
home-manager/linux.nix
Normal file
@@ -0,0 +1,211 @@
|
||||
{
|
||||
nixpkgs,
|
||||
username,
|
||||
dotnet,
|
||||
...
|
||||
}: {
|
||||
home.packages = [nixpkgs.wl-clipboard nixpkgs.jetbrains.rider];
|
||||
nixpkgs.config.firefox = {
|
||||
speechSynthesisSupport = true;
|
||||
};
|
||||
|
||||
wayland.windowManager.sway = {
|
||||
enable = true;
|
||||
config = {
|
||||
focus = {followMouse = false;};
|
||||
modifier = "Mod4";
|
||||
terminal = "alacritty";
|
||||
window = {border = 5;};
|
||||
bars = [
|
||||
{command = "${nixpkgs.waybar}/bin/waybar";}
|
||||
];
|
||||
};
|
||||
extraConfig = builtins.readFile ./sway.conf;
|
||||
};
|
||||
|
||||
programs.waybar = {
|
||||
enable = true;
|
||||
settings = {
|
||||
"bar-0" = {
|
||||
position = "bottom";
|
||||
layer = "top";
|
||||
height = 34;
|
||||
spacing = 8;
|
||||
modules-left = ["sway/workspaces" "sway/mode" "sway/scratchpad" "custom/media"];
|
||||
modules-center = ["sway/window"];
|
||||
modules-right = ["mpd" "idle_inhibitor" "pulseaudio" "network" "power-profiles-daemon" "cpu" "memory" "temperature" "backlight" "keyboard-state" "sway/language" "battery" "battery#bat2" "clock" "tray" "custom/power"];
|
||||
|
||||
"keyboard-state" = {
|
||||
"numlock" = true;
|
||||
"capslock" = true;
|
||||
"format" = "{name} {icon}";
|
||||
"format-icons" = {
|
||||
"locked" = "";
|
||||
"unlocked" = "";
|
||||
};
|
||||
};
|
||||
"sway/mode" = {
|
||||
"format" = "<span style=\"italic\">{}</span>";
|
||||
};
|
||||
"sway/scratchpad" = {
|
||||
"format" = "{icon} {count}";
|
||||
"show-empty" = false;
|
||||
"format-icons" = ["" ""];
|
||||
"tooltip" = true;
|
||||
"tooltip-format" = "{app}: {title}";
|
||||
};
|
||||
"mpd" = {
|
||||
"format" = "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ";
|
||||
"format-disconnected" = "Disconnected ";
|
||||
"format-stopped" = "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ";
|
||||
"unknown-tag" = "N/A";
|
||||
"interval" = 5;
|
||||
"consume-icons" = {
|
||||
"on" = " ";
|
||||
};
|
||||
"random-icons" = {
|
||||
"off" = "<span color=\"#f53c3c\"></span> ";
|
||||
"on" = " ";
|
||||
};
|
||||
"repeat-icons" = {
|
||||
"on" = " ";
|
||||
};
|
||||
"single-icons" = {
|
||||
"on" = "1 ";
|
||||
};
|
||||
"state-icons" = {
|
||||
"paused" = "";
|
||||
"playing" = "";
|
||||
};
|
||||
"tooltip-format" = "MPD (connected)";
|
||||
"tooltip-format-disconnected" = "MPD (disconnected)";
|
||||
};
|
||||
|
||||
"idle_inhibitor" = {
|
||||
"format" = "{icon}";
|
||||
"format-icons" = {
|
||||
"activated" = "";
|
||||
"deactivated" = "";
|
||||
};
|
||||
};
|
||||
|
||||
"tray" = {
|
||||
"spacing" = 20;
|
||||
};
|
||||
"clock" = {
|
||||
"tooltip-format" = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
|
||||
"format" = "{:%Y-%m-%d %H:%M:%S}";
|
||||
"interval" = 1;
|
||||
};
|
||||
"cpu" = {
|
||||
"format" = "{usage}% ";
|
||||
"tooltip" = false;
|
||||
};
|
||||
"memory" = {
|
||||
"format" = "{}% ";
|
||||
};
|
||||
"temperature" = {
|
||||
"critical-threshold" = 80;
|
||||
"format" = "{temperatureC}°C {icon}";
|
||||
"format-icons" = ["" "" ""];
|
||||
};
|
||||
"backlight" = {
|
||||
"format" = "{percent}% {icon}";
|
||||
"format-icons" = ["" "" "" "" "" "" "" "" ""];
|
||||
};
|
||||
"battery" = {
|
||||
"states" = {
|
||||
"warning" = 30;
|
||||
"critical" = 15;
|
||||
};
|
||||
"format" = "{capacity}% {icon}";
|
||||
"format-full" = "{capacity}% {icon}";
|
||||
"format-charging" = "{capacity}% ";
|
||||
"format-plugged" = "{capacity}% ";
|
||||
"format-alt" = "{time} {icon}";
|
||||
"format-icons" = ["" "" "" "" ""];
|
||||
};
|
||||
"battery#bat2" = {
|
||||
"bat" = "BAT2";
|
||||
};
|
||||
"power-profiles-daemon" = {
|
||||
"format" = "{icon}";
|
||||
"tooltip-format" = "Power profile: {profile}\nDriver: {driver}";
|
||||
"tooltip" = true;
|
||||
"format-icons" = {
|
||||
"default" = "";
|
||||
"performance" = "";
|
||||
"balanced" = "";
|
||||
"power-saver" = "";
|
||||
};
|
||||
};
|
||||
"network" = {
|
||||
"format-wifi" = "{essid} ({signalStrength}%) ";
|
||||
"format-ethernet" = "{bandwidthDownBytes}/{bandwidthUpBytes} ";
|
||||
"interval" = 5;
|
||||
"tooltip-format" = "{ifname} via {gwaddr} ";
|
||||
"format-linked" = "{ifname} (No IP) ";
|
||||
"format-disconnected" = "Disconnected ⚠";
|
||||
"format-alt" = "{ifname}: {ipaddr}/{cidr}";
|
||||
};
|
||||
"pulseaudio" = {
|
||||
"format" = "{volume}% {icon} {format_source}";
|
||||
"format-bluetooth" = "{volume}% {icon} {format_source}";
|
||||
"format-bluetooth-muted" = " {icon} {format_source}";
|
||||
"format-muted" = " {format_source}";
|
||||
"format-source" = "{volume}% ";
|
||||
"format-source-muted" = "";
|
||||
"format-icons" = {
|
||||
"headphone" = "";
|
||||
"hands-free" = "";
|
||||
"headset" = "";
|
||||
"phone" = "";
|
||||
"portable" = "";
|
||||
"car" = "";
|
||||
"default" = ["" "" ""];
|
||||
};
|
||||
"on-click" = "${nixpkgs.pavucontrol}/bin/pavucontrol";
|
||||
};
|
||||
"custom/media" = {
|
||||
"format" = "{icon} {text}";
|
||||
"return-type" = "json";
|
||||
"max-length" = 40;
|
||||
"format-icons" = {
|
||||
"spotify" = "";
|
||||
"default" = "🎜";
|
||||
};
|
||||
"escape" = true;
|
||||
"exec" = let
|
||||
python = nixpkgs.python312.withPackages (ppkgs: [ppkgs.pygobject3]);
|
||||
in "${python}/bin/python ${./modules/waybar/mediaplayer.py} 2> /dev/null";
|
||||
};
|
||||
"custom/power" = {
|
||||
"format" = "⏻ ";
|
||||
"tooltip" = false;
|
||||
"menu" = "on-click";
|
||||
"menu-file" = ./modules/waybar/power_menu.xml;
|
||||
"menu-actions" = {
|
||||
"shutdown" = "shutdown";
|
||||
"reboot" = "reboot";
|
||||
"suspend" = "systemctl suspend";
|
||||
"hibernate" = "systemctl hibernate";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
style = ''
|
||||
* {
|
||||
font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
services.gpg-agent = {
|
||||
enable = nixpkgs.stdenv.isLinux;
|
||||
pinentryPackage = nixpkgs.pinentry-curses;
|
||||
};
|
||||
|
||||
services.swayidle = {enable = true;};
|
||||
}
|
7
home-manager/modules/agda.nix
Normal file
7
home-manager/modules/agda.nix
Normal file
@@ -0,0 +1,7 @@
|
||||
{pkgs, ...}: {
|
||||
imports = [./emacs.nix];
|
||||
|
||||
home.packages = [
|
||||
pkgs.agda
|
||||
];
|
||||
}
|
18
home-manager/modules/alacritty.nix
Normal file
18
home-manager/modules/alacritty.nix
Normal file
@@ -0,0 +1,18 @@
|
||||
{pkgs, ...}: {
|
||||
programs.alacritty = {
|
||||
enable = true;
|
||||
settings = {
|
||||
font = {
|
||||
normal = {
|
||||
family = "FiraCode Nerd Font Mono";
|
||||
};
|
||||
};
|
||||
terminal = {shell = "${pkgs.zsh}/bin/zsh";};
|
||||
};
|
||||
};
|
||||
|
||||
home.packages = [
|
||||
pkgs.alacritty
|
||||
(pkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];})
|
||||
];
|
||||
}
|
10
home-manager/modules/direnv.nix
Normal file
10
home-manager/modules/direnv.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{pkgs, ...}: {
|
||||
home.packages = [
|
||||
pkgs.direnv
|
||||
];
|
||||
programs.direnv = {
|
||||
enable = true;
|
||||
enableZshIntegration = true;
|
||||
nix-direnv.enable = true;
|
||||
};
|
||||
}
|
14
home-manager/modules/emacs.nix
Normal file
14
home-manager/modules/emacs.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{pkgs, ...}: {
|
||||
programs.emacs = {
|
||||
enable = true;
|
||||
package = pkgs.emacs;
|
||||
extraPackages = epkgs: [epkgs.evil];
|
||||
extraConfig = ''
|
||||
(load-file (let ((coding-system-for-read 'utf-8))
|
||||
(shell-command-to-string "agda-mode locate")))
|
||||
(require 'evil)
|
||||
(evil-mode 1)
|
||||
(evil-set-undo-system 'undo-redo)
|
||||
'';
|
||||
};
|
||||
}
|
182
home-manager/modules/mail.nix
Normal file
182
home-manager/modules/mail.nix
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
pkgs,
|
||||
mbsync,
|
||||
secretsPath,
|
||||
...
|
||||
}: let
|
||||
deobfuscate = str: let
|
||||
lib = pkgs.lib;
|
||||
base64Table =
|
||||
builtins.listToAttrs
|
||||
(lib.imap0 (i: c: lib.nameValuePair c i)
|
||||
(lib.stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"));
|
||||
|
||||
# Generated using python3:
|
||||
# print(''.join([ chr(n) for n in range(1, 256) ]), file=open('ascii', 'w'))
|
||||
ascii = builtins.readFile ./mail/ascii;
|
||||
|
||||
# List of base-64 numbers
|
||||
numbers64 = map (c: base64Table.${c}) (lib.lists.reverseList (lib.stringToCharacters str));
|
||||
|
||||
# List of base-256 numbers
|
||||
numbers256 = lib.concatLists (lib.genList (
|
||||
i: let
|
||||
v =
|
||||
lib.foldl'
|
||||
(acc: el: acc * 64 + el)
|
||||
0
|
||||
(lib.sublist (i * 4) 4 numbers64);
|
||||
in [
|
||||
(lib.mod (v / 256 / 256) 256)
|
||||
(lib.mod (v / 256) 256)
|
||||
(lib.mod v 256)
|
||||
]
|
||||
) (lib.length numbers64 / 4));
|
||||
in
|
||||
# Converts base-256 numbers to ascii
|
||||
lib.concatMapStrings (
|
||||
n:
|
||||
# Can't represent the null byte in Nix..
|
||||
let
|
||||
result = lib.substring (n - 1) 1 ascii;
|
||||
in
|
||||
if result == " "
|
||||
then ""
|
||||
else result
|
||||
)
|
||||
numbers256;
|
||||
in {
|
||||
accounts.email.accounts."Gmail" = let
|
||||
address = (deobfuscate "AFTN0cWdh12c") + "gmail.com";
|
||||
in {
|
||||
notmuch.enable = true;
|
||||
neomutt = {
|
||||
enable = true;
|
||||
};
|
||||
address = address;
|
||||
flavor = "gmail.com";
|
||||
mbsync = {
|
||||
enable = true;
|
||||
create = "maildir";
|
||||
extraConfig.account = {
|
||||
AuthMechs = "XOAUTH2";
|
||||
};
|
||||
};
|
||||
userName = address;
|
||||
# This is accompanied by a developer application at Google:
|
||||
# https://console.cloud.google.com/apis/credentials
|
||||
# Create an OAuth 2.0 Client ID with type `Desktop`.
|
||||
# The Google application needs the https://mail.google.com scope; mine has
|
||||
# an authorized domain `google.com` but I don't know if that's required.
|
||||
# Enter the client ID and client secret into a two-line text file
|
||||
# named gmail-client-app.txt immediately next to the intended destination
|
||||
# secret file (the arg to mutt-oauth2.py in the invocation):
|
||||
# so here it would be /path/to/gmail-client-app.txt .
|
||||
# Run `./mail/mutt-oauth2.py /path/to/secret --authorize --verbose` once manually,
|
||||
# and that will populate /path/to/secret.
|
||||
# I've left it unencrypted here; the original uses GPG to store it encrypted at rest.
|
||||
passwordCommand = ''${pkgs.python3}/bin/python ${./mail/mutt-oauth2.py} ${secretsPath}/gmail.txt 2>/tmp/gmail-passcmd.2.txt'';
|
||||
realName = "Patrick Stevens";
|
||||
};
|
||||
|
||||
accounts.email.accounts."BTInternet" = let
|
||||
address = (deobfuscate "z5WZ2VGdz5yajlmc0FGc") + "@btinternet.com";
|
||||
in {
|
||||
notmuch.enable = true;
|
||||
neomutt = {
|
||||
enable = true;
|
||||
};
|
||||
address = address;
|
||||
imap = {
|
||||
host = "mail.btinternet.com";
|
||||
port = 993;
|
||||
tls = {
|
||||
enable = true;
|
||||
useStartTls = false;
|
||||
};
|
||||
};
|
||||
mbsync = {
|
||||
enable = true;
|
||||
create = "maildir";
|
||||
};
|
||||
realName = "Patrick Stevens";
|
||||
passwordCommand = "${pkgs.coreutils}/bin/cat ${secretsPath}/btinternet.txt";
|
||||
smtp = {
|
||||
host = "mail.btinternet.com";
|
||||
port = 465;
|
||||
tls = {
|
||||
enable = true;
|
||||
useStartTls = false;
|
||||
};
|
||||
};
|
||||
userName = address;
|
||||
primary = true;
|
||||
};
|
||||
|
||||
accounts.email.accounts."Proton" = let
|
||||
address = deobfuscate "gAya15ybj5ycuVmdlR3crNWayRXYwB0ajlmc0FGc";
|
||||
in {
|
||||
notmuch.enable = true;
|
||||
neomutt = {
|
||||
enable = true;
|
||||
};
|
||||
address = address;
|
||||
# I use the ProtonMail bridge, which sits at localhost.
|
||||
imap = {
|
||||
host = "127.0.0.1";
|
||||
port = 1143; # 8125; if using hydroxide
|
||||
tls = {
|
||||
enable = false;
|
||||
useStartTls = true;
|
||||
};
|
||||
};
|
||||
mbsync = {
|
||||
enable = true;
|
||||
create = "maildir";
|
||||
extraConfig.account = {
|
||||
# Because ProtonMail Bridge is localhost, we don't
|
||||
# care that we can only auth to it in plain text.
|
||||
AuthMechs = "LOGIN";
|
||||
};
|
||||
};
|
||||
realName = "Patrick Stevens";
|
||||
passwordCommand =
|
||||
# I store the ProtonMail Bridge password here.
|
||||
# Extracting it from a keychain would be better.
|
||||
"${pkgs.coreutils}/bin/cat ${secretsPath}/proton.txt";
|
||||
smtp = {
|
||||
host = "127.0.0.1";
|
||||
port = 1025; # 8126; if using hydroxide
|
||||
tls = {enable = false;};
|
||||
};
|
||||
userName =
|
||||
if pkgs.stdenv.isLinux
|
||||
then builtins.head (pkgs.lib.strings.splitString "@" address)
|
||||
else address;
|
||||
};
|
||||
|
||||
programs.mbsync = {
|
||||
enable = true;
|
||||
extraConfig = ''
|
||||
CopyArrivalDate yes
|
||||
'';
|
||||
package = mbsync;
|
||||
};
|
||||
programs.neomutt = {
|
||||
enable = true;
|
||||
extraConfig = ''
|
||||
set use_threads=threads sort=last-date sort_aux=date
|
||||
'';
|
||||
sidebar.enable = true;
|
||||
vimKeys = true;
|
||||
};
|
||||
|
||||
programs.notmuch.enable = true;
|
||||
|
||||
home.file.".mailcap".source = ./mail/mailcap;
|
||||
|
||||
home.packages = [
|
||||
pkgs.notmuch
|
||||
pkgs.lynx
|
||||
];
|
||||
}
|
2
home-manager/modules/mail/ascii
Normal file
2
home-manager/modules/mail/ascii
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
|
3
home-manager/modules/mail/mailcap
Normal file
3
home-manager/modules/mail/mailcap
Normal file
@@ -0,0 +1,3 @@
|
||||
text/html; lynx -force_html %s; needsterminal
|
||||
image/png; qlmanage -p %s
|
||||
image/jpeg; qlmanage -p %s
|
402
home-manager/modules/mail/mutt-oauth2.py
Normal file
402
home-manager/modules/mail/mutt-oauth2.py
Normal file
@@ -0,0 +1,402 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Mutt OAuth2 token management script, version 2020-08-07
|
||||
# Written against python 3.7.3, not tried with earlier python versions.
|
||||
#
|
||||
# Copyright (C) 2020 Alexander Perlis
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Subsequently adapted by Patrick Stevens, who hacked it up to read gmail
|
||||
# client app configuration from a file called gmail-client-app.txt that
|
||||
# lives next to the secret file.
|
||||
|
||||
'''Mutt OAuth2 token management'''
|
||||
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import imaplib
|
||||
import poplib
|
||||
import smtplib
|
||||
import base64
|
||||
import secrets
|
||||
import hashlib
|
||||
import time
|
||||
from datetime import timedelta, datetime
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import http.server
|
||||
import subprocess
|
||||
|
||||
ap = argparse.ArgumentParser(epilog='''
|
||||
This script obtains and prints a valid OAuth2 access token. State is maintained in an
|
||||
encrypted TOKENFILE. Run with "--verbose --authorize" to get started or whenever all
|
||||
tokens have expired, optionally with "--authflow" to override the default authorization
|
||||
flow. To truly start over from scratch, first delete TOKENFILE. Use "--verbose --test"
|
||||
to test the IMAP/POP/SMTP endpoints.
|
||||
''')
|
||||
ap.add_argument('-v', '--verbose', action='store_true', help='increase verbosity')
|
||||
ap.add_argument('-d', '--debug', action='store_true', help='enable debug output')
|
||||
ap.add_argument('tokenfile', help='persistent token storage')
|
||||
ap.add_argument('-a', '--authorize', action='store_true', help='manually authorize new tokens')
|
||||
ap.add_argument('--authflow', help='authcode | localhostauthcode | devicecode')
|
||||
ap.add_argument('-t', '--test', action='store_true', help='test IMAP/POP/SMTP endpoints')
|
||||
args = ap.parse_args()
|
||||
|
||||
token = {}
|
||||
path = Path(args.tokenfile)
|
||||
if path.exists():
|
||||
if 0o777 & path.stat().st_mode != 0o600:
|
||||
sys.exit('Token file has unsafe mode. Suggest deleting and starting over.')
|
||||
try:
|
||||
token = json.loads(path.read_bytes())
|
||||
except subprocess.CalledProcessError:
|
||||
sys.exit('Difficulty decrypting token file. Is your decryption agent primed for '
|
||||
'non-interactive usage, or an appropriate environment variable such as '
|
||||
'GPG_TTY set to allow interactive agent usage from inside a pipe?')
|
||||
|
||||
|
||||
client_id, client_secret = (path.parent / "gmail-client-app.txt").read_text().strip().split('\n')
|
||||
|
||||
registrations = {
|
||||
'google': {
|
||||
'authorize_endpoint': 'https://accounts.google.com/o/oauth2/auth',
|
||||
'devicecode_endpoint': 'https://oauth2.googleapis.com/device/code',
|
||||
'token_endpoint': 'https://accounts.google.com/o/oauth2/token',
|
||||
'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob',
|
||||
'imap_endpoint': 'imap.gmail.com',
|
||||
'pop_endpoint': 'pop.gmail.com',
|
||||
'smtp_endpoint': 'smtp.gmail.com',
|
||||
'sasl_method': 'OAUTHBEARER',
|
||||
'scope': 'https://mail.google.com/',
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def writetokenfile():
|
||||
'''Writes global token dictionary into token file.'''
|
||||
if not path.exists():
|
||||
path.touch(mode=0o600)
|
||||
if 0o777 & path.stat().st_mode != 0o600:
|
||||
sys.exit('Token file has unsafe mode. Suggest deleting and starting over.')
|
||||
path.write_bytes(json.dumps(token).encode('utf-8'))
|
||||
|
||||
|
||||
if args.debug:
|
||||
print('Obtained from token file:', json.dumps(token))
|
||||
if not token:
|
||||
if not args.authorize:
|
||||
sys.exit('You must run script with "--authorize" at least once.')
|
||||
print('Available app and endpoint registrations:', *registrations)
|
||||
token['registration'] = input('OAuth2 registration: ')
|
||||
token['authflow'] = input('Preferred OAuth2 flow ("authcode" or "localhostauthcode" '
|
||||
'or "devicecode"): ')
|
||||
token['email'] = input('Account e-mail address: ')
|
||||
token['access_token'] = ''
|
||||
token['access_token_expiration'] = ''
|
||||
token['refresh_token'] = ''
|
||||
writetokenfile()
|
||||
|
||||
if token['registration'] not in registrations:
|
||||
sys.exit(f'ERROR: Unknown registration "{token["registration"]}". Delete token file '
|
||||
f'and start over.')
|
||||
registration = registrations[token['registration']]
|
||||
|
||||
authflow = token['authflow']
|
||||
if args.authflow:
|
||||
authflow = args.authflow
|
||||
|
||||
baseparams = {'client_id': registration['client_id']}
|
||||
# Microsoft uses 'tenant' but Google does not
|
||||
if 'tenant' in registration:
|
||||
baseparams['tenant'] = registration['tenant']
|
||||
|
||||
|
||||
def access_token_valid():
|
||||
'''Returns True when stored access token exists and is still valid at this time.'''
|
||||
token_exp = token['access_token_expiration']
|
||||
return token_exp and datetime.now() < datetime.fromisoformat(token_exp)
|
||||
|
||||
|
||||
def update_tokens(r):
|
||||
'''Takes a response dictionary, extracts tokens out of it, and updates token file.'''
|
||||
token['access_token'] = r['access_token']
|
||||
token['access_token_expiration'] = (datetime.now() +
|
||||
timedelta(seconds=int(r['expires_in']))).isoformat()
|
||||
if 'refresh_token' in r:
|
||||
token['refresh_token'] = r['refresh_token']
|
||||
writetokenfile()
|
||||
if args.verbose:
|
||||
print(f'NOTICE: Obtained new access token, expires {token["access_token_expiration"]}.')
|
||||
|
||||
|
||||
if args.authorize:
|
||||
p = baseparams.copy()
|
||||
p['scope'] = registration['scope']
|
||||
|
||||
if authflow in ('authcode', 'localhostauthcode'):
|
||||
verifier = secrets.token_urlsafe(90)
|
||||
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest())[:-1]
|
||||
redirect_uri = registration['redirect_uri']
|
||||
listen_port = 0
|
||||
if authflow == 'localhostauthcode':
|
||||
# Find an available port to listen on
|
||||
s = socket.socket()
|
||||
s.bind(('127.0.0.1', 0))
|
||||
listen_port = s.getsockname()[1]
|
||||
s.close()
|
||||
redirect_uri = 'http://localhost:'+str(listen_port)+'/'
|
||||
# Probably should edit the port number into the actual redirect URL.
|
||||
|
||||
p.update({'login_hint': token['email'],
|
||||
'response_type': 'code',
|
||||
'redirect_uri': redirect_uri,
|
||||
'code_challenge': challenge,
|
||||
'code_challenge_method': 'S256'})
|
||||
print(registration["authorize_endpoint"] + '?' +
|
||||
urllib.parse.urlencode(p, quote_via=urllib.parse.quote))
|
||||
|
||||
authcode = ''
|
||||
if authflow == 'authcode':
|
||||
authcode = input('Visit displayed URL to retrieve authorization code. Enter '
|
||||
'code from server (might be in browser address bar): ')
|
||||
else:
|
||||
print('Visit displayed URL to authorize this application. Waiting...',
|
||||
end='', flush=True)
|
||||
|
||||
class MyHandler(http.server.BaseHTTPRequestHandler):
|
||||
'''Handles the browser query resulting from redirect to redirect_uri.'''
|
||||
|
||||
# pylint: disable=C0103
|
||||
def do_HEAD(self):
|
||||
'''Response to a HEAD requests.'''
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
'''For GET request, extract code parameter from URL.'''
|
||||
# pylint: disable=W0603
|
||||
global authcode
|
||||
querystring = urllib.parse.urlparse(self.path).query
|
||||
querydict = urllib.parse.parse_qs(querystring)
|
||||
if 'code' in querydict:
|
||||
authcode = querydict['code'][0]
|
||||
self.do_HEAD()
|
||||
self.wfile.write(b'<html><head><title>Authorizaton result</title></head>')
|
||||
self.wfile.write(b'<body><p>Authorization redirect completed. You may '
|
||||
b'close this window.</p></body></html>')
|
||||
with http.server.HTTPServer(('127.0.0.1', listen_port), MyHandler) as httpd:
|
||||
try:
|
||||
httpd.handle_request()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
if not authcode:
|
||||
sys.exit('Did not obtain an authcode.')
|
||||
|
||||
for k in 'response_type', 'login_hint', 'code_challenge', 'code_challenge_method':
|
||||
del p[k]
|
||||
p.update({'grant_type': 'authorization_code',
|
||||
'code': authcode,
|
||||
'client_secret': registration['client_secret'],
|
||||
'code_verifier': verifier})
|
||||
print('Exchanging the authorization code for an access token')
|
||||
try:
|
||||
response = urllib.request.urlopen(registration['token_endpoint'],
|
||||
urllib.parse.urlencode(p).encode())
|
||||
except urllib.error.HTTPError as err:
|
||||
print(err.code, err.reason)
|
||||
response = err
|
||||
response = response.read()
|
||||
if args.debug:
|
||||
print(response)
|
||||
response = json.loads(response)
|
||||
if 'error' in response:
|
||||
print(response['error'])
|
||||
if 'error_description' in response:
|
||||
print(response['error_description'])
|
||||
sys.exit(1)
|
||||
|
||||
elif authflow == 'devicecode':
|
||||
try:
|
||||
response = urllib.request.urlopen(registration['devicecode_endpoint'],
|
||||
urllib.parse.urlencode(p).encode())
|
||||
except urllib.error.HTTPError as err:
|
||||
print(err.code, err.reason)
|
||||
response = err
|
||||
response = response.read()
|
||||
if args.debug:
|
||||
print(response)
|
||||
response = json.loads(response)
|
||||
if 'error' in response:
|
||||
print(response['error'])
|
||||
if 'error_description' in response:
|
||||
print(response['error_description'])
|
||||
sys.exit(1)
|
||||
print(response['message'])
|
||||
del p['scope']
|
||||
p.update({'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',
|
||||
'client_secret': registration['client_secret'],
|
||||
'device_code': response['device_code']})
|
||||
interval = int(response['interval'])
|
||||
print('Polling...', end='', flush=True)
|
||||
while True:
|
||||
time.sleep(interval)
|
||||
print('.', end='', flush=True)
|
||||
try:
|
||||
response = urllib.request.urlopen(registration['token_endpoint'],
|
||||
urllib.parse.urlencode(p).encode())
|
||||
except urllib.error.HTTPError as err:
|
||||
# Not actually always an error, might just mean "keep trying..."
|
||||
response = err
|
||||
response = response.read()
|
||||
if args.debug:
|
||||
print(response)
|
||||
response = json.loads(response)
|
||||
if 'error' not in response:
|
||||
break
|
||||
if response['error'] == 'authorization_declined':
|
||||
print(' user declined authorization.')
|
||||
sys.exit(1)
|
||||
if response['error'] == 'expired_token':
|
||||
print(' too much time has elapsed.')
|
||||
sys.exit(1)
|
||||
if response['error'] != 'authorization_pending':
|
||||
print(response['error'])
|
||||
if 'error_description' in response:
|
||||
print(response['error_description'])
|
||||
sys.exit(1)
|
||||
print()
|
||||
|
||||
else:
|
||||
sys.exit(f'ERROR: Unknown OAuth2 flow "{token["authflow"]}. Delete token file and '
|
||||
f'start over.')
|
||||
|
||||
update_tokens(response)
|
||||
|
||||
|
||||
if not access_token_valid():
|
||||
if args.verbose:
|
||||
print('NOTICE: Invalid or expired access token; using refresh token '
|
||||
'to obtain new access token.')
|
||||
if not token['refresh_token']:
|
||||
sys.exit('ERROR: No refresh token. Run script with "--authorize".')
|
||||
p = baseparams.copy()
|
||||
p.update({'client_secret': registration['client_secret'],
|
||||
'refresh_token': token['refresh_token'],
|
||||
'grant_type': 'refresh_token'})
|
||||
try:
|
||||
response = urllib.request.urlopen(registration['token_endpoint'],
|
||||
urllib.parse.urlencode(p).encode())
|
||||
except urllib.error.HTTPError as err:
|
||||
print(err.code, err.reason)
|
||||
response = err
|
||||
response = response.read()
|
||||
if args.debug:
|
||||
print(response)
|
||||
response = json.loads(response)
|
||||
if 'error' in response:
|
||||
print(response['error'])
|
||||
if 'error_description' in response:
|
||||
print(response['error_description'])
|
||||
print('Perhaps refresh token invalid. Try running once with "--authorize"')
|
||||
sys.exit(1)
|
||||
update_tokens(response)
|
||||
|
||||
|
||||
if not access_token_valid():
|
||||
sys.exit('ERROR: No valid access token. This should not be able to happen.')
|
||||
|
||||
|
||||
if args.verbose:
|
||||
print('Access Token: ', end='')
|
||||
print(token['access_token'])
|
||||
|
||||
|
||||
def build_sasl_string(user, host, port, bearer_token):
|
||||
'''Build appropriate SASL string, which depends on cloud server's supported SASL method.'''
|
||||
if registration['sasl_method'] == 'OAUTHBEARER':
|
||||
return f'n,a={user},\1host={host}\1port={port}\1auth=Bearer {bearer_token}\1\1'
|
||||
if registration['sasl_method'] == 'XOAUTH2':
|
||||
return f'user={user}\1auth=Bearer {bearer_token}\1\1'
|
||||
sys.exit(f'Unknown SASL method {registration["sasl_method"]}.')
|
||||
|
||||
|
||||
if args.test:
|
||||
errors = False
|
||||
|
||||
imap_conn = imaplib.IMAP4_SSL(registration['imap_endpoint'])
|
||||
sasl_string = build_sasl_string(token['email'], registration['imap_endpoint'], 993,
|
||||
token['access_token'])
|
||||
if args.debug:
|
||||
imap_conn.debug = 4
|
||||
try:
|
||||
imap_conn.authenticate(registration['sasl_method'], lambda _: sasl_string.encode())
|
||||
# Microsoft has a bug wherein a mismatch between username and token can still report a
|
||||
# successful login... (Try a consumer login with the token from a work/school account.)
|
||||
# Fortunately subsequent commands fail with an error. Thus we follow AUTH with another
|
||||
# IMAP command before reporting success.
|
||||
imap_conn.list()
|
||||
if args.verbose:
|
||||
print('IMAP authentication succeeded')
|
||||
except imaplib.IMAP4.error as e:
|
||||
print('IMAP authentication FAILED (does your account allow IMAP?):', e)
|
||||
errors = True
|
||||
|
||||
pop_conn = poplib.POP3_SSL(registration['pop_endpoint'])
|
||||
sasl_string = build_sasl_string(token['email'], registration['pop_endpoint'], 995,
|
||||
token['access_token'])
|
||||
if args.debug:
|
||||
pop_conn.set_debuglevel(2)
|
||||
try:
|
||||
# poplib doesn't have an auth command taking an authenticator object
|
||||
# Microsoft requires a two-line SASL for POP
|
||||
# pylint: disable=W0212
|
||||
pop_conn._shortcmd('AUTH ' + registration['sasl_method'])
|
||||
pop_conn._shortcmd(base64.standard_b64encode(sasl_string.encode()).decode())
|
||||
if args.verbose:
|
||||
print('POP authentication succeeded')
|
||||
except poplib.error_proto as e:
|
||||
print('POP authentication FAILED (does your account allow POP?):', e.args[0].decode())
|
||||
errors = True
|
||||
|
||||
# SMTP_SSL would be simpler but Microsoft does not answer on port 465.
|
||||
smtp_conn = smtplib.SMTP(registration['smtp_endpoint'], 587)
|
||||
sasl_string = build_sasl_string(token['email'], registration['smtp_endpoint'], 587,
|
||||
token['access_token'])
|
||||
smtp_conn.ehlo('test')
|
||||
smtp_conn.starttls()
|
||||
smtp_conn.ehlo('test')
|
||||
if args.debug:
|
||||
smtp_conn.set_debuglevel(2)
|
||||
try:
|
||||
smtp_conn.auth(registration['sasl_method'], lambda _=None: sasl_string)
|
||||
if args.verbose:
|
||||
print('SMTP authentication succeeded')
|
||||
except smtplib.SMTPAuthenticationError as e:
|
||||
print('SMTP authentication FAILED:', e)
|
||||
errors = True
|
||||
|
||||
if errors:
|
||||
sys.exit(1)
|
||||
|
7
home-manager/modules/ripgrep.nix
Normal file
7
home-manager/modules/ripgrep.nix
Normal file
@@ -0,0 +1,7 @@
|
||||
{pkgs, ...}: {
|
||||
home.packages = [
|
||||
pkgs.ripgrep
|
||||
];
|
||||
|
||||
home.file.".config/ripgrep/config".source = ./ripgrep/ripgrep.conf;
|
||||
}
|
6
home-manager/modules/ripgrep/ripgrep.conf
Normal file
6
home-manager/modules/ripgrep/ripgrep.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
# Don't let ripgrep vomit really long lines to my terminal, and show a preview.
|
||||
--max-columns=150
|
||||
--max-columns-preview
|
||||
|
||||
# Ignore contents of .git
|
||||
--glob=!.git/*
|
10
home-manager/modules/rust.nix
Normal file
10
home-manager/modules/rust.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{pkgs, ...}: {
|
||||
programs.zsh.sessionVariables = {
|
||||
RUSTFLAGS = "-L ${pkgs.libiconv}/lib -L ${pkgs.libcxx}/lib";
|
||||
RUST_BACKTRACE = "full";
|
||||
};
|
||||
home.file.".cargo/config.toml".source = ./rust/cargo-config.toml;
|
||||
home.packages = [
|
||||
pkgs.rust-analyzer
|
||||
];
|
||||
}
|
2
home-manager/modules/rust/cargo-config.toml
Normal file
2
home-manager/modules/rust/cargo-config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[registries.crates-io]
|
||||
protocol = "sparse"
|
19
home-manager/modules/tmux.nix
Normal file
19
home-manager/modules/tmux.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
{pkgs, ...}: {
|
||||
imports = [./zsh.nix];
|
||||
home.packages = [
|
||||
pkgs.tmux
|
||||
];
|
||||
|
||||
programs.tmux = {
|
||||
shell = "${pkgs.zsh}/bin/zsh";
|
||||
escapeTime = 50;
|
||||
mouse = false;
|
||||
prefix = "C-b";
|
||||
enable = true;
|
||||
terminal = "screen-256color";
|
||||
extraConfig = ''
|
||||
set-option -sa terminal-features ',xterm-256color:RGB'
|
||||
set -g default-command "exec ${pkgs.zsh}/bin/zsh"
|
||||
'';
|
||||
};
|
||||
}
|
199
home-manager/modules/waybar/mediaplayer.py
Normal file
199
home-manager/modules/waybar/mediaplayer.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MIT licenced, https://github.com/Alexays/Waybar/blob/dacecb9b265c1c7c36ee43d17526fa95f4e6596f/resources/custom_modules/mediaplayer.py
|
||||
# See licence in power_menu.xml
|
||||
|
||||
import gi
|
||||
gi.require_version("Playerctl", "2.0")
|
||||
from gi.repository import Playerctl, GLib
|
||||
from gi.repository.Playerctl import Player
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
import signal
|
||||
import gi
|
||||
import json
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
logger.info("Received signal to stop, exiting")
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
# loop.quit()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class PlayerManager:
|
||||
def __init__(self, selected_player=None, excluded_player=[]):
|
||||
self.manager = Playerctl.PlayerManager()
|
||||
self.loop = GLib.MainLoop()
|
||||
self.manager.connect(
|
||||
"name-appeared", lambda *args: self.on_player_appeared(*args))
|
||||
self.manager.connect(
|
||||
"player-vanished", lambda *args: self.on_player_vanished(*args))
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
self.selected_player = selected_player
|
||||
self.excluded_player = excluded_player.split(',') if excluded_player else []
|
||||
|
||||
self.init_players()
|
||||
|
||||
def init_players(self):
|
||||
for player in self.manager.props.player_names:
|
||||
if player.name in self.excluded_player:
|
||||
continue
|
||||
if self.selected_player is not None and self.selected_player != player.name:
|
||||
logger.debug(f"{player.name} is not the filtered player, skipping it")
|
||||
continue
|
||||
self.init_player(player)
|
||||
|
||||
def run(self):
|
||||
logger.info("Starting main loop")
|
||||
self.loop.run()
|
||||
|
||||
def init_player(self, player):
|
||||
logger.info(f"Initialize new player: {player.name}")
|
||||
player = Playerctl.Player.new_from_name(player)
|
||||
player.connect("playback-status",
|
||||
self.on_playback_status_changed, None)
|
||||
player.connect("metadata", self.on_metadata_changed, None)
|
||||
self.manager.manage_player(player)
|
||||
self.on_metadata_changed(player, player.props.metadata)
|
||||
|
||||
def get_players(self) -> List[Player]:
|
||||
return self.manager.props.players
|
||||
|
||||
def write_output(self, text, player):
|
||||
logger.debug(f"Writing output: {text}")
|
||||
|
||||
output = {"text": text,
|
||||
"class": "custom-" + player.props.player_name,
|
||||
"alt": player.props.player_name}
|
||||
|
||||
sys.stdout.write(json.dumps(output) + "\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
def clear_output(self):
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
def on_playback_status_changed(self, player, status, _=None):
|
||||
logger.debug(f"Playback status changed for player {player.props.player_name}: {status}")
|
||||
self.on_metadata_changed(player, player.props.metadata)
|
||||
|
||||
def get_first_playing_player(self):
|
||||
players = self.get_players()
|
||||
logger.debug(f"Getting first playing player from {len(players)} players")
|
||||
if len(players) > 0:
|
||||
# if any are playing, show the first one that is playing
|
||||
# reverse order, so that the most recently added ones are preferred
|
||||
for player in players[::-1]:
|
||||
if player.props.status == "Playing":
|
||||
return player
|
||||
# if none are playing, show the first one
|
||||
return players[0]
|
||||
else:
|
||||
logger.debug("No players found")
|
||||
return None
|
||||
|
||||
def show_most_important_player(self):
|
||||
logger.debug("Showing most important player")
|
||||
# show the currently playing player
|
||||
# or else show the first paused player
|
||||
# or else show nothing
|
||||
current_player = self.get_first_playing_player()
|
||||
if current_player is not None:
|
||||
self.on_metadata_changed(current_player, current_player.props.metadata)
|
||||
else:
|
||||
self.clear_output()
|
||||
|
||||
def on_metadata_changed(self, player, metadata, _=None):
|
||||
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||
player_name = player.props.player_name
|
||||
artist = player.get_artist()
|
||||
title = player.get_title()
|
||||
title = title.replace("&", "&")
|
||||
|
||||
track_info = ""
|
||||
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
|
||||
track_info = "Advertisement"
|
||||
elif artist is not None and title is not None:
|
||||
track_info = f"{artist} - {title}"
|
||||
else:
|
||||
track_info = title
|
||||
|
||||
if track_info:
|
||||
if player.props.status == "Playing":
|
||||
track_info = " " + track_info
|
||||
else:
|
||||
track_info = " " + track_info
|
||||
# only print output if no other player is playing
|
||||
current_playing = self.get_first_playing_player()
|
||||
if current_playing is None or current_playing.props.player_name == player.props.player_name:
|
||||
self.write_output(track_info, player)
|
||||
else:
|
||||
logger.debug(f"Other player {current_playing.props.player_name} is playing, skipping")
|
||||
|
||||
def on_player_appeared(self, _, player):
|
||||
logger.info(f"Player has appeared: {player.name}")
|
||||
if player.name in self.excluded_player:
|
||||
logger.debug(
|
||||
"New player appeared, but it's in exclude player list, skipping")
|
||||
return
|
||||
if player is not None and (self.selected_player is None or player.name == self.selected_player):
|
||||
self.init_player(player)
|
||||
else:
|
||||
logger.debug(
|
||||
"New player appeared, but it's not the selected player, skipping")
|
||||
|
||||
def on_player_vanished(self, _, player):
|
||||
logger.info(f"Player {player.props.player_name} has vanished")
|
||||
self.show_most_important_player()
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
# Increase verbosity with every occurrence of -v
|
||||
parser.add_argument("-v", "--verbose", action="count", default=0)
|
||||
|
||||
parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player")
|
||||
|
||||
# Define for which player we"re listening
|
||||
parser.add_argument("--player")
|
||||
|
||||
parser.add_argument("--enable-logging", action="store_true")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
arguments = parse_arguments()
|
||||
|
||||
# Initialize logging
|
||||
if arguments.enable_logging:
|
||||
logfile = os.path.join(os.path.dirname(
|
||||
os.path.realpath(__file__)), "media-player.log")
|
||||
logging.basicConfig(filename=logfile, level=logging.DEBUG,
|
||||
format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s")
|
||||
|
||||
# Logging is set by default to WARN and higher.
|
||||
# With every occurrence of -v it's lowered by one
|
||||
logger.setLevel(max((3 - arguments.verbose) * 10, 0))
|
||||
|
||||
logger.info("Creating player manager")
|
||||
if arguments.player:
|
||||
logger.info(f"Filtering for player: {arguments.player}")
|
||||
if arguments.exclude:
|
||||
logger.info(f"Exclude player {arguments.exclude}")
|
||||
|
||||
player = PlayerManager(arguments.player, arguments.exclude)
|
||||
player.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
53
home-manager/modules/waybar/power_menu.xml
Normal file
53
home-manager/modules/waybar/power_menu.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Source: https://github.com/Alexays/Waybar/blob/dacecb9b265c1c7c36ee43d17526fa95f4e6596f/resources/custom_modules/power_menu.xml -->
|
||||
<!-- MIT licence available at https://github.com/Alexays/Waybar/blob/dacecb9b265c1c7c36ee43d17526fa95f4e6596f/LICENSE
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Alex
|
||||
|
||||
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.
|
||||
-->
|
||||
<interface>
|
||||
<object class="GtkMenu" id="menu">
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="suspend">
|
||||
<property name="label">Suspend</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="hibernate">
|
||||
<property name="label">Hibernate</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="shutdown">
|
||||
<property name="label">Shutdown</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="reboot">
|
||||
<property name="label">Reboot</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
23
home-manager/modules/zsh.nix
Normal file
23
home-manager/modules/zsh.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
{pkgs, ...}: {
|
||||
programs.zsh = {
|
||||
enable = true;
|
||||
autocd = true;
|
||||
autosuggestion.enable = true;
|
||||
enableCompletion = true;
|
||||
history = {
|
||||
expireDuplicatesFirst = true;
|
||||
};
|
||||
sessionVariables = {
|
||||
EDITOR = "vim";
|
||||
LC_ALL = "en_US.UTF-8";
|
||||
LC_CTYPE = "en_US.UTF-8";
|
||||
};
|
||||
shellAliases = {
|
||||
vim = "nvim";
|
||||
view = "vim -R";
|
||||
};
|
||||
initExtra = builtins.readFile ./zsh/zshrc;
|
||||
};
|
||||
|
||||
programs.fzf.enableZshIntegration = true;
|
||||
}
|
36
home-manager/modules/zsh/zshrc
Normal file
36
home-manager/modules/zsh/zshrc
Normal file
@@ -0,0 +1,36 @@
|
||||
prompt_custom() {
|
||||
local cyan='%F{cyan}'
|
||||
local red='%F{red}'
|
||||
local blue='%F{blue}'
|
||||
local reset_color='%f'
|
||||
|
||||
local git_info=$(git symbolic-ref --short HEAD 2> /dev/null || git describe --tags --exact-match 2> /dev/null || git rev-parse --short HEAD 2> /dev/null)
|
||||
if [[ -n "$git_info" ]]; then
|
||||
# escape the percent character, which is the only zsh prompt metacharacter
|
||||
git_info=$git_info:s/%/%%/
|
||||
git_info=" ${blue}git:${reset_color}${red}(${git_info})${reset_color}"
|
||||
else
|
||||
git_info=""
|
||||
fi
|
||||
|
||||
# %1 is the name of cwd
|
||||
PROMPT="${cyan}%1~${reset_color}${git_info} > "
|
||||
}
|
||||
|
||||
# Full path to cwd, with `~` for any initial home component, in light green,
|
||||
RPROMPT='%F{155}%~%f'
|
||||
|
||||
precmd_functions+=(prompt_custom)
|
||||
|
||||
export WORDCHARS=''
|
||||
|
||||
autoload edit-command-line
|
||||
zle -N edit-command-line
|
||||
bindkey '^X^E' edit-command-line
|
||||
bindkey -e
|
||||
|
||||
PATH="$PATH:$HOME/.cargo/bin"
|
||||
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
tmux new-session -A -s default
|
||||
fi
|
110
home-manager/nvim/build-utils.lua
Normal file
110
home-manager/nvim/build-utils.lua
Normal file
@@ -0,0 +1,110 @@
|
||||
BuildUtils = {}
|
||||
|
||||
-- Create a new buffer and a new floating window to hold that buffer.
|
||||
local function create_floating_window()
|
||||
-- Create a new buffer for build output
|
||||
local buf = vim.api.nvim_create_buf(false, true) -- No listed, scratch buffer
|
||||
|
||||
-- Calculate window size and position here (example: full width, 10 lines high at the bottom)
|
||||
local width = vim.api.nvim_get_option_value("columns", {})
|
||||
local height = vim.api.nvim_get_option_value("lines", {})
|
||||
local win_height = math.min(10, math.floor(height * 0.2)) -- 20% of total height or 10 lines
|
||||
local win_opts = {
|
||||
relative = "editor",
|
||||
width = width,
|
||||
height = win_height,
|
||||
col = 0,
|
||||
row = height - win_height,
|
||||
style = "minimal",
|
||||
border = "single",
|
||||
}
|
||||
|
||||
local win = vim.api.nvim_open_win(buf, false, win_opts)
|
||||
|
||||
return { window = win, buffer = buf }
|
||||
end
|
||||
|
||||
local function _on_output(context, is_stdout, err, data, on_line)
|
||||
local prefix
|
||||
if is_stdout then
|
||||
prefix = "OUT"
|
||||
else
|
||||
prefix = "ERR"
|
||||
end
|
||||
if err or data then
|
||||
vim.schedule(function()
|
||||
if err then
|
||||
-- Append the error message to the buffer
|
||||
local count = vim.api.nvim_buf_line_count(context.buffer)
|
||||
vim.api.nvim_buf_set_lines(context.buffer, count, count, false, { "error " .. prefix .. ": " .. err })
|
||||
end
|
||||
if data then
|
||||
-- Append the data to the buffer
|
||||
local count = vim.api.nvim_buf_line_count(context.buffer)
|
||||
vim.api.nvim_buf_set_lines(
|
||||
context.buffer,
|
||||
count,
|
||||
count,
|
||||
false,
|
||||
vim.tbl_map(function(line)
|
||||
return prefix .. ": " .. line
|
||||
end, vim.split(data, "\n"))
|
||||
)
|
||||
end
|
||||
if vim.api.nvim_win_is_valid(context.window) then
|
||||
local cur_win = vim.api.nvim_get_current_win()
|
||||
local cur_buf = vim.api.nvim_win_get_buf(cur_win)
|
||||
if cur_buf ~= context.buffer then
|
||||
local new_line_count = vim.api.nvim_buf_line_count(context.buffer)
|
||||
vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 })
|
||||
end
|
||||
end
|
||||
|
||||
on_line(data, is_stdout, context)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Arguments:
|
||||
-- * exe, a string (no need to escape this)
|
||||
-- * args, a table like { "-m", "venv", vim.fn.shellescape(some_path) }
|
||||
-- * description of this process, visible to the user, e.g. "venv creation"
|
||||
-- * context, the result of `create_floating_window`
|
||||
-- * on_line, a function which takes "the string written", (true if stdout else false), and the context table; should return nothing. We'll call that on every line of stdout and stderr.
|
||||
-- * on_complete, takes `context`, `code` (exit code) and `signal` ("documented" with neovim's uv.spawn, hah)
|
||||
local function run_external(exe, args, description, context, on_line, on_complete)
|
||||
local handle
|
||||
local stdout = vim.uv.new_pipe(false)
|
||||
local stderr = vim.uv.new_pipe(false)
|
||||
handle, _ = vim.uv.spawn(
|
||||
exe,
|
||||
{
|
||||
args = args,
|
||||
stdio = { nil, stdout, stderr },
|
||||
},
|
||||
vim.schedule_wrap(function(code, signal)
|
||||
stdout:read_stop()
|
||||
stderr:read_stop()
|
||||
stdout:close()
|
||||
stderr:close()
|
||||
handle:close()
|
||||
print("External process " .. description .. " completed, exit code " .. code .. " and signal " .. signal)
|
||||
on_complete(context, code, signal)
|
||||
end)
|
||||
)
|
||||
|
||||
if not handle then
|
||||
print("Failed to start " .. description .. " process.")
|
||||
return
|
||||
end
|
||||
|
||||
vim.uv.read_start(stdout, function(err, data)
|
||||
_on_output(context, true, err, data, on_line)
|
||||
end)
|
||||
vim.uv.read_start(stderr, function(err, data)
|
||||
_on_output(context, false, err, data, on_line)
|
||||
end)
|
||||
end
|
||||
|
||||
BuildUtils.create_window = create_floating_window
|
||||
BuildUtils.run = run_external
|
57
home-manager/nvim/chadtree.lua
Normal file
57
home-manager/nvim/chadtree.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
vim.g.chadtree_settings = { xdg = true }
|
||||
|
||||
vim.api.nvim_create_autocmd("VimEnter", {
|
||||
pattern = "*",
|
||||
command = "CHADopen --nofocus",
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("BufEnter", {
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
if vim.fn.winnr("$") == 1 and vim.bo.filetype == "CHADTree" then
|
||||
vim.cmd("quit")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Variable to store the CHADtree window ID
|
||||
local chadtree_winid_and_buf = nil
|
||||
|
||||
-- Function to check if a window is displaying CHADtree
|
||||
local function is_chadtree_window(winid)
|
||||
local bufnr = vim.api.nvim_win_get_buf(winid)
|
||||
local filetype = vim.api.nvim_get_option_value("filetype", { buf = bufnr })
|
||||
return filetype == "CHADTree"
|
||||
end
|
||||
|
||||
-- Function to find and store the CHADtree window ID
|
||||
local function find_chadtree_window()
|
||||
for _, winid in ipairs(vim.api.nvim_list_wins()) do
|
||||
if is_chadtree_window(winid) then
|
||||
chadtree_winid_and_buf = { winid, vim.api.nvim_win_get_buf(winid) }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to switch to CHADtree buffer in the CHADtree window
|
||||
local function switch_to_chadtree()
|
||||
if chadtree_winid_and_buf and vim.api.nvim_win_is_valid(chadtree_winid_and_buf[1]) then
|
||||
local current_winid = vim.api.nvim_get_current_win()
|
||||
if current_winid == chadtree_winid_and_buf[1] and not is_chadtree_window(current_winid) then
|
||||
print("CHADtree window may only point to CHADtree")
|
||||
vim.api.nvim_win_set_buf(chadtree_winid_and_buf[1], chadtree_winid_and_buf[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Autocommand to find the CHADtree window after startup
|
||||
vim.api.nvim_create_autocmd("VimEnter", {
|
||||
callback = function()
|
||||
vim.defer_fn(find_chadtree_window, 500)
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("BufEnter", {
|
||||
callback = switch_to_chadtree,
|
||||
})
|
765
home-manager/nvim/dotnet.lua
Normal file
765
home-manager/nvim/dotnet.lua
Normal file
@@ -0,0 +1,765 @@
|
||||
local dotnet_has_set_status_line
|
||||
|
||||
function DetachSolution()
|
||||
vim.g.current_sln_path = nil
|
||||
-- TODO: unregister key bindings again
|
||||
end
|
||||
|
||||
local function on_line(data, _, context)
|
||||
-- Keep the window alive if there were warnings
|
||||
if string.match(data, "%s[1-9]%d* Warning%(s%)") then
|
||||
context.warn = context.warn + 1
|
||||
end
|
||||
end
|
||||
local function on_complete(context, code, _)
|
||||
if code ~= 0 then
|
||||
print("Exit code " .. code)
|
||||
context.errs = context.errs + 1
|
||||
end
|
||||
|
||||
if context.errs == 0 and context.warn == 0 then
|
||||
-- Close the temporary floating window (but keep it alive if the
|
||||
-- cursor is in it)
|
||||
local cur_win = vim.api.nvim_get_current_win()
|
||||
local cur_buf = vim.api.nvim_win_get_buf(cur_win)
|
||||
if cur_buf ~= context.buffer then
|
||||
vim.api.nvim_win_close(context.window, true)
|
||||
end
|
||||
print("All builds successful")
|
||||
end
|
||||
end
|
||||
|
||||
function GetCurrentSln()
|
||||
if vim.g.current_sln_path then
|
||||
return vim.g.current_sln_path
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function BuildDotNetSolution()
|
||||
if vim.g.current_sln_path then
|
||||
local context = BuildUtils.create_window()
|
||||
context.errs = 0
|
||||
context.warn = 0
|
||||
BuildUtils.run("dotnet", { "build", vim.g.current_sln_path }, "dotnet build", context, on_line, on_complete)
|
||||
end
|
||||
end
|
||||
|
||||
function TestDotNetSolution()
|
||||
if vim.g.current_sln_path then
|
||||
local context = BuildUtils.create_window()
|
||||
context.warn = 0
|
||||
context.errs = 0
|
||||
BuildUtils.run("dotnet", { "test", vim.g.current_sln_path }, "dotnet test", context, on_line, on_complete)
|
||||
end
|
||||
end
|
||||
|
||||
function CurrentSlnOrEmpty()
|
||||
local sln = GetCurrentSln()
|
||||
if sln then
|
||||
return sln
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
function RegisterSolution(sln_path)
|
||||
vim.g.current_sln_path = sln_path
|
||||
|
||||
if not dotnet_has_set_status_line then
|
||||
dotnet_has_set_status_line = true
|
||||
vim.o.statusline = vim.o.statusline .. " %{v:lua.CurrentSlnOrEmpty()}"
|
||||
end
|
||||
|
||||
local whichkey = require("which-key")
|
||||
whichkey.add({
|
||||
{
|
||||
"<localleader>s",
|
||||
desc = ".NET solution",
|
||||
},
|
||||
{ "<localleader>sb", BuildDotNetSolution, desc = "Build .NET solution" },
|
||||
{ "<localleader>st", TestDotNetSolution, desc = "Test .NET solution" },
|
||||
}, { buffer = vim.api.nvim_get_current_buf() })
|
||||
end
|
||||
|
||||
local function find_nearest_slns()
|
||||
local path = vim.fn.expand("%:p:h") -- Get the full path of the current buffer's directory
|
||||
|
||||
while path and path ~= "/" do
|
||||
local sln_paths = vim.fn.glob(path .. "/*.sln", nil, true)
|
||||
if #sln_paths > 0 then
|
||||
return sln_paths
|
||||
end
|
||||
path = vim.fn.fnamemodify(path, ":h") -- Move up one directory
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
local function FindAndRegisterSolution(should_override)
|
||||
if not should_override and GetCurrentSln() ~= nil then
|
||||
RegisterSolution(GetCurrentSln())
|
||||
end
|
||||
|
||||
local solutions = find_nearest_slns()
|
||||
if not solutions or #solutions == 0 then
|
||||
print("No .sln file found in any parent directory.")
|
||||
return
|
||||
elseif #solutions == 1 then
|
||||
-- Exactly one solution found; register it directly
|
||||
RegisterSolution(solutions[1])
|
||||
elseif #solutions > 1 then
|
||||
-- Multiple solutions found; use Telescope to pick one
|
||||
local pickers = require("telescope.pickers")
|
||||
local finders = require("telescope.finders")
|
||||
local actions = require("telescope.actions")
|
||||
local action_state = require("telescope.actions.state")
|
||||
local conf = require("telescope.config").values
|
||||
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = "Select a Solution File",
|
||||
finder = finders.new_table({
|
||||
results = solutions,
|
||||
entry_maker = function(entry)
|
||||
return {
|
||||
value = entry,
|
||||
display = entry,
|
||||
ordinal = entry,
|
||||
}
|
||||
end,
|
||||
}),
|
||||
sorter = conf.generic_sorter({}),
|
||||
attach_mappings = function(prompt_bufnr, _)
|
||||
actions.select_default:replace(function()
|
||||
local selection = action_state.get_selected_entry()
|
||||
actions.close(prompt_bufnr)
|
||||
RegisterSolution(selection.value)
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, {
|
||||
pattern = "*.sln",
|
||||
callback = function()
|
||||
if GetCurrentSln() == nil then
|
||||
RegisterSolution(vim.fn.expand("%:p"))
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
pattern = { "fsharp", "cs", "fsharp_project" },
|
||||
callback = function()
|
||||
FindAndRegisterSolution(false)
|
||||
end,
|
||||
})
|
||||
|
||||
-- For what I'm sure are reasons, Lua appears to have nothing in its standard library
|
||||
---@generic K
|
||||
---@generic V1
|
||||
---@generic V2
|
||||
---@param tbl table<K, V1>
|
||||
---@param f fun(V1): V2
|
||||
---@return table<K, V2>
|
||||
local function map(tbl, f)
|
||||
local t = {}
|
||||
for k, v in pairs(tbl) do
|
||||
t[k] = f(v)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
---@generic K
|
||||
---@generic V
|
||||
---@param tbl table<K, V>
|
||||
---@param f fun(V1): nil
|
||||
local function iter(tbl, f)
|
||||
for _, v in pairs(tbl) do
|
||||
f(v)
|
||||
end
|
||||
end
|
||||
|
||||
---@generic K
|
||||
---@generic V
|
||||
---@param tbl table<K, V>
|
||||
---@param predicate fun(V): boolean
|
||||
---@return boolean, V
|
||||
local function find(tbl, predicate)
|
||||
for _, v in pairs(tbl) do
|
||||
if predicate(v) then
|
||||
return true, v
|
||||
end
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
---@class (exact) NuGetVersion
|
||||
---@field major number
|
||||
---@field minor number
|
||||
---@field patch number
|
||||
---@field suffix? string
|
||||
local NuGetVersion = {}
|
||||
|
||||
---@param v NuGetVersion
|
||||
---@nodiscard
|
||||
---@return string
|
||||
local function nuGetVersionToString(v)
|
||||
local s = tostring(v.major) .. "." .. tostring(v.minor) .. "." .. tostring(v.patch)
|
||||
if v.suffix then
|
||||
return s .. v.suffix
|
||||
else
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
local function get_all_variables()
|
||||
local variables = {}
|
||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
||||
for _, line in ipairs(lines) do
|
||||
local var_name, var_value = line:match("<(%w+)>([^<]+)</(%w+)>")
|
||||
if var_name and var_value then
|
||||
variables[var_name] = var_value
|
||||
end
|
||||
end
|
||||
return variables
|
||||
end
|
||||
|
||||
local function resolve_variable(version, variables)
|
||||
if version:match("^%$%((.+)%)$") then
|
||||
local var_name = version:match("^%$%((.+)%)$")
|
||||
return variables[var_name] or nil
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param v string
|
||||
---@nodiscard
|
||||
---@return NuGetVersion
|
||||
local function parse_version(v)
|
||||
local variables = get_all_variables()
|
||||
local major, minor, patch, pre = v:match("(%d+)%.(%d+)%.(%d+)(.*)$")
|
||||
if major == nil then
|
||||
local resolved = resolve_variable(v, variables)
|
||||
if resolved ~= nil then
|
||||
return parse_version(resolved)
|
||||
end
|
||||
end
|
||||
-- TODO: why does this type-check if you remove the field names?
|
||||
return {
|
||||
major = tonumber(major) or 0,
|
||||
minor = tonumber(minor) or 0,
|
||||
patch = tonumber(patch) or 0,
|
||||
suffix = pre or nil,
|
||||
}
|
||||
end
|
||||
|
||||
---@param a NuGetVersion
|
||||
---@param b NuGetVersion
|
||||
---@nodiscard
|
||||
---@return boolean
|
||||
local function compare_versions(a, b)
|
||||
if a.major ~= b.major then
|
||||
return a.major < b.major
|
||||
elseif a.minor ~= b.minor then
|
||||
return a.minor < b.minor
|
||||
elseif a.patch ~= b.patch then
|
||||
return a.patch < b.patch
|
||||
elseif a.suffix and not b.suffix then
|
||||
return false
|
||||
elseif not a.suffix and b.suffix then
|
||||
return true
|
||||
else
|
||||
return a.suffix < b.suffix
|
||||
end
|
||||
end
|
||||
|
||||
---@param url string
|
||||
---@nodiscard
|
||||
local function curl_sync(url)
|
||||
local command = string.format("_CURL_ --silent --compressed --fail '%s'", url)
|
||||
local response = vim.fn.system(command)
|
||||
if vim.v.shell_error ~= 0 then
|
||||
print("Failed to fetch " .. url)
|
||||
return nil
|
||||
end
|
||||
local success, decoded = pcall(vim.fn.json_decode, response)
|
||||
if not success then
|
||||
print("Failed to decode JSON from curl at " .. url)
|
||||
return nil
|
||||
end
|
||||
return decoded
|
||||
end
|
||||
|
||||
---@param url string
|
||||
---@param callback fun(table): nil
|
||||
---@return nil
|
||||
local function curl(url, callback)
|
||||
local stdout = vim.uv.new_pipe(false)
|
||||
local stdout_text = ""
|
||||
local handle
|
||||
handle, _ = vim.uv.spawn(
|
||||
"_CURL_",
|
||||
{ args = { "--silent", "--compressed", "--fail", url }, stdio = { nil, stdout, nil } },
|
||||
vim.schedule_wrap(function(code, _)
|
||||
stdout:read_stop()
|
||||
stdout:close()
|
||||
if handle and not handle:is_closing() then
|
||||
handle:close()
|
||||
end
|
||||
if code ~= 0 then
|
||||
print("Failed to fetch " .. url)
|
||||
end
|
||||
local success, decoded = pcall(vim.fn.json_decode, stdout_text)
|
||||
if not success then
|
||||
print("Failed to decode JSON from curl at " .. url .. "\n" .. stdout_text)
|
||||
end
|
||||
callback(decoded)
|
||||
end)
|
||||
)
|
||||
vim.uv.read_start(stdout, function(err, data)
|
||||
assert(not err, err)
|
||||
if data then
|
||||
stdout_text = stdout_text .. data
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local _nugetIndex
|
||||
local _packageBaseAddress
|
||||
|
||||
---@param callback fun(): nil
|
||||
local function populate_nuget_api(callback)
|
||||
if _nugetIndex ~= nil then
|
||||
callback()
|
||||
end
|
||||
local url = string.format("https://api.nuget.org/v3/index.json")
|
||||
local function handle(decoded)
|
||||
local default_nuget_reg = "https://api.nuget.org/v3/registration5-semver1/"
|
||||
local default_base_address = "https://api.nuget.org/v3-flatcontainer/"
|
||||
|
||||
if not decoded then
|
||||
print("Failed to fetch NuGet index; falling back to default")
|
||||
_nugetIndex = default_nuget_reg
|
||||
_packageBaseAddress = default_base_address
|
||||
else
|
||||
local resources = decoded["resources"]
|
||||
if resources == nil then
|
||||
print("Failed to parse: " .. decoded .. tostring(decoded))
|
||||
for k, v in pairs(decoded) do
|
||||
print(k .. ": " .. tostring(v))
|
||||
end
|
||||
callback()
|
||||
return
|
||||
end
|
||||
|
||||
local resourceSuccess, regUrl = find(resources, function(o)
|
||||
return o["@type"] == "RegistrationsBaseUrl/3.6.0"
|
||||
end)
|
||||
if not resourceSuccess then
|
||||
print("Failed to find endpoint in NuGet index; falling back to default")
|
||||
_nugetIndex = default_nuget_reg
|
||||
else
|
||||
_nugetIndex = regUrl["@id"]
|
||||
end
|
||||
|
||||
local baseAddrSuccess, baseAddrUrl = find(resources, function(o)
|
||||
return o["@type"] == "PackageBaseAddress/3.0.0"
|
||||
end)
|
||||
if not baseAddrSuccess then
|
||||
print("Failed to find endpoint in NuGet index; falling back to default")
|
||||
_packageBaseAddress = default_base_address
|
||||
else
|
||||
_packageBaseAddress = baseAddrUrl["@id"]
|
||||
end
|
||||
end
|
||||
callback()
|
||||
end
|
||||
curl(url, handle)
|
||||
end
|
||||
|
||||
---@return nil
|
||||
local function populate_nuget_api_sync()
|
||||
if _nugetIndex ~= nil then
|
||||
return
|
||||
end
|
||||
local url = string.format("https://api.nuget.org/v3/index.json")
|
||||
local decoded = curl_sync(url)
|
||||
local default_nuget_reg = "https://api.nuget.org/v3/registration5-semver1/"
|
||||
local default_base_address = "https://api.nuget.org/v3-flatcontainer/"
|
||||
|
||||
if not decoded then
|
||||
print("Failed to fetch NuGet index; falling back to default")
|
||||
_nugetIndex = default_nuget_reg
|
||||
_packageBaseAddress = default_base_address
|
||||
else
|
||||
local resources = decoded["resources"]
|
||||
|
||||
local resourceSuccess, regUrl = find(resources, function(o)
|
||||
return o["@type"] == "RegistrationsBaseUrl/3.6.0"
|
||||
end)
|
||||
if not resourceSuccess then
|
||||
print("Failed to find endpoint in NuGet index; falling back to default")
|
||||
_nugetIndex = default_nuget_reg
|
||||
else
|
||||
_nugetIndex = regUrl["@id"]
|
||||
end
|
||||
|
||||
local baseAddrSuccess, baseAddrUrl = find(resources, function(o)
|
||||
return o["@type"] == "PackageBaseAddress/3.0.0"
|
||||
end)
|
||||
if not baseAddrSuccess then
|
||||
print("Failed to find endpoint in NuGet index; falling back to default")
|
||||
_packageBaseAddress = default_base_address
|
||||
else
|
||||
_packageBaseAddress = baseAddrUrl["@id"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@return nil
|
||||
---@param callback fun(nugetIndex: string): nil
|
||||
local function get_nuget_index(callback)
|
||||
populate_nuget_api(function()
|
||||
callback(_nugetIndex)
|
||||
end)
|
||||
end
|
||||
|
||||
---@return nil
|
||||
---@param callback fun(packageBaseIndex: string): nil
|
||||
local function get_package_base_addr(callback)
|
||||
populate_nuget_api(function()
|
||||
callback(_packageBaseAddress)
|
||||
end)
|
||||
end
|
||||
|
||||
---@return string
|
||||
local function get_package_base_addr_sync()
|
||||
populate_nuget_api_sync()
|
||||
return _packageBaseAddress
|
||||
end
|
||||
|
||||
local _package_versions_cache = {}
|
||||
|
||||
---@param package_name string
|
||||
---@return NuGetVersion[]
|
||||
local function get_package_versions_sync(package_name)
|
||||
if _package_versions_cache[package_name] ~= nil then
|
||||
return _package_versions_cache[package_name]
|
||||
end
|
||||
local base = get_package_base_addr_sync()
|
||||
|
||||
local url = base .. string.format("%s/index.json", package_name:lower())
|
||||
local decoded = curl_sync(url)
|
||||
if not decoded then
|
||||
print("Failed to fetch package versions")
|
||||
return {}
|
||||
end
|
||||
|
||||
local versions = map(decoded.versions, parse_version)
|
||||
table.sort(versions, function(a, b)
|
||||
return compare_versions(b, a)
|
||||
end)
|
||||
_package_versions_cache[package_name] = versions
|
||||
return versions
|
||||
end
|
||||
|
||||
---@param package_name string
|
||||
---@param callback fun(v: NuGetVersion[]): nil
|
||||
---@return nil
|
||||
local function get_package_versions(package_name, callback)
|
||||
if _package_versions_cache[package_name] ~= nil then
|
||||
callback(_package_versions_cache[package_name])
|
||||
end
|
||||
|
||||
local function handle(base)
|
||||
local url = base .. string.format("%s/index.json", package_name:lower())
|
||||
local function handle2(decoded)
|
||||
if not decoded then
|
||||
print("Failed to fetch package versions")
|
||||
callback({})
|
||||
end
|
||||
|
||||
local versions = map(decoded.versions, parse_version)
|
||||
table.sort(versions, function(a, b)
|
||||
return compare_versions(b, a)
|
||||
end)
|
||||
_package_versions_cache[package_name] = versions
|
||||
callback(versions)
|
||||
end
|
||||
curl(url, handle2)
|
||||
end
|
||||
get_package_base_addr(handle)
|
||||
end
|
||||
|
||||
---@param version NuGetVersion
|
||||
---@return nil
|
||||
local function update_package_version(version)
|
||||
local line = vim.api.nvim_get_current_line()
|
||||
local new_line = line:gsub('Version="[^"]+"', string.format('Version="%s"', nuGetVersionToString(version)))
|
||||
vim.api.nvim_set_current_line(new_line)
|
||||
end
|
||||
|
||||
-- A map from package to { packageWeDependOn: version }.
|
||||
--- @type table<string, table<string, string>>
|
||||
local _package_dependency_cache = {}
|
||||
---@param package_name string
|
||||
---@param version NuGetVersion
|
||||
---@param callback fun(result: table<string, string>): nil
|
||||
---@return nil
|
||||
local function get_package_dependencies(package_name, version, callback)
|
||||
local key = package_name .. "@" .. nuGetVersionToString(version)
|
||||
local cache_hit = _package_dependency_cache[key]
|
||||
if cache_hit ~= nil then
|
||||
callback(cache_hit)
|
||||
return
|
||||
end
|
||||
|
||||
local function handle1(index)
|
||||
local url = index .. string.format("%s/%s.json", package_name:lower(), nuGetVersionToString(version):lower())
|
||||
|
||||
local function handle(response)
|
||||
if not response then
|
||||
print(
|
||||
"Failed to get dependencies of "
|
||||
.. package_name
|
||||
.. " at version "
|
||||
.. version
|
||||
.. " : unsuccessful response to "
|
||||
.. url
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local entry_url = response["catalogEntry"]
|
||||
local function handle2(catalog_entry)
|
||||
if not catalog_entry then
|
||||
print(
|
||||
"Failed to get dependencies of "
|
||||
.. package_name
|
||||
.. " at version "
|
||||
.. version
|
||||
.. " : unsuccessful response to "
|
||||
.. entry_url
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local result = {}
|
||||
if catalog_entry["dependencyGroups"] then
|
||||
iter(catalog_entry["dependencyGroups"], function(grp)
|
||||
if grp["dependencies"] then
|
||||
for _, dep in pairs(grp["dependencies"]) do
|
||||
result[dep["id"]] = dep["range"]
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
_package_dependency_cache[key] = result
|
||||
|
||||
callback(result)
|
||||
end
|
||||
curl(entry_url, handle2)
|
||||
end
|
||||
|
||||
curl(url, handle)
|
||||
end
|
||||
|
||||
get_nuget_index(handle1)
|
||||
end
|
||||
|
||||
---@return table<string, NuGetVersion>
|
||||
---@nodiscard
|
||||
local function get_all_package_references()
|
||||
local packages = {}
|
||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
local package_name = line:match('PackageReference Include="([^"]+)"')
|
||||
or line:match('PackageReference Update="([^"]+)"')
|
||||
local version = line:match('Version="([^"]+)"')
|
||||
|
||||
if package_name and version then
|
||||
if not packages[package_name] then
|
||||
packages[package_name] = {}
|
||||
end
|
||||
table.insert(packages[package_name], parse_version(version))
|
||||
end
|
||||
end
|
||||
|
||||
return packages
|
||||
end
|
||||
|
||||
function ClearNuGetDependencyCache()
|
||||
for k, _ in pairs(_package_dependency_cache) do
|
||||
_package_dependency_cache[k] = nil
|
||||
end
|
||||
end
|
||||
vim.api.nvim_create_user_command("ClearNuGetDependencyCache", ClearNuGetDependencyCache, {})
|
||||
|
||||
function PrintNuGetDependencyCache()
|
||||
for k, v in pairs(_package_dependency_cache) do
|
||||
print(k .. ":")
|
||||
for dep, ver in pairs(v) do
|
||||
print(" " .. dep .. ": " .. ver)
|
||||
end
|
||||
end
|
||||
end
|
||||
vim.api.nvim_create_user_command("PrintNuGetDependencyCache", PrintNuGetDependencyCache, {})
|
||||
|
||||
local function prefetch_dependencies()
|
||||
local packages = get_all_package_references()
|
||||
|
||||
local function process_package(package_name, versions, callback)
|
||||
local count = #versions
|
||||
for _, version in ipairs(versions) do
|
||||
vim.schedule(function()
|
||||
get_package_dependencies(package_name, version, function(_)
|
||||
count = count - 1
|
||||
if count == 0 then
|
||||
callback()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local total_packages = 0
|
||||
for _ in pairs(packages) do
|
||||
total_packages = total_packages + 1
|
||||
end
|
||||
|
||||
local processed_packages = 0
|
||||
for package_name, versions in pairs(packages) do
|
||||
process_package(package_name, versions, function()
|
||||
local function handle(package_versions)
|
||||
if package_versions then
|
||||
process_package(package_name, package_versions, function()
|
||||
processed_packages = processed_packages + 1
|
||||
if processed_packages == total_packages then
|
||||
print("All dependencies prefetched")
|
||||
end
|
||||
end)
|
||||
else
|
||||
processed_packages = processed_packages + 1
|
||||
if processed_packages == total_packages then
|
||||
print("All dependencies prefetched")
|
||||
end
|
||||
end
|
||||
end
|
||||
get_package_versions(package_name, handle)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
---@param v1 NuGetVersion
|
||||
---@param v2 NuGetVersion
|
||||
---@return boolean
|
||||
---@nodiscard
|
||||
local function nuget_versions_equal(v1, v2)
|
||||
return v1.major == v2.major and v1.minor == v2.minor and v1.patch == v2.patch and v1.suffix == v2.suffix
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
pattern = { "fsharp_project", "csharp_project", "xml" },
|
||||
callback = function()
|
||||
function UpdateNuGetVersion()
|
||||
local line = vim.api.nvim_get_current_line()
|
||||
local package_name = line:match('PackageReference Include="([^"]+)"')
|
||||
or line:match('PackageReference Update="([^"]+)"')
|
||||
|
||||
if not package_name then
|
||||
print("No package reference found on the current line")
|
||||
return
|
||||
end
|
||||
|
||||
local current_version = parse_version(line:match('Version="([^"]+)"'))
|
||||
if not current_version then
|
||||
print("oh no!")
|
||||
end
|
||||
|
||||
local package_versions = get_package_versions_sync(package_name)
|
||||
|
||||
if #package_versions == 0 then
|
||||
print("No versions found for the package")
|
||||
return
|
||||
end
|
||||
|
||||
local pickers = require("telescope.pickers")
|
||||
local finders = require("telescope.finders")
|
||||
local previewers = require("telescope.previewers")
|
||||
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = string.format("Select version for %s", package_name),
|
||||
finder = finders.new_table({
|
||||
results = package_versions,
|
||||
entry_maker = function(entry)
|
||||
local val = nuGetVersionToString(entry)
|
||||
local display_value = val
|
||||
if current_version and nuget_versions_equal(entry, current_version) then
|
||||
display_value = "[CURRENT] " .. val
|
||||
end
|
||||
return {
|
||||
value = entry,
|
||||
display = display_value,
|
||||
ordinal = entry,
|
||||
}
|
||||
end,
|
||||
}),
|
||||
previewer = previewers.new_buffer_previewer({
|
||||
define_preview = function(self, entry, _)
|
||||
local bufnr = self.state.bufnr
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "Loading..." })
|
||||
get_package_dependencies(package_name, entry.value, function(package_dependencies)
|
||||
if not package_dependencies then
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "No dependencies found" })
|
||||
return
|
||||
end
|
||||
|
||||
local display = {}
|
||||
table.insert(
|
||||
display,
|
||||
"Dependencies for "
|
||||
.. package_name
|
||||
.. " at version "
|
||||
.. nuGetVersionToString(entry.value)
|
||||
.. ":"
|
||||
)
|
||||
for dep, range in pairs(package_dependencies) do
|
||||
table.insert(display, dep .. ": " .. range)
|
||||
end
|
||||
local ok, err = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, display)
|
||||
if not ok then
|
||||
-- If we can't set lines, the window's probably gone. Ignore.
|
||||
return
|
||||
end
|
||||
end)
|
||||
end,
|
||||
}),
|
||||
attach_mappings = function(_, mapping)
|
||||
mapping("i", "<CR>", function(prompt_bufnr)
|
||||
local selection = require("telescope.actions.state").get_selected_entry()
|
||||
require("telescope.actions").close(prompt_bufnr)
|
||||
update_package_version(selection.value)
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
local whichkey = require("which-key")
|
||||
whichkey.add({
|
||||
{ "<localleader>n", desc = "NuGet" },
|
||||
{ "<localleader>nu", UpdateNuGetVersion, desc = "Upgrade NuGet versions" },
|
||||
}, { buffer = vim.api.nvim_get_current_buf() })
|
||||
|
||||
vim.schedule(prefetch_dependencies)
|
||||
end,
|
||||
})
|
290
home-manager/nvim/init.lua
Normal file
290
home-manager/nvim/init.lua
Normal file
@@ -0,0 +1,290 @@
|
||||
vim.opt.mouse = ""
|
||||
vim.opt.history = 500
|
||||
vim.opt.background = "dark"
|
||||
|
||||
vim.opt.signcolumn = "yes"
|
||||
|
||||
vim.opt.wildmenu = true
|
||||
vim.opt.wildignore = vim.opt.wildignore + { "*/.git/*", "*/.hg/*", "*/.svn/*", "*/.DS_Store" }
|
||||
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.smartcase = true
|
||||
vim.opt.incsearch = true
|
||||
vim.opt.magic = true
|
||||
vim.opt.hlsearch = true
|
||||
|
||||
vim.opt.autoindent = true
|
||||
vim.opt.smartindent = true
|
||||
|
||||
vim.opt.wrap = true
|
||||
vim.opt.linebreak = true
|
||||
vim.opt.textwidth = 500
|
||||
|
||||
vim.opt.switchbuf = "useopen"
|
||||
|
||||
vim.opt.laststatus = 2
|
||||
-- I don't use tabs, but one day I might!
|
||||
vim.opt.showtabline = 2
|
||||
|
||||
vim.opt.langmenu = "en"
|
||||
|
||||
vim.opt.ffs = "unix"
|
||||
vim.opt.encoding = "utf8"
|
||||
|
||||
-- Always show current position
|
||||
vim.opt.ruler = true
|
||||
vim.opt.number = true
|
||||
|
||||
-- A bit of extra margin to the left
|
||||
vim.opt.foldcolumn = "1"
|
||||
|
||||
vim.opt.autoread = true
|
||||
vim.opt.backup = false
|
||||
vim.opt.writebackup = true
|
||||
vim.opt.swapfile = false
|
||||
|
||||
vim.opt.cmdheight = 2
|
||||
|
||||
-- Use spaces instead of tabs
|
||||
vim.opt.expandtab = true
|
||||
vim.opt.smarttab = true
|
||||
vim.opt.shiftwidth = 4
|
||||
vim.opt.tabstop = 4
|
||||
|
||||
vim.opt.lazyredraw = true
|
||||
|
||||
-- Show matching brackets when text indicator is on one of them
|
||||
vim.opt.showmatch = true
|
||||
vim.opt.mat = 2
|
||||
|
||||
-- Turn off sound
|
||||
vim.opt.errorbells = false
|
||||
vim.opt.visualbell = false
|
||||
|
||||
vim.opt.timeoutlen = 500
|
||||
|
||||
vim.opt.scrolloff = 2
|
||||
|
||||
-- Return to last edit position when opening files
|
||||
vim.api.nvim_create_autocmd("BufReadPost", {
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
local line = vim.fn.line
|
||||
local last_pos = line("'\"")
|
||||
if last_pos > 1 and last_pos <= line("$") then
|
||||
vim.cmd("normal! g'\"")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Trim trailing whitespace on save
|
||||
function CleanExtraSpaces()
|
||||
local save_cursor = vim.api.nvim_win_get_cursor(0)
|
||||
local old_query = vim.fn.getreg("/")
|
||||
vim.cmd("%s/\\s\\+$//e")
|
||||
vim.api.nvim_win_set_cursor(0, save_cursor)
|
||||
vim.fn.setreg("/", old_query)
|
||||
end
|
||||
vim.api.nvim_create_autocmd("BufWritePre", {
|
||||
pattern = { "*.fs", "*.fsi", "*.txt", "*.js", "*.py", "*.wiki", "*.sh", "*.coffee" },
|
||||
callback = CleanExtraSpaces,
|
||||
})
|
||||
|
||||
-- Status line
|
||||
|
||||
-- Returns true if paste mode is enabled
|
||||
function HasPaste()
|
||||
if vim.opt.paste:get() then
|
||||
return "PASTE MODE "
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
vim.o.statusline = vim.o.statusline .. "%{v:lua.HasPaste()}%F%m%r%h %w Line: %l Column: %c"
|
||||
|
||||
--------------------------------------------------------------
|
||||
|
||||
vim.api.nvim_set_keymap("n", ";", "<Nop>", { noremap = true })
|
||||
vim.api.nvim_set_var("maplocalleader", ";")
|
||||
vim.api.nvim_set_var("mapleader", " ")
|
||||
|
||||
function MarkdownPreview()
|
||||
local temp_file = vim.fn.tempname() .. ".md"
|
||||
local file_name = vim.fn.substitute(vim.fn.tolower(vim.fn.expand("%:t")), "\\W", "_", "g")
|
||||
local temp_html = "/tmp/" .. file_name .. "_tmp.html"
|
||||
|
||||
-- Write the current buffer to the temp file
|
||||
vim.cmd("write! " .. temp_file)
|
||||
|
||||
local pandoc_cmd = "pandoc " .. temp_file .. " -o " .. temp_html
|
||||
|
||||
-- Execute the pandoc command
|
||||
vim.fn.system(pandoc_cmd)
|
||||
|
||||
-- Use tmux and lynx to preview the HTML file
|
||||
local lynx_cmd = "tmux split-window -h lynx " .. temp_html
|
||||
vim.fn.jobstart(vim.split(lynx_cmd, " "), { silent = true })
|
||||
|
||||
-- Delete the temp markdown file
|
||||
vim.fn.delete(temp_file, "rf")
|
||||
end
|
||||
|
||||
function RemoveCarriageReturn()
|
||||
vim.cmd("mark m")
|
||||
vim.cmd("normal! Hmt")
|
||||
vim.cmd("%s/\r//ge")
|
||||
vim.cmd("normal! 'tzt'm")
|
||||
end
|
||||
|
||||
function FormatJson()
|
||||
vim.cmd("%!python -m json.tool")
|
||||
end
|
||||
|
||||
function ChangeToCurrentDirectory()
|
||||
vim.cmd(":cd %:p:h")
|
||||
vim.cmd(":pwd")
|
||||
end
|
||||
|
||||
local function close_loclist_if_orphaned()
|
||||
local win = vim.fn.expand("<afile>")
|
||||
vim.fn.win_execute(win, "lclose")
|
||||
end
|
||||
|
||||
-- Set up an autocmd using the nvim_create_autocmd API
|
||||
vim.api.nvim_create_autocmd("WinClosed", {
|
||||
pattern = "*",
|
||||
callback = close_loclist_if_orphaned,
|
||||
})
|
||||
|
||||
local whichkey = require("which-key")
|
||||
local pickers = require("telescope.pickers")
|
||||
local action_state = require("telescope.actions.state")
|
||||
local actions = require("telescope.actions")
|
||||
local finders = require("telescope.finders")
|
||||
local conf = require("telescope.config").values
|
||||
|
||||
function DisplayAllMappingsWithTelescope()
|
||||
local mappings = {}
|
||||
local commands = {} -- Store commands keyed by the display string
|
||||
|
||||
local function accumulate(tree)
|
||||
tree:walk(function(node)
|
||||
if node.mapping then
|
||||
local mapping = node.mapping
|
||||
if not mapping.group then
|
||||
local description = mapping.desc or mapping.label or mapping.cmd
|
||||
-- Some actions are just there for which-key to hook into to display prefixes; they don't have a description.
|
||||
if description then
|
||||
local displayString = description .. " | " .. mapping.prefix
|
||||
commands[displayString] = mapping.prefix
|
||||
mappings[#mappings + 1] = displayString
|
||||
else
|
||||
for k, v in pairs(mapping) do
|
||||
print("Nothing: " .. k .. " : " .. tostring(v) .. " (type: " .. type(v) .. ")")
|
||||
end
|
||||
print("-----")
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local cur_buf = vim.api.nvim_win_get_buf(0)
|
||||
|
||||
accumulate(require("which-key.keys").get_tree("n").tree)
|
||||
accumulate(require("which-key.keys").get_tree("n", cur_buf).tree)
|
||||
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = "Actions",
|
||||
finder = finders.new_table({
|
||||
results = mappings,
|
||||
}),
|
||||
sorter = conf.generic_sorter({}),
|
||||
attach_mappings = function(_, map)
|
||||
map("i", "<CR>", function(bufnr)
|
||||
local selection = action_state.get_selected_entry()
|
||||
actions.close(bufnr)
|
||||
local cmd = commands[selection.value]
|
||||
if cmd then
|
||||
vim.api.nvim_command(":normal " .. vim.api.nvim_replace_termcodes(cmd, true, true, true))
|
||||
else
|
||||
print("no command found")
|
||||
end
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
|
||||
function ToggleSpell()
|
||||
vim.cmd("setlocal spell!")
|
||||
end
|
||||
|
||||
whichkey.add({
|
||||
{
|
||||
"<localleader><localleader>",
|
||||
function()
|
||||
require("which-key").show({ global = false })
|
||||
end,
|
||||
desc = "View all mappings",
|
||||
},
|
||||
{ "<localleader>mp", MarkdownPreview, desc = "Preview Markdown in Lynx" },
|
||||
{ "<localleader>md", RemoveCarriageReturn, desc = "Delete carriage returns from file" },
|
||||
{ "<localleader>j", FormatJson, desc = "Auto-format JSON" },
|
||||
})
|
||||
whichkey.add({
|
||||
{
|
||||
"<leader>g",
|
||||
function()
|
||||
require("telescope.builtin").grep_string()
|
||||
end,
|
||||
desc = "Find instances of text under cursor",
|
||||
},
|
||||
{ "<leader>h", desc = "Find historical..." },
|
||||
{
|
||||
"<leader>hf",
|
||||
function()
|
||||
require("telescope.builtin").oldfiles()
|
||||
end,
|
||||
desc = "List previously open files",
|
||||
},
|
||||
{
|
||||
"<leader>hc",
|
||||
function()
|
||||
require("telescope.builtin").command_history()
|
||||
end,
|
||||
desc = "List previously run commands",
|
||||
},
|
||||
{
|
||||
"<leader>hs",
|
||||
function()
|
||||
require("telescope.builtin").search_history()
|
||||
end,
|
||||
desc = "List previously run searches",
|
||||
},
|
||||
{
|
||||
"<leader>m",
|
||||
function()
|
||||
require("telescope.builtin").marks()
|
||||
end,
|
||||
desc = "List marks",
|
||||
},
|
||||
{ "<leader>cd", ChangeToCurrentDirectory, desc = "Switch CWD to the directory of the open buffer" },
|
||||
{ "<leader>ss", ToggleSpell, desc = "Toggle spell-checker on or off" },
|
||||
{
|
||||
"<leader><leader>",
|
||||
function()
|
||||
require("telescope.builtin").find_files()
|
||||
end,
|
||||
desc = "Find files by name",
|
||||
},
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
|
||||
pattern = { "Directory.Build.props", "*.fsproj", "*.csproj" },
|
||||
callback = function()
|
||||
vim.bo.filetype = "xml"
|
||||
end,
|
||||
})
|
230
home-manager/nvim/ionide-vim.lua
Normal file
230
home-manager/nvim/ionide-vim.lua
Normal file
@@ -0,0 +1,230 @@
|
||||
vim.g["fsharp#fsautocomplete_command"] = { "fsautocomplete" }
|
||||
vim.g["fsharp#show_signature_on_cursor_move"] = 1
|
||||
vim.g["fsharp#fsi_keymap"] = "none"
|
||||
|
||||
-- Supply nil to get all loaded F# projects and build them.
|
||||
local function BuildFSharpProjects(projects)
|
||||
local function on_line(data, _, context)
|
||||
-- Keep the window alive if there were warnings
|
||||
if string.match(data, "%s[1-9]%d* Warning%(s%)") then
|
||||
context.warn = context.warn + 1
|
||||
end
|
||||
end
|
||||
|
||||
local on_complete
|
||||
local function spawn_next(context)
|
||||
BuildUtils.run(
|
||||
"dotnet",
|
||||
{ "build", context.projects[context.completed + 1] },
|
||||
"dotnet build",
|
||||
context,
|
||||
on_line,
|
||||
on_complete
|
||||
)
|
||||
end
|
||||
|
||||
on_complete = function(context, code, signal)
|
||||
print("Build process exited with code " .. code .. " and signal " .. signal)
|
||||
if code ~= 0 then
|
||||
context.errs = context.errs + 1
|
||||
end
|
||||
context.completed = context.completed + 1
|
||||
|
||||
print(
|
||||
"Completed: "
|
||||
.. context.completed
|
||||
.. " out of "
|
||||
.. context.expected
|
||||
.. " (errors: "
|
||||
.. context.errs
|
||||
.. ", warnings: "
|
||||
.. context.warn
|
||||
.. ")"
|
||||
)
|
||||
|
||||
if context.completed == context.expected then
|
||||
if context.errs == 0 and context.warn == 0 then
|
||||
-- Close the temporary floating window (but keep it alive if the
|
||||
-- cursor is in it)
|
||||
local cur_win = vim.api.nvim_get_current_win()
|
||||
local cur_buf = vim.api.nvim_win_get_buf(cur_win)
|
||||
if cur_buf ~= context.buffer then
|
||||
vim.api.nvim_win_close(context.window, true)
|
||||
end
|
||||
print("All builds successful")
|
||||
end
|
||||
else
|
||||
spawn_next(context)
|
||||
end
|
||||
end
|
||||
|
||||
if not projects then
|
||||
projects = vim.fn["fsharp#getLoadedProjects"]()
|
||||
end
|
||||
if projects then
|
||||
local total_projects = 0
|
||||
for _, _ in ipairs(projects) do
|
||||
total_projects = total_projects + 1
|
||||
end
|
||||
local context = BuildUtils.create_window()
|
||||
context.warn = 0
|
||||
context.errs = 0
|
||||
context.completed = 0
|
||||
context.expected = total_projects
|
||||
context.projects = projects
|
||||
|
||||
spawn_next(context)
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_create_user_command("BuildFSharpProject", function(opts)
|
||||
if opts.fargs and opts.fargs[1] then
|
||||
BuildFSharpProjects(opts.fargs)
|
||||
else
|
||||
local pickers = require("telescope.pickers")
|
||||
local finders = require("telescope.finders")
|
||||
local conf = require("telescope.config").values
|
||||
local action_state = require("telescope.actions.state")
|
||||
local actions = require("telescope.actions")
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = "Projects",
|
||||
finder = finders.new_table({
|
||||
results = vim.fn["fsharp#getLoadedProjects"](),
|
||||
}),
|
||||
sorter = conf.generic_sorter({}),
|
||||
attach_mappings = function(prompt_buf, _)
|
||||
actions.select_default:replace(function()
|
||||
actions.close(prompt_buf)
|
||||
local selection = action_state.get_selected_entry()
|
||||
BuildFSharpProjects({ selection.value })
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
end, { nargs = "?", complete = "file" })
|
||||
|
||||
local function TableConcat(tables)
|
||||
local result = {}
|
||||
for _, tab in ipairs(tables) do
|
||||
for _, v in ipairs(tab) do
|
||||
table.insert(result, v)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- args is a table that will be splatted into the command line and will be immediately
|
||||
-- followed by the project.
|
||||
local function RunDotnet(command, args, project, configuration)
|
||||
local function on_line(data, _, context) end
|
||||
|
||||
local function on_complete(context, code, signal) end
|
||||
|
||||
local context = BuildUtils.create_window()
|
||||
|
||||
BuildUtils.run(
|
||||
"dotnet",
|
||||
TableConcat({ { command }, args, { project, "--configuration", configuration } }),
|
||||
"dotnet",
|
||||
context,
|
||||
on_line,
|
||||
on_complete
|
||||
)
|
||||
end
|
||||
|
||||
-- Call this as:
|
||||
-- RunFSharpProject path/to/fsproj
|
||||
-- RunFSharpProject Debug path/to/fsproj
|
||||
vim.api.nvim_create_user_command("RunFSharpProject", function(opts)
|
||||
local configuration = "Release"
|
||||
if opts.fargs and opts.fargs[1] and opts.fargs[1]:match("sproj$") then
|
||||
RunDotnet("run", { "--project" }, opts.fargs[1], configuration)
|
||||
elseif opts.fargs and opts.fargs[1] and opts.fargs[2] then
|
||||
configuration = opts.fargs[1]
|
||||
RunDotnet("run", { "--project" }, opts.fargs[2], configuration)
|
||||
else
|
||||
configuration = opts.fargs[1]
|
||||
local pickers = require("telescope.pickers")
|
||||
local finders = require("telescope.finders")
|
||||
local conf = require("telescope.config").values
|
||||
local action_state = require("telescope.actions.state")
|
||||
local actions = require("telescope.actions")
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = "Projects",
|
||||
finder = finders.new_table({
|
||||
results = vim.fn["fsharp#getLoadedProjects"](),
|
||||
}),
|
||||
sorter = conf.generic_sorter({}),
|
||||
attach_mappings = function(prompt_buf, _)
|
||||
actions.select_default:replace(function()
|
||||
actions.close(prompt_buf)
|
||||
local selection = action_state.get_selected_entry()
|
||||
RunDotnet("run", { "--project" }, selection.value, configuration)
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
end, { nargs = "*", complete = "file" })
|
||||
|
||||
vim.api.nvim_create_user_command("PublishFSharpProject", function(opts)
|
||||
if opts.fargs and opts.fargs[1] then
|
||||
RunDotnet("publish", {}, opts.fargs[1], "Release")
|
||||
else
|
||||
local pickers = require("telescope.pickers")
|
||||
local finders = require("telescope.finders")
|
||||
local conf = require("telescope.config").values
|
||||
local action_state = require("telescope.actions.state")
|
||||
local actions = require("telescope.actions")
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = "Projects",
|
||||
finder = finders.new_table({
|
||||
results = vim.fn["fsharp#getLoadedProjects"](),
|
||||
}),
|
||||
sorter = conf.generic_sorter({}),
|
||||
attach_mappings = function(prompt_buf, _)
|
||||
actions.select_default:replace(function()
|
||||
actions.close(prompt_buf)
|
||||
local selection = action_state.get_selected_entry()
|
||||
RunDotnet("publish", {}, selection.value, "Release")
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
end, { nargs = "*", complete = "file" })
|
||||
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
pattern = "fsharp",
|
||||
callback = function()
|
||||
local status, whichkey = pcall(require, "which-key")
|
||||
if status then
|
||||
whichkey.add({
|
||||
{ "<localleader>f", desc = "F#" },
|
||||
{ "<localleader>ft", ":call fsharp#showTooltip()<CR>", desc = "Show F# Tooltip" },
|
||||
{ "<localleader>fsi", ":call fsharp#toggleFsi()<CR>", desc = "Toggle FSI (F# Interactive)" },
|
||||
{ "<localleader>fsl", ":call fsharp#sendLineToFsi()<cr>", desc = "Send line to FSI (F# Interactive)" },
|
||||
{ "<localleader>fr", desc = "Run F# project" },
|
||||
{ "<localleader>frd", ":RunFSharpProject Debug", desc = "Run F# project in debug configuration" },
|
||||
{ "<localleader>frr", ":RunFSharpProject Release", desc = "Run F# project in release configuration" },
|
||||
{ "<localleader>fp", ":PublishFSharpProject", desc = "Publish F# project" },
|
||||
{ "<localleader>fb", desc = "Build F# project" },
|
||||
{ "<localleader>fba", BuildFSharpProjects, desc = "Build all projects" },
|
||||
{ "<localleader>fbs", ":BuildFSharpProject", desc = "Build specified project" },
|
||||
}, { buffer = vim.api.nvim_get_current_buf() })
|
||||
else
|
||||
vim.api.nvim_set_keymap("n", "<localleader>ft", ":call fsharp#showTooltip()<CR>", { noremap = true })
|
||||
vim.api.nvim_set_keymap("n", "<localleader>fsi", ":call fsharp#toggleFsi()<CR>", { noremap = true })
|
||||
vim.api.nvim_set_keymap("n", "<localleader>fsl", ":call fsharp#sendLineToFsi()<CR>", { noremap = true })
|
||||
vim.api.nvim_set_keymap("n", "<localleader>bpa", ":lua BuildFSharpProjects()", { noremap = true })
|
||||
vim.api.nvim_set_keymap("n", "<localleader>bps", ":BuildFSharpProject", { noremap = true })
|
||||
end
|
||||
end,
|
||||
})
|
17
home-manager/nvim/lean.lua
Normal file
17
home-manager/nvim/lean.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
require("lspconfig")["leanls"].setup({})
|
||||
|
||||
require("lean").setup({})
|
||||
|
||||
require("which-key").add({
|
||||
{ "<localleader>l", desc = "Lean" },
|
||||
{ "<localleader>li", "<Cmd>LeanInfoviewToggle<CR>", desc = "Toggle Lean info view" },
|
||||
{ "<localleader>lp", "<Cmd>LeanInfoviewPinTogglePause<CR>", desc = "Pause Lean info view" },
|
||||
{ "<localleader>ls", "<Cmd>LeanSorryFill<CR>", desc = "Fill open goals with sorry" },
|
||||
{ "<localleader>lw", "<Cmd>LeanInfoviewEnableWidgets<CR>", desc = "Enable Lean widgets" },
|
||||
{ "<localleader>lW", "<Cmd>LeanInfoviewDisableWidgets<CR>", desc = "Disable Lean widgets" },
|
||||
{
|
||||
"<localleader>l?",
|
||||
"<Cmd>LeanAbbreviationsReverseLookup<CR>",
|
||||
desc = "Show what Lean abbreviation produces the symbol under the cursor",
|
||||
},
|
||||
})
|
192
home-manager/nvim/lspconfig.lua
Normal file
192
home-manager/nvim/lspconfig.lua
Normal file
@@ -0,0 +1,192 @@
|
||||
local coq = require("coq")
|
||||
|
||||
-- Using rustaceanvim means we shouldn't set up the LSP for Rust manually.
|
||||
-- Similarly csharp_ls is unnecessary given roslyn.nvim
|
||||
-- require("lspconfig")["csharp_ls"].setup({})
|
||||
local schemas = {
|
||||
["https://raw.githubusercontent.com/docker/compose/master/compose/config/compose_spec.json"] = "docker-compose*.{yml,yaml}",
|
||||
["https://json.schemastore.org/github-workflow.json"] = ".github/**/*.{yml,yaml}",
|
||||
["https://json.schemastore.org/package.json"] = "package.json",
|
||||
["https://json.schemastore.org/global.json"] = "global.json",
|
||||
["https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json"] = "version.json",
|
||||
["https://json-schema.org/draft/2020-12/schema"] = "*.schema.json",
|
||||
["https://json.schemastore.org/dotnet-tools.json"] = "dotnet-tools.json",
|
||||
}
|
||||
|
||||
require("lspconfig")["clangd"].setup({})
|
||||
|
||||
require("lspconfig")["gopls"].setup({})
|
||||
|
||||
require("lspconfig")["yamlls"].setup({
|
||||
settings = {
|
||||
yaml = {
|
||||
validate = true,
|
||||
-- disable the schema store
|
||||
schemaStore = {
|
||||
enable = false,
|
||||
url = "",
|
||||
},
|
||||
-- manually select schemas
|
||||
schemas = schemas,
|
||||
},
|
||||
},
|
||||
filetypes = { "yaml", "json", "jsonc" },
|
||||
})
|
||||
|
||||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
capabilities.textDocument.completion.completionItem.snippetSupport = true
|
||||
require("lspconfig")["jsonls"].setup({
|
||||
capabilities = capabilities,
|
||||
cmd = { "vscode-json-languageserver", "--stdio" },
|
||||
settings = {
|
||||
json = {
|
||||
validate = { enable = true },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require("lspconfig")["denols"].setup({})
|
||||
require("lspconfig")["bashls"].setup({})
|
||||
require("lspconfig")["dockerls"].setup({})
|
||||
require("lspconfig")["html"].setup({
|
||||
capabilities = capabilities,
|
||||
})
|
||||
|
||||
require("lspconfig")["lua_ls"].setup({
|
||||
on_init = function(client)
|
||||
if not client.workspace_folders then
|
||||
return
|
||||
end
|
||||
local path = client.workspace_folders[1].name
|
||||
if vim.uv.fs_stat(path .. "/.luarc.json") or vim.loop.fs_stat(path .. "/.luarc.jsonc") then
|
||||
return
|
||||
end
|
||||
|
||||
client.config.settings.Lua = vim.tbl_deep_extend("force", client.config.settings.Lua, {
|
||||
runtime = {
|
||||
-- Tell the language server which version of Lua you're using
|
||||
-- (most likely LuaJIT in the case of Neovim)
|
||||
version = "LuaJIT",
|
||||
},
|
||||
-- Make the server aware of Neovim runtime files
|
||||
workspace = {
|
||||
checkThirdParty = false,
|
||||
library = {
|
||||
vim.env.VIMRUNTIME,
|
||||
-- Depending on the usage, you might want to add additional paths here.
|
||||
-- "${3rd}/luv/library"
|
||||
-- "${3rd}/busted/library",
|
||||
},
|
||||
-- or pull in all of 'runtimepath'. NOTE: this is a lot slower
|
||||
-- library = vim.api.nvim_get_runtime_file("", true)
|
||||
},
|
||||
})
|
||||
end,
|
||||
settings = {
|
||||
Lua = {},
|
||||
},
|
||||
})
|
||||
|
||||
require("lspconfig").pyright.setup(coq.lsp_ensure_capabilities({
|
||||
handlers = {
|
||||
["textDocument/publishDiagnostics"] = function(...)
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(...)
|
||||
|
||||
local window = vim.api.nvim_get_current_win()
|
||||
vim.diagnostic.setloclist({ open_loclist = true })
|
||||
vim.api.nvim_set_current_win(window)
|
||||
end,
|
||||
},
|
||||
}))
|
||||
|
||||
require("lspconfig").nil_ls.setup(coq.lsp_ensure_capabilities({
|
||||
settings = {
|
||||
nix = {
|
||||
flake = {
|
||||
autoArchive = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
function ToggleLocList()
|
||||
local winid = vim.fn.getloclist(0, { winid = 0 }).winid
|
||||
if winid == 0 then
|
||||
local window = vim.api.nvim_get_current_win()
|
||||
vim.cmd.lopen()
|
||||
vim.api.nvim_set_current_win(window)
|
||||
else
|
||||
vim.cmd.lclose()
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local whichkey_status, whichkey = pcall(require, "which-key")
|
||||
if whichkey_status then
|
||||
whichkey.add({
|
||||
{ "<leader>l", desc = "loclist-related commands" },
|
||||
{ "<leader>lp", vim.diagnostic.goto_prev, desc = "Go to previous entry in loclist" },
|
||||
{ "<leader>ln", vim.diagnostic.goto_next, desc = "Go to next entry in loclist" },
|
||||
{ "<leader>ll", ToggleLocList, desc = "Toggle loclist" },
|
||||
{ "<leader>lf", vim.diagnostic.open_float, desc = "Open current loclist entry in floating window" },
|
||||
})
|
||||
else
|
||||
vim.keymap.set("n", "<leader>lp", vim.diagnostic.goto_prev)
|
||||
vim.keymap.set("n", "<leader>ln", vim.diagnostic.goto_next)
|
||||
vim.keymap.set("n", "<leader>ll", ToggleLocList)
|
||||
vim.keymap.set("n", "<leader>lf", vim.diagnostic.open_float)
|
||||
end
|
||||
end
|
||||
|
||||
-- Use LspAttach autocommand to only map the following keys
|
||||
-- after the language server attaches to the current buffer
|
||||
vim.api.nvim_create_autocmd("LspAttach", {
|
||||
group = vim.api.nvim_create_augroup("UserLspConfig", {}),
|
||||
callback = function(ev)
|
||||
local whichkey = require("which-key")
|
||||
-- Enable completion triggered by <c-x><c-o>
|
||||
vim.bo[ev.buf].omnifunc = "v:lua.vim.lsp.omnifunc"
|
||||
|
||||
-- Buffer local mappings.
|
||||
-- See `:help vim.lsp.*` for documentation on any of the below functions
|
||||
whichkey.add({
|
||||
{ "g", desc = "Go-to related commands" },
|
||||
{ "gD", vim.lsp.buf.declaration, desc = "Go to declaration" },
|
||||
{ "gd", vim.lsp.buf.definition, desc = "Go to definition" },
|
||||
{ "gi", vim.lsp.buf.implementation, desc = "Go to implementation" },
|
||||
{
|
||||
"gr",
|
||||
function()
|
||||
require("telescope.builtin").lsp_references()
|
||||
end,
|
||||
desc = "Find references",
|
||||
},
|
||||
{ "gK", vim.lsp.buf.hover, desc = "Display information about symbol under cursor" },
|
||||
})
|
||||
whichkey.add({
|
||||
{ "<C-k>", vim.lsp.buf.signature_help, desc = "Display signature information about symbol under cursor" },
|
||||
})
|
||||
whichkey.add({
|
||||
{ "<leader>w", desc = "Workspace-related commands" },
|
||||
{ "<leader>wa", vim.lsp.buf.add_workspace_folder, desc = "Add a path to the workspace folders list" },
|
||||
{ "<leader>wr", vim.lsp.buf.add_workspace_folder, desc = "Remove a path from the workspace folders list" },
|
||||
{
|
||||
"<leader>wl",
|
||||
function()
|
||||
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
|
||||
end,
|
||||
desc = "Show the workspace folders list",
|
||||
},
|
||||
{
|
||||
"<leader>f",
|
||||
function()
|
||||
vim.lsp.buf.format({ async = true })
|
||||
end,
|
||||
desc = "Autoformat",
|
||||
},
|
||||
{ "<leader>ca", vim.lsp.buf.code_action, desc = "Select a code action" },
|
||||
{ "<leader>rn", vim.lsp.buf.rename, desc = "Rename variable" },
|
||||
{ "<leader>D", vim.lsp.buf.type_definition, desc = "Go to type definition" },
|
||||
})
|
||||
end,
|
||||
})
|
11
home-manager/nvim/nvim-dap-python.lua
Normal file
11
home-manager/nvim/nvim-dap-python.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
require("dap-python").setup("%PYTHONENV%/bin/python")
|
||||
|
||||
do
|
||||
local whichkey = require("which-key")
|
||||
whichkey.add({
|
||||
{ "<localleader>pd", desc = "Debugger-related commands" },
|
||||
{ "<localleader>pdt", desc = "Tests" },
|
||||
{ "<localleader>pdtf", require("dap-python").test_class, desc = "Run Python tests in the current file" },
|
||||
{ "<localleader>pdtc", require("dap-python").test_method, desc = "Run the Python test under the cursor" },
|
||||
})
|
||||
end
|
65
home-manager/nvim/nvim-dap.lua
Normal file
65
home-manager/nvim/nvim-dap.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
local dap = require("dap")
|
||||
local dap_ui = require("dap.ui.widgets")
|
||||
dap.adapters.coreclr = {
|
||||
type = "executable",
|
||||
command = "netcoredbg",
|
||||
args = { "--interpreter=vscode" },
|
||||
}
|
||||
|
||||
dap.configurations.fsharp = {
|
||||
{
|
||||
type = "coreclr",
|
||||
name = "launch - netcoredbg",
|
||||
request = "launch",
|
||||
program = function()
|
||||
return vim.fn.input("Path to dll: ", vim.fn.getcwd() .. "/bin/Debug/", "file")
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
dap.configurations.cs = {
|
||||
{
|
||||
type = "coreclr",
|
||||
name = "launch - netcoredbg",
|
||||
request = "launch",
|
||||
program = function()
|
||||
return vim.fn.input("Path to dll: ", vim.fn.getcwd() .. "/bin/Debug/", "file")
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
do
|
||||
local whichkey = require("which-key")
|
||||
whichkey.add({
|
||||
{ "<localleader>d", desc = "Debugger-related commands" },
|
||||
{ "<localleader>do", dap.step_over, desc = "Step over" },
|
||||
{ "<localleader>di", dap.step_into, desc = "Step into" },
|
||||
{ "<localleader>dc", dap.continue, desc = "Continue" },
|
||||
{ "<localleader>dC", dap.run_last, desc = "Run with last debug configuration" },
|
||||
{ "<localleader>db", dap.toggle_breakpoint, desc = "Toggle breakpoint" },
|
||||
{ "<localleader>dr", dap.repl.open, desc = "Open debug repl" },
|
||||
{ "<localleader>dv", desc = "Commands to view debugger state" },
|
||||
{
|
||||
"<localleader>dvv",
|
||||
function()
|
||||
dap_ui.hover()
|
||||
end,
|
||||
desc = "View value of expression under cursor",
|
||||
},
|
||||
{
|
||||
"<localleader>dvs",
|
||||
function()
|
||||
dap_ui.sidebar(dap_ui.scopes).open()
|
||||
end,
|
||||
desc = "View values of all variables in all scopes",
|
||||
},
|
||||
{
|
||||
"<localleader>dvf",
|
||||
function()
|
||||
dap_ui.sidebar(dap_ui.frames).open()
|
||||
end,
|
||||
desc = "View stack frames",
|
||||
},
|
||||
{ "<localleader>dt", dap.terminate, desc = "Terminate/stop/end debug session" },
|
||||
})
|
||||
end
|
9
home-manager/nvim/nvim-lightbulb.lua
Normal file
9
home-manager/nvim/nvim-lightbulb.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
require("nvim-lightbulb").setup({
|
||||
autocmd = { enabled = true },
|
||||
ignore = {
|
||||
clients = {
|
||||
-- This one is really noisy
|
||||
"lua_ls",
|
||||
},
|
||||
},
|
||||
})
|
64
home-manager/nvim/python.lua
Normal file
64
home-manager/nvim/python.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
local function pytest_on_line(_, _, _) end
|
||||
local function pytest_on_complete(_, code, _)
|
||||
if code ~= 0 then
|
||||
print("Exit code " .. code)
|
||||
end
|
||||
end
|
||||
|
||||
function RunPythonTestAtCursor()
|
||||
local api = vim.api
|
||||
|
||||
-- Get the current buffer and cursor position
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local line_nr = api.nvim_win_get_cursor(0)[1]
|
||||
local filename = api.nvim_buf_get_name(bufnr)
|
||||
|
||||
-- Read the file content
|
||||
local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
|
||||
-- Find the test function
|
||||
local test_name = nil
|
||||
for i = line_nr, 1, -1 do
|
||||
local line = lines[i]
|
||||
if line:match("^def test_") then
|
||||
test_name = line:match("^def (%S+)%(")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if test_name then
|
||||
-- Run pytest for the found test function
|
||||
local context = BuildUtils.create_window()
|
||||
BuildUtils.run(
|
||||
"pytest",
|
||||
{ filename .. "::" .. test_name },
|
||||
"Run PyTest (" .. test_name .. ")",
|
||||
context,
|
||||
pytest_on_line,
|
||||
pytest_on_complete
|
||||
)
|
||||
else
|
||||
print("No test function found at or above line " .. line_nr)
|
||||
end
|
||||
end
|
||||
|
||||
function RunPythonTestsInFile()
|
||||
local file_path = vim.fn.expand("%:p")
|
||||
local context = BuildUtils.create_window()
|
||||
BuildUtils.run("pytest", { file_path }, "Run PyTest", context, pytest_on_line, pytest_on_complete)
|
||||
end
|
||||
|
||||
function RunAllPythonTests()
|
||||
local context = BuildUtils.create_window()
|
||||
BuildUtils.run("pytest", {}, "Run PyTest", context, pytest_on_line, pytest_on_complete)
|
||||
end
|
||||
|
||||
do
|
||||
local whichkey = require("which-key")
|
||||
whichkey.add({
|
||||
{ "<localleader>pt", desc = "Run Python tests" },
|
||||
{ "<localleader>ptf", RunPythonTestsInFile, desc = "Run Python tests in the current file" },
|
||||
{ "<localleader>pta", RunAllPythonTests, desc = "Run all Python tests" },
|
||||
{ "<localleader>ptc", RunPythonTestAtCursor, desc = "Run the Python test under the cursor" },
|
||||
})
|
||||
end
|
4
home-manager/nvim/roslyn-nvim.lua
Normal file
4
home-manager/nvim/roslyn-nvim.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
require("roslyn").setup({
|
||||
on_attach = function(_, _) end,
|
||||
capabilities = vim.lsp.protocol.make_client_capabilities(),
|
||||
})
|
8
home-manager/nvim/tokyonight.lua
Normal file
8
home-manager/nvim/tokyonight.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
require("tokyonight").setup({
|
||||
style = "night",
|
||||
on_colors = function(colors)
|
||||
colors.border = "#565f89"
|
||||
end,
|
||||
})
|
||||
|
||||
vim.cmd([[colorscheme tokyonight]])
|
9
home-manager/nvim/treesitter.lua
Normal file
9
home-manager/nvim/treesitter.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
require("nvim-treesitter.configs").setup({
|
||||
-- Automatically install missing parsers when entering buffer
|
||||
-- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally
|
||||
auto_install = false,
|
||||
|
||||
highlight = {
|
||||
enable = true,
|
||||
},
|
||||
})
|
99
home-manager/nvim/venv-selector.lua
Normal file
99
home-manager/nvim/venv-selector.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
local venv_selector = require("venv-selector")
|
||||
|
||||
venv_selector.setup({
|
||||
changed_venv_hooks = { venv_selector.hooks.pyright },
|
||||
name = { "venv", ".venv" },
|
||||
search_venv_managers = true,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("VimEnter", {
|
||||
desc = "Auto select virtualenv Nvim open",
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
-- Mystery: this seems to be being called twice whenever we open nvim
|
||||
local venv = vim.fn.findfile("pyproject.toml", vim.fn.getcwd() .. ";")
|
||||
if venv ~= "" then
|
||||
require("venv-selector").retrieve_from_cache()
|
||||
end
|
||||
end,
|
||||
once = true,
|
||||
})
|
||||
|
||||
function SelectVenv()
|
||||
local old_path = vim.fn.getenv("PATH")
|
||||
vim.cmd("VenvSelectCached")
|
||||
local new_path = vim.fn.getenv("PATH")
|
||||
if old_path == new_path then
|
||||
-- Failed to source venv. Get the user to choose one.
|
||||
vim.cmd("VenvSelect")
|
||||
end
|
||||
end
|
||||
|
||||
local function find_requirements_txt(start_path)
|
||||
local path = vim.fn.fnamemodify(start_path, ":p")
|
||||
while path and #path > 1 do
|
||||
local req_path = path .. "requirements.txt"
|
||||
if vim.fn.filereadable(req_path) ~= 0 then
|
||||
return req_path
|
||||
end
|
||||
path = vim.fn.fnamemodify(path, ":h")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- TODO: make this one work
|
||||
local function load_venv(venv_dir)
|
||||
require("venv-selector.venv").load()
|
||||
require("venv-selector.venv").set_venv_and_system_paths(venv_dir)
|
||||
require("venv-selector.venv").cache_venv(venv_dir)
|
||||
end
|
||||
|
||||
function CreateVenv()
|
||||
local requirements_path = find_requirements_txt(vim.fn.getcwd())
|
||||
local venv_dir
|
||||
if not requirements_path then
|
||||
print("requirements.txt not found; creating fresh venv in current working directory.")
|
||||
venv_dir = vim.fn.getcwd() .. "/.venv"
|
||||
else
|
||||
venv_dir = vim.fn.fnamemodify(requirements_path, ":h") .. "/.venv"
|
||||
end
|
||||
|
||||
print("Creating virtual environment in " .. venv_dir)
|
||||
|
||||
-- Create virtual environment
|
||||
vim.fn.system("python -m venv " .. vim.fn.shellescape(venv_dir))
|
||||
|
||||
-- Install requirements
|
||||
if requirements_path then
|
||||
print("Installing requirements from " .. requirements_path)
|
||||
local context = BuildUtils.create_window()
|
||||
BuildUtils.run(
|
||||
venv_dir .. "/bin/python",
|
||||
{ "-m", "pip", "install", "-r", requirements_path },
|
||||
"venv creation",
|
||||
context,
|
||||
function(_, _, _) end,
|
||||
function(_, _, _)
|
||||
load_venv(venv_dir)
|
||||
end
|
||||
)
|
||||
else
|
||||
load_venv(venv_dir)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local whichkey = require("which-key")
|
||||
whichkey.add({
|
||||
{ "<localleader>pv", desc = "Python virtual environment-related commands" },
|
||||
{ "<localleader>pvc", CreateVenv, desc = "Create virtual environment" },
|
||||
{ "<localleader>pvl", SelectVenv, desc = "Load virtual environment" },
|
||||
{
|
||||
"<localleader>pvo",
|
||||
function()
|
||||
vim.cmd("VenvSelect")
|
||||
end,
|
||||
desc = "Choose (override) new virtual environment",
|
||||
},
|
||||
})
|
||||
end
|
40
home-manager/nvim/which-key.lua
Normal file
40
home-manager/nvim/which-key.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
require("which-key").setup({
|
||||
plugins = {
|
||||
marks = true, -- shows a list of your marks on ' and `
|
||||
registers = true, -- shows your registers on " in NORMAL or <C-r> in INSERT mode
|
||||
-- the presets plugin, adds help for a bunch of default keybindings in Neovim
|
||||
-- No actual key bindings are created
|
||||
spelling = {
|
||||
enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions
|
||||
suggestions = 20, -- how many suggestions should be shown in the list?
|
||||
},
|
||||
presets = {
|
||||
operators = true, -- adds help for operators like d, y, ...
|
||||
motions = true, -- adds help for motions
|
||||
text_objects = true, -- help for text objects triggered after entering an operator
|
||||
windows = true, -- default bindings on <c-w>
|
||||
nav = true, -- misc bindings to work with windows
|
||||
z = true, -- bindings for folds, spelling and others prefixed with z
|
||||
g = true, -- bindings for prefixed with g
|
||||
},
|
||||
},
|
||||
icons = {
|
||||
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
|
||||
separator = "➜", -- symbol used between a key and it's label
|
||||
group = "+", -- symbol prepended to a group
|
||||
},
|
||||
layout = {
|
||||
height = { min = 4, max = 25 }, -- min and max height of the columns
|
||||
width = { min = 20, max = 50 }, -- min and max width of the columns
|
||||
spacing = 3, -- spacing between columns
|
||||
align = "left", -- align columns left, center or right
|
||||
},
|
||||
show_help = true, -- show a help message in the command line for using WhichKey
|
||||
show_keys = true, -- show the currently pressed key and its label as a message in the command line
|
||||
-- disable the WhichKey popup for certain buf types and file types.
|
||||
-- Disabled by default for Telescope
|
||||
disable = {
|
||||
buftypes = {},
|
||||
filetypes = {},
|
||||
},
|
||||
})
|
@@ -1,6 +1,6 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/Environment/Hierarchy/NuGetOptions/IncludePrerelease/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/AppBrowserSnapshotsStoreSettings/CurrentStore/@EntryValue">/Users/Patrick/Library/Preferences/Rider2019.3/resharper-host/Sessions</s:String>
|
||||
<s:String x:Key="/Default/AppBrowserSnapshotsStoreSettings/CurrentStore/@EntryValue">/Users/patrick/Library/Preferences/Rider2019.3/resharper-host/Sessions</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeEditing/Intellisense/CodeCompletion/CacheImportCompletion/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/Roslyn/LegacySeveritiesMigrated/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/ExternalSources/Decompiler/DecompilerLegalNoticeAccepted/@EntryValue">True</s:Boolean>
|
||||
@@ -34,7 +34,7 @@
|
||||
<s:String x:Key="/Default/Profilers/Common/ConfigurationsStore/SelectedProfileConfigurationId/=NotSupported/@EntryIndexedValue">00000000-0000-0000-0000-000000000000</s:String>
|
||||
<s:String x:Key="/Default/Profilers/Common/ConfigurationsStore/SelectedProfileTestsConfigurationId/=Any/@EntryIndexedValue">2704f639-7a6d-4dc0-8931-351a3f933333</s:String>
|
||||
<s:Boolean x:Key="/Default/RiderDebugger/RiderRestoreDecompile/RestoreDecompileSetting/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/SnapshotsStore/CurrentStore/@EntryValue">/Users/Patrick/Library/Preferences/Rider2019.3/resharper-host/Sessions</s:String>
|
||||
<s:String x:Key="/Default/SnapshotsStore/CurrentStore/@EntryValue">/Users/patrick/Library/Preferences/Rider2019.3/resharper-host/Sessions</s:String>
|
||||
<s:Boolean x:Key="/Default/SymbolServers/RestoreDecompileSetting/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leet/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=nums/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=nums/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
49
home-manager/rider/default.nix
Normal file
49
home-manager/rider/default.nix
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
src = ./GlobalSettingsStorage.DotSettings;
|
||||
in let
|
||||
link = ./link.sh;
|
||||
in let
|
||||
riderconfig = pkgs.stdenv.mkDerivation {
|
||||
name = "rider-config";
|
||||
version = "2023.1";
|
||||
__contentAddressed = true;
|
||||
|
||||
src = src;
|
||||
phases = ["unpackPhase"];
|
||||
unpackPhase = ''
|
||||
mkdir -p "$out"
|
||||
cp ${src} "$out/GlobalSettingsStorage.DotSettings"
|
||||
cp ${link} "$out/link.sh"
|
||||
chmod u+x "$out/link.sh"
|
||||
sed -i 's_NIX-DOTNET-SDK_${config.rider.dotnet}_' "$out/GlobalSettingsStorage.DotSettings"
|
||||
sed -i "s!NIX-RIDER-CONFIG!$out!" "$out/link.sh"
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
'';
|
||||
};
|
||||
in {
|
||||
options = {
|
||||
rider.enable = lib.mkOption {default = false;};
|
||||
rider.username = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "Patrick";
|
||||
};
|
||||
rider.dotnet = lib.mkOption {default = pkgs.dotnet-sdk;};
|
||||
};
|
||||
|
||||
config = lib.mkIf config.rider.enable {
|
||||
home.activation.jetbrains-rider-settings = lib.hm.dag.entryAfter ["writeBoundary"] ''
|
||||
|
||||
dest="/Users/${config.rider.username}/Library/Application Support/JetBrains"
|
||||
if [ -e "$dest" ]; then
|
||||
find "$dest" -maxdepth 1 -type d -name 'Rider*' -exec sh -c '${riderconfig}/link.sh "$0"' {} \;
|
||||
fi
|
||||
'';
|
||||
};
|
||||
}
|
@@ -3,17 +3,19 @@
|
||||
outfile="$1/resharper-host/GlobalSettingsStorage.DotSettings"
|
||||
if [ -e "$outfile" ]; then
|
||||
existing=$(readlink "$outfile")
|
||||
if [ $? -eq 1 ] ; then
|
||||
if [ $? -eq 1 ]; then
|
||||
echo "Backing up existing settings file $outfile"
|
||||
mv "$outfile" "$outfile.bak"
|
||||
ln -s "NIX-RIDER-CONFIG/GlobalSettingsStorage.DotSettings" "$outfile"
|
||||
else
|
||||
case "$existing" in
|
||||
"/nix/store/"*)
|
||||
ln -fs "NIX-RIDER-CONFIG/GlobalSettingsStorage.DotSettings" "$outfile" ;;
|
||||
ln -fs "NIX-RIDER-CONFIG/GlobalSettingsStorage.DotSettings" "$outfile"
|
||||
;;
|
||||
*)
|
||||
echo "Refusing to overwrite existing symlink to $existing" && \
|
||||
exit 1 ;;
|
||||
echo "Refusing to overwrite existing symlink to $existing" &&
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
@@ -4,4 +4,4 @@ Host *
|
||||
AddKeysToAgent yes
|
||||
IgnoreUnknown UseKeychain
|
||||
UseKeychain yes
|
||||
IdentityFile ~/.ssh/id_rsa
|
||||
IdentityFile ~/.ssh/id_ed25519
|
4
home-manager/sway.conf
Normal file
4
home-manager/sway.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
output Unknown-1 scale 2
|
||||
input * {
|
||||
xkb_layout "gb"
|
||||
}
|
87
home-manager/vscode-extensions.nix
Normal file
87
home-manager/vscode-extensions.nix
Normal file
@@ -0,0 +1,87 @@
|
||||
{pkgs}:
|
||||
with pkgs.vscode-extensions;
|
||||
[
|
||||
bbenoist.nix
|
||||
haskell.haskell
|
||||
yzhang.markdown-all-in-one
|
||||
james-yu.latex-workshop
|
||||
vscodevim.vim
|
||||
ms-dotnettools.csharp
|
||||
ms-vscode-remote.remote-ssh
|
||||
justusadam.language-haskell
|
||||
rust-lang.rust-analyzer
|
||||
github.vscode-pull-request-github
|
||||
shardulm94.trailing-spaces
|
||||
nvarner.typst-lsp
|
||||
arrterian.nix-env-selector
|
||||
# Doesn't build on arm64
|
||||
# vadimcn.vscode-lldb
|
||||
]
|
||||
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
|
||||
{
|
||||
name = "remote-containers";
|
||||
publisher = "ms-vscode-remote";
|
||||
version = "0.285.0";
|
||||
sha256 = "sha256-UHp6Ewx0bUvPjgaO0A5k77SGj8ovPFLl/WvxyLcZ4y0=";
|
||||
}
|
||||
{
|
||||
name = "vscode-hsx";
|
||||
publisher = "s0kil";
|
||||
version = "0.4.0";
|
||||
sha256 = "sha256-/WRy+cQBqzb6QB5+AizlyIcjqNpZ86o2at885hOcroM=";
|
||||
}
|
||||
{
|
||||
name = "vscode-docker";
|
||||
publisher = "ms-azuretools";
|
||||
version = "1.24.0";
|
||||
sha256 = "sha256-zZ34KQrRPqVbfGdpYACuLMiMj4ZIWSnJIPac1yXD87k=";
|
||||
}
|
||||
{
|
||||
name = "toml";
|
||||
publisher = "be5invis";
|
||||
version = "0.6.0";
|
||||
sha256 = "yk7buEyQIw6aiUizAm+sgalWxUibIuP9crhyBaOjC2E=";
|
||||
}
|
||||
{
|
||||
name = "ionide-fsharp";
|
||||
publisher = "ionide";
|
||||
version = "7.18.1";
|
||||
sha256 = "sha256-6NPMQncoZhZYtx5c+qzarjuSzUXMb5HdKCzcHPCFUhU=";
|
||||
}
|
||||
{
|
||||
name = "lean4";
|
||||
publisher = "leanprover";
|
||||
version = "0.0.128";
|
||||
sha256 = "sha256-odRDOrlDFahweLzoQtpufY8UUwAutPFunqg7atTxnPo=";
|
||||
}
|
||||
{
|
||||
name = "vscode-clang";
|
||||
publisher = "mitaki28";
|
||||
version = "0.2.4";
|
||||
sha256 = "0sys2h4jvnannlk2q02lprc2ss9nkgh0f0kwa188i7viaprpnx23";
|
||||
}
|
||||
{
|
||||
name = "dotnet-interactive-vscode";
|
||||
publisher = "ms-dotnettools";
|
||||
version = "1.0.4165021";
|
||||
sha256 = "sha256-P5EHc5t4UyKEfxIGNTg+SyQPFlrbwaNIaprPY63iJ/k=";
|
||||
}
|
||||
{
|
||||
name = "mono-debug";
|
||||
publisher = "ms-vscode";
|
||||
version = "0.16.3";
|
||||
sha256 = "sha256-6IU8aP4FQVbEMZAgssGiyqM+PAbwipxou5Wk3Q2mjZg=";
|
||||
}
|
||||
{
|
||||
name = "Theme-MarkdownKit";
|
||||
publisher = "ms-vscode";
|
||||
version = "0.1.4";
|
||||
sha256 = "1im78k2gaj6cri2jcvy727qdy25667v0f7vv3p3hv13apzxgzl0l";
|
||||
}
|
||||
{
|
||||
name = "debug";
|
||||
publisher = "webfreak";
|
||||
version = "0.26.1";
|
||||
sha256 = "sha256-lLLa8SN+Sf9Tbi7HeWYWa2KhPQFJyQWrf9l3EUljwYo=";
|
||||
}
|
||||
]
|
209
home.nix
209
home.nix
@@ -1,209 +0,0 @@
|
||||
{
|
||||
nixpkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
username = "Patrick";
|
||||
in let
|
||||
dotnet = nixpkgs.dotnet-sdk_6;
|
||||
in {
|
||||
imports = [./rider];
|
||||
|
||||
rider = {
|
||||
enable = true;
|
||||
username = username;
|
||||
dotnet = dotnet;
|
||||
};
|
||||
|
||||
# Let Home Manager install and manage itself.
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# Home Manager needs a bit of information about you and the
|
||||
# paths it should manage.
|
||||
home.username = username;
|
||||
|
||||
# This value determines the Home Manager release that your
|
||||
# configuration is compatible with. This helps avoid breakage
|
||||
# when a new Home Manager release introduces backwards
|
||||
# incompatible changes.
|
||||
#
|
||||
# You can update Home Manager without changing this value. See
|
||||
# the Home Manager release notes for a list of state version
|
||||
# changes in each release.
|
||||
home.stateVersion = "22.05";
|
||||
|
||||
home.packages =
|
||||
[
|
||||
nixpkgs.rust-analyzer
|
||||
nixpkgs.tmux
|
||||
nixpkgs.wget
|
||||
nixpkgs.youtube-dl
|
||||
nixpkgs.cmake
|
||||
nixpkgs.gnumake
|
||||
nixpkgs.gcc
|
||||
nixpkgs.gdb
|
||||
nixpkgs.hledger
|
||||
nixpkgs.hledger-web
|
||||
dotnet
|
||||
nixpkgs.docker
|
||||
nixpkgs.jitsi-meet
|
||||
#nixpkgs.handbrake
|
||||
nixpkgs.ripgrep
|
||||
nixpkgs.elan
|
||||
nixpkgs.coreutils-prefixed
|
||||
nixpkgs.shellcheck
|
||||
nixpkgs.html-tidy
|
||||
nixpkgs.hugo
|
||||
#nixpkgs.agda
|
||||
nixpkgs.pijul
|
||||
nixpkgs.universal-ctags
|
||||
nixpkgs.asciinema
|
||||
nixpkgs.git-lfs
|
||||
nixpkgs.imagemagick
|
||||
nixpkgs.nixpkgs-fmt
|
||||
nixpkgs.rnix-lsp
|
||||
];
|
||||
|
||||
programs.vscode = {
|
||||
enable = true;
|
||||
package = nixpkgs.vscode;
|
||||
extensions = import ./vscode-extensions.nix { pkgs = nixpkgs; };
|
||||
userSettings = {
|
||||
workbench.colorTheme = "Default High Contrast";
|
||||
"files.Exclude" = {
|
||||
"**/.git" = true;
|
||||
"**/.DS_Store" = true;
|
||||
"**/Thumbs.db" = true;
|
||||
"**/*.olean" = true;
|
||||
"**/result" = true;
|
||||
};
|
||||
"git.path" = "${nixpkgs.git}/bin/git";
|
||||
"update.mode" = "none";
|
||||
"docker.dockerPath" = "${nixpkgs.docker}/bin/docker";
|
||||
#"lean.leanpkgPath" = "/Users/${username}/.elan/toolchains/stable/bin/leanpkg";
|
||||
"lean.executablePath" = "/Users/${username}/.elan/toolchains/lean4/bin/lean";
|
||||
"lean.memoryLimit" = 8092;
|
||||
};
|
||||
};
|
||||
|
||||
programs.tmux = {
|
||||
shell = "\${nixpkgs.zsh}/bin/zsh";
|
||||
};
|
||||
|
||||
programs.zsh = {
|
||||
enable = true;
|
||||
autocd = true;
|
||||
enableAutosuggestions = true;
|
||||
enableCompletion = true;
|
||||
history = {
|
||||
expireDuplicatesFirst = true;
|
||||
};
|
||||
oh-my-zsh = {
|
||||
enable = true;
|
||||
plugins = ["git" "macos" "dircycle" "timer"];
|
||||
theme = "robbyrussell";
|
||||
};
|
||||
sessionVariables = {
|
||||
EDITOR = "vim";
|
||||
LC_ALL = "en_US.UTF-8";
|
||||
LC_CTYPE = "en_US.UTF-8";
|
||||
RUSTFLAGS = "-L ${nixpkgs.libiconv}/lib";
|
||||
RUST_BACKTRACE = "full";
|
||||
};
|
||||
shellAliases = {
|
||||
vim = "nvim";
|
||||
view = "vim -R";
|
||||
nix-upgrade = "sudo -i sh -c 'nix-channel --update && nix-env -iA nixpkgs.nix && launchctl remove org.nixos.nix-daemon && launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist'";
|
||||
cmake = "cmake -DCMAKE_MAKE_PROGRAM=${nixpkgs.gnumake}/bin/make -DCMAKE_AR=${nixpkgs.darwin.cctools}/bin/ar -DCMAKE_RANLIB=${nixpkgs.darwin.cctools}/bin/ranlib -DGMP_INCLUDE_DIR=${nixpkgs.gmp.dev}/include/ -DGMP_LIBRARIES=${nixpkgs.gmp}/lib/libgmp.10.dylib";
|
||||
ar = "${nixpkgs.darwin.cctools}/bin/ar";
|
||||
};
|
||||
};
|
||||
|
||||
programs.fzf = {
|
||||
enable = true;
|
||||
enableZshIntegration = true;
|
||||
};
|
||||
|
||||
programs.git = {
|
||||
package = nixpkgs.gitAndTools.gitFull;
|
||||
enable = true;
|
||||
userName = "Smaug123";
|
||||
userEmail = "patrick+github@patrickstevens.co.uk";
|
||||
aliases = {
|
||||
co = "checkout";
|
||||
st = "status";
|
||||
};
|
||||
extraConfig = {
|
||||
rerere = {
|
||||
enabled = true;
|
||||
};
|
||||
push = {
|
||||
default = "current";
|
||||
};
|
||||
pull = {
|
||||
rebase = false;
|
||||
};
|
||||
init = {
|
||||
defaultBranch = "main";
|
||||
};
|
||||
advice = {
|
||||
addIgnoredFile = false;
|
||||
};
|
||||
"filter \"lfs\"" = {
|
||||
clean = "${nixpkgs.git-lfs} clean -- %f";
|
||||
smudge = "${nixpkgs.git-lfs}/bin/git-lfs smudge --skip -- %f";
|
||||
process = "${nixpkgs.git-lfs}/bin/git-lfs filter-process";
|
||||
required = true;
|
||||
};
|
||||
pull = {
|
||||
twohead = "ort";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
programs.neovim.enable = true;
|
||||
programs.neovim.plugins = with nixpkgs.vimPlugins; [
|
||||
molokai
|
||||
tagbar
|
||||
{
|
||||
plugin = rust-vim;
|
||||
config = "let g:rustfmt_autosave = 1";
|
||||
}
|
||||
{
|
||||
plugin = LanguageClient-neovim;
|
||||
config = "let g:LanguageClient_serverCommands = { 'nix': ['rnix-lsp'] }";
|
||||
}
|
||||
{
|
||||
plugin = syntastic;
|
||||
config = '' let g:syntastic_rust_checkers = ['cargo']
|
||||
let g:syntastic_always_populate_loc_list = 1
|
||||
let g:syntastic_auto_loc_list = 1
|
||||
let g:syntastic_check_on_open = 1
|
||||
let g:syntastic_check_on_wq = 0'';
|
||||
}
|
||||
|
||||
YouCompleteMe
|
||||
tagbar
|
||||
];
|
||||
programs.neovim.viAlias = true;
|
||||
programs.neovim.vimAlias = true;
|
||||
programs.neovim.vimdiffAlias = true;
|
||||
programs.neovim.withPython3 = true;
|
||||
|
||||
programs.neovim.extraConfig = builtins.readFile ./init.vim;
|
||||
|
||||
home.file.".ssh/config".source = ./ssh.config;
|
||||
|
||||
home.file.".ideavimrc".source = ./ideavimrc;
|
||||
|
||||
home.file.".config/youtube-dl/config".source = ./youtube-dl.conf;
|
||||
programs.emacs = {
|
||||
enable = true;
|
||||
package = nixpkgs.emacsGcc;
|
||||
extraPackages = (epkgs: []);
|
||||
extraConfig = ''
|
||||
(load-file (let ((coding-system-for-read 'utf-8))
|
||||
(shell-command-to-string "agda-mode locate")))
|
||||
'';
|
||||
};
|
||||
}
|
368
init.vim
368
init.vim
@@ -1,368 +0,0 @@
|
||||
set nu
|
||||
colorscheme molokai
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Maintainer:
|
||||
" Amir Salihefendic — @amix3k
|
||||
"
|
||||
" Awesome_version:
|
||||
" Get this config, nice color schemes and lots of plugins!
|
||||
"
|
||||
" Install the awesome version from:
|
||||
"
|
||||
" https://github.com/amix/vimrc
|
||||
"
|
||||
" Sections:
|
||||
" -> General
|
||||
" -> VIM user interface
|
||||
" -> Colors and Fonts
|
||||
" -> Files and backups
|
||||
" -> Text, tab and indent related
|
||||
" -> Visual mode related
|
||||
" -> Moving around, tabs and buffers
|
||||
" -> Status line
|
||||
" -> Editing mappings
|
||||
" -> vimgrep searching and cope displaying
|
||||
" -> Spell checking
|
||||
" -> Misc
|
||||
" -> Helper functions
|
||||
"
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => General
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Sets how many lines of history VIM has to remember
|
||||
set history=500
|
||||
|
||||
" Enable filetype plugins
|
||||
filetype plugin on
|
||||
filetype indent on
|
||||
|
||||
" Set to auto read when a file is changed from the outside
|
||||
set autoread
|
||||
|
||||
" With a map leader it's possible to do extra key combinations
|
||||
" like <leader>w saves the current file
|
||||
let mapleader = "`"
|
||||
|
||||
" :W sudo saves the file
|
||||
" (useful for handling the permission-denied error)
|
||||
command W w !sudo tee % > /dev/null
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => VIM user interface
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Set 7 lines to the cursor - when moving vertically using j/k
|
||||
set so=7
|
||||
|
||||
" Avoid garbled characters in Chinese language windows OS
|
||||
let $LANG='en'
|
||||
set langmenu=en
|
||||
source $VIMRUNTIME/delmenu.vim
|
||||
source $VIMRUNTIME/menu.vim
|
||||
|
||||
" Turn on the Wild menu
|
||||
set wildmenu
|
||||
|
||||
" Ignore compiled files
|
||||
set wildignore=*.o,*~,*.pyc
|
||||
if has("win16") || has("win32")
|
||||
set wildignore+=.git\*,.hg\*,.svn\*
|
||||
else
|
||||
set wildignore+=*/.git/*,*/.hg/*,*/.svn/*,*/.DS_Store
|
||||
endif
|
||||
|
||||
"Always show current position
|
||||
set ruler
|
||||
|
||||
" Height of the command bar
|
||||
set cmdheight=2
|
||||
|
||||
" A buffer becomes hidden when it is abandoned
|
||||
set hid
|
||||
|
||||
" Ignore case when searching
|
||||
set ignorecase
|
||||
|
||||
" When searching try to be smart about cases
|
||||
set smartcase
|
||||
|
||||
" Highlight search results
|
||||
set hlsearch
|
||||
|
||||
" Makes search act like search in modern browsers
|
||||
set incsearch
|
||||
|
||||
" Don't redraw while executing macros (good performance config)
|
||||
set lazyredraw
|
||||
|
||||
" For regular expressions turn magic on
|
||||
set magic
|
||||
|
||||
" Show matching brackets when text indicator is over them
|
||||
set showmatch
|
||||
" How many tenths of a second to blink when matching brackets
|
||||
set mat=2
|
||||
|
||||
" No annoying sound on errors
|
||||
set noerrorbells
|
||||
set novisualbell
|
||||
set t_vb=
|
||||
set tm=500
|
||||
|
||||
" Properly disable sound on errors on MacVim
|
||||
if has("gui_macvim")
|
||||
autocmd GUIEnter * set vb t_vb=
|
||||
endif
|
||||
|
||||
|
||||
" Add a bit extra margin to the left
|
||||
set foldcolumn=1
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Colors and Fonts
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Enable syntax highlighting
|
||||
syntax enable
|
||||
|
||||
" Enable 256 colors palette in Gnome Terminal
|
||||
if $COLORTERM == 'gnome-terminal'
|
||||
set t_Co=256
|
||||
endif
|
||||
|
||||
set background=dark
|
||||
|
||||
" Set extra options when running in GUI mode
|
||||
if has("gui_running")
|
||||
set guioptions-=T
|
||||
set guioptions-=e
|
||||
set t_Co=256
|
||||
set guitablabel=%M\ %t
|
||||
endif
|
||||
|
||||
" Set utf8 as standard encoding and en_US as the standard language
|
||||
set encoding=utf8
|
||||
|
||||
" Use Unix as the standard file type
|
||||
set ffs=unix,dos,mac
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Files, backups and undo
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Turn backup off, since most stuff is in SVN, git et.c anyway...
|
||||
set nobackup
|
||||
set nowb
|
||||
set noswapfile
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Text, tab and indent related
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Use spaces instead of tabs
|
||||
set expandtab
|
||||
|
||||
" Be smart when using tabs ;)
|
||||
set smarttab
|
||||
|
||||
" 1 tab == 4 spaces
|
||||
set shiftwidth=4
|
||||
set tabstop=4
|
||||
|
||||
" Linebreak on 500 characters
|
||||
set lbr
|
||||
set tw=500
|
||||
|
||||
set ai "Auto indent
|
||||
set si "Smart indent
|
||||
set wrap "Wrap lines
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Moving around, tabs, windows and buffers
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Map <Space> to / (search) and Ctrl-<Space> to ? (backwards search)
|
||||
map <space> /
|
||||
map <c-space> ?
|
||||
|
||||
" Disable highlight when <leader><cr> is pressed
|
||||
map <silent> <leader><cr> :noh<cr>
|
||||
|
||||
" Smart way to move between windows
|
||||
map <C-j> <C-W>j
|
||||
map <C-k> <C-W>k
|
||||
map <C-h> <C-W>h
|
||||
map <C-l> <C-W>l
|
||||
|
||||
" Close the current buffer
|
||||
map <leader>bd :Bclose<cr>:tabclose<cr>gT
|
||||
|
||||
" Close all the buffers
|
||||
map <leader>ba :bufdo bd<cr>
|
||||
|
||||
map <leader>l :bnext<cr>
|
||||
map <leader>h :bprevious<cr>
|
||||
|
||||
" Useful mappings for managing tabs
|
||||
map <leader>tn :tabnew<cr>
|
||||
map <leader>to :tabonly<cr>
|
||||
map <leader>tc :tabclose<cr>
|
||||
map <leader>tm :tabmove
|
||||
map <leader>t<leader> :tabnext
|
||||
|
||||
" Let 'tl' toggle between this and the last accessed tab
|
||||
let g:lasttab = 1
|
||||
nmap <Leader>tl :exe "tabn ".g:lasttab<CR>
|
||||
au TabLeave * let g:lasttab = tabpagenr()
|
||||
|
||||
|
||||
" Opens a new tab with the current buffer's path
|
||||
" Super useful when editing files in the same directory
|
||||
map <leader>te :tabedit <c-r>=expand("%:p:h")<cr>/
|
||||
|
||||
" Switch CWD to the directory of the open buffer
|
||||
map <leader>cd :cd %:p:h<cr>:pwd<cr>
|
||||
|
||||
" Specify the behavior when switching between buffers
|
||||
try
|
||||
set switchbuf=useopen,usetab,newtab
|
||||
set stal=2
|
||||
catch
|
||||
endtry
|
||||
|
||||
" Return to last edit position when opening files (You want this!)
|
||||
au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
|
||||
|
||||
|
||||
""""""""""""""""""""""""""""""
|
||||
" => Status line
|
||||
""""""""""""""""""""""""""""""
|
||||
" Always show the status line
|
||||
set laststatus=2
|
||||
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Editing mappings
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
" Move a line of text using ALT+[jk] or Command+[jk] on mac
|
||||
nmap <M-j> mz:m+<cr>`z
|
||||
nmap <M-k> mz:m-2<cr>`z
|
||||
vmap <M-j> :m'>+<cr>`<my`>mzgv`yo`z
|
||||
vmap <M-k> :m'<-2<cr>`>my`<mzgv`yo`z
|
||||
|
||||
if has("mac") || has("macunix")
|
||||
nmap <D-j> <M-j>
|
||||
nmap <D-k> <M-k>
|
||||
vmap <D-j> <M-j>
|
||||
vmap <D-k> <M-k>
|
||||
endif
|
||||
|
||||
" Delete trailing white space on save, useful for some filetypes ;)
|
||||
fun! CleanExtraSpaces()
|
||||
let save_cursor = getpos(".")
|
||||
let old_query = getreg('/')
|
||||
silent! %s/\s\+$//e
|
||||
call setpos('.', save_cursor)
|
||||
call setreg('/', old_query)
|
||||
endfun
|
||||
|
||||
if has("autocmd")
|
||||
autocmd BufWritePre *.fs,*.fsi,*.txt,*.js,*.py,*.wiki,*.sh,*.coffee :call CleanExtraSpaces()
|
||||
endif
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Spell checking
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Pressing ,ss will toggle and untoggle spell checking
|
||||
map <leader>ss :setlocal spell!<cr>
|
||||
|
||||
" Shortcuts using <leader>
|
||||
map <leader>sn ]s
|
||||
map <leader>sp [s
|
||||
map <leader>sa zg
|
||||
map <leader>s? z=
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Misc
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Remove the Windows ^M - when the encodings gets messed up
|
||||
noremap <Leader>m mmHmt:%s/<C-V><cr>//ge<cr>'tzt'm
|
||||
|
||||
" Quickly open a buffer for scribble
|
||||
map <leader>q :e ~/buffer<cr>
|
||||
|
||||
" Quickly open a markdown buffer for scribble
|
||||
map <leader>x :e ~/buffer.md<cr>
|
||||
|
||||
" Toggle paste mode on and off
|
||||
map <leader>pp :setlocal paste!<cr>
|
||||
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" => Helper functions
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" Returns true if paste mode is enabled
|
||||
function! HasPaste()
|
||||
if &paste
|
||||
return 'PASTE MODE '
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Don't close window, when deleting a buffer
|
||||
command! Bclose call <SID>BufcloseCloseIt()
|
||||
function! <SID>BufcloseCloseIt()
|
||||
let l:currentBufNum = bufnr("%")
|
||||
let l:alternateBufNum = bufnr("#")
|
||||
|
||||
if buflisted(l:alternateBufNum)
|
||||
buffer #
|
||||
else
|
||||
bnext
|
||||
endif
|
||||
|
||||
if bufnr("%") == l:currentBufNum
|
||||
new
|
||||
endif
|
||||
|
||||
if buflisted(l:currentBufNum)
|
||||
execute("bdelete! ".l:currentBufNum)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! CmdLine(str)
|
||||
call feedkeys(":" . a:str)
|
||||
endfunction
|
||||
|
||||
function! VisualSelection(direction, extra_filter) range
|
||||
let l:saved_reg = @"
|
||||
execute "normal! vgvy"
|
||||
|
||||
let l:pattern = escape(@", "\\/.*'$^~[]")
|
||||
let l:pattern = substitute(l:pattern, "\n$", "", "")
|
||||
|
||||
if a:direction == 'gv'
|
||||
call CmdLine("Ack '" . l:pattern . "' " )
|
||||
elseif a:direction == 'replace'
|
||||
call CmdLine("%s" . '/'. l:pattern . '/')
|
||||
endif
|
||||
|
||||
let @/ = l:pattern
|
||||
let @" = l:saved_reg
|
||||
endfunction
|
||||
|
||||
nnoremap <leader>c :!cargo clippy
|
||||
|
||||
set statusline+=%#warningmsg#
|
||||
set statusline+=%{SyntasticStatuslineFlag()}
|
||||
set statusline+=%*
|
||||
" Format the status line
|
||||
set statusline=\ %{HasPaste()}%F%m%r%h\ %w\ \ CWD:\ %r%{getcwd()}%h\ \ \ Line:\ %l\ \ Column:\ %c
|
||||
|
11
mbsync.nix
Normal file
11
mbsync.nix
Normal file
@@ -0,0 +1,11 @@
|
||||
{pkgs}:
|
||||
pkgs.buildEnv {
|
||||
name = "isync-oauth2";
|
||||
paths = [pkgs.isync];
|
||||
pathsToLink = ["/bin"];
|
||||
nativeBuildInputs = [pkgs.makeWrapper];
|
||||
postBuild = ''
|
||||
wrapProgram "$out/bin/mbsync" \
|
||||
--prefix SASL_PATH : "${pkgs.cyrus_sasl}/lib/sasl2:${pkgs.cyrus-sasl-xoauth2}/lib/sasl2"
|
||||
'';
|
||||
}
|
10
overlays.nix
10
overlays.nix
@@ -1,10 +0,0 @@
|
||||
[
|
||||
(self: super: {
|
||||
# https://github.com/NixOS/nixpkgs/issues/153304
|
||||
alacritty = super.alacritty.overrideAttrs (
|
||||
o: rec {
|
||||
doCheck = false;
|
||||
}
|
||||
);
|
||||
})
|
||||
]
|
16
python.nix
16
python.nix
@@ -1,16 +0,0 @@
|
||||
{ pkgs }:
|
||||
|
||||
let my-python-packages = python-packages: with python-packages; [
|
||||
pip
|
||||
mathlibtools
|
||||
];
|
||||
in
|
||||
|
||||
let packageOverrides = self: super: {
|
||||
# Test failures on darwin ("windows-1252"); just skip pytest
|
||||
# (required for elan)
|
||||
beautifulsoup4 = super.beautifulsoup4.overridePythonAttrs (old: { pytestCheckPhase = "true"; });
|
||||
};
|
||||
in
|
||||
|
||||
(pkgs.python3.override { inherit packageOverrides; }).withPackages my-python-packages
|
@@ -1,44 +0,0 @@
|
||||
{ pkgs, config, lib, ... }:
|
||||
|
||||
let src = ./GlobalSettingsStorage.DotSettings; in
|
||||
let link = ./link.sh; in
|
||||
|
||||
let riderconfig =
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "rider-config";
|
||||
version = "2021.2";
|
||||
|
||||
src = src;
|
||||
phases = [ "unpackPhase" ];
|
||||
unpackPhase = ''
|
||||
mkdir -p "$out"
|
||||
cp ${src} "$out/GlobalSettingsStorage.DotSettings"
|
||||
cp ${link} "$out/link.sh"
|
||||
chmod u+x "$out/link.sh"
|
||||
sed -i 's_NIX-DOTNET-SDK_${config.rider.dotnet}_' "$out/GlobalSettingsStorage.DotSettings"
|
||||
sed -i "s!NIX-RIDER-CONFIG!$out!" "$out/link.sh"
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
'';
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
rider.enable = lib.mkOption { default = false; };
|
||||
rider.username = lib.mkOption { type = lib.types.str; example = "Patrick"; };
|
||||
rider.dotnet = lib.mkOption { default = pkgs.dotnet-sdk; };
|
||||
};
|
||||
|
||||
config = lib.mkIf config.rider.enable {
|
||||
home.activation.jetbrains-rider-settings = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
|
||||
dest="/Users/${config.rider.username}/Library/Application Support/JetBrains"
|
||||
if [ -e "$dest" ]; then
|
||||
find "$dest" -maxdepth 1 -type d -name 'Rider*' -exec sh -c '${riderconfig}/link.sh "$0"' {} \;
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
@@ -1,115 +0,0 @@
|
||||
{pkgs}:
|
||||
with pkgs.vscode-extensions;
|
||||
[
|
||||
bbenoist.nix
|
||||
haskell.haskell
|
||||
yzhang.markdown-all-in-one
|
||||
james-yu.latex-workshop
|
||||
vscodevim.vim
|
||||
# Not supported on Darwin, apparently
|
||||
# ms-dotnettools.csharp
|
||||
]
|
||||
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
|
||||
{
|
||||
name = "remote-containers";
|
||||
publisher = "ms-vscode-remote";
|
||||
version = "0.222.0";
|
||||
sha256 = "4Li0sYfHOsJMn5FJtvDTGKoGPcRmoosD9tZ7q9H9DfQ=";
|
||||
}
|
||||
{
|
||||
name = "remote-ssh";
|
||||
publisher = "ms-vscode-remote";
|
||||
version = "0.75.2022021903";
|
||||
sha256 = "hTRfoUHKrIOSV8eZ/62ewaII5291huXjOZ++dRUmKoI=";
|
||||
}
|
||||
{
|
||||
name = "vscode-docker";
|
||||
publisher = "ms-azuretools";
|
||||
version = "1.19.0";
|
||||
sha256 = "UPUfTOc5xJhI5ACm2oyWqtZ4zNxZjy16D6Mf30eHFEI=";
|
||||
}
|
||||
{
|
||||
name = "code-gnu-global";
|
||||
publisher = "austin";
|
||||
version = "0.2.2";
|
||||
sha256 = "1fz89m6ja25aif6wszg9h2fh5vajk6bj3lp1mh0l2b04nw2mzhd5";
|
||||
}
|
||||
{
|
||||
name = "rust-analyzer";
|
||||
publisher = "matklad";
|
||||
version = "0.3.939";
|
||||
sha256 = "t5CCUdFCiSYrMsBHG5eOfg3sXMacFWiR0hmVa7S1i8Y=";
|
||||
}
|
||||
{
|
||||
name = "vscode-lldb";
|
||||
publisher = "vadimcn";
|
||||
version = "1.6.10";
|
||||
sha256 = "CGVVs//jIZM8uX7Wc9gM4aQGwECi88eIpfPqU2hKbeA=";
|
||||
}
|
||||
{
|
||||
name = "toml";
|
||||
publisher = "be5invis";
|
||||
version = "0.6.0";
|
||||
sha256 = "yk7buEyQIw6aiUizAm+sgalWxUibIuP9crhyBaOjC2E=";
|
||||
}
|
||||
{
|
||||
name = "Ionide-Paket";
|
||||
publisher = "Ionide";
|
||||
version = "2.0.0";
|
||||
sha256 = "1455zx5p0d30b1agdi1zw22hj0d3zqqglw98ga8lj1l1d757gv6v";
|
||||
}
|
||||
{
|
||||
name = "lean";
|
||||
publisher = "jroesch";
|
||||
version = "0.16.46";
|
||||
sha256 = "hjflz5JHVr1YWq6QI9DpdNPY1uL7lAuQTMAdwCtLEfY=";
|
||||
}
|
||||
{
|
||||
name = "language-haskell";
|
||||
publisher = "justusadam";
|
||||
version = "3.4.0";
|
||||
sha256 = "0ab7m5jzxakjxaiwmg0jcck53vnn183589bbxh3iiylkpicrv67y";
|
||||
}
|
||||
{
|
||||
name = "vscode-clang";
|
||||
publisher = "mitaki28";
|
||||
version = "0.2.4";
|
||||
sha256 = "0sys2h4jvnannlk2q02lprc2ss9nkgh0f0kwa188i7viaprpnx23";
|
||||
}
|
||||
{
|
||||
name = "dotnet-interactive-vscode";
|
||||
publisher = "ms-dotnettools";
|
||||
version = "1.0.3103011";
|
||||
sha256 = "a3u9NKsqHZKhZkKqJqo+LgJFTL2yhehBepTOFOXE+jY=";
|
||||
}
|
||||
{
|
||||
name = "python";
|
||||
publisher = "ms-python";
|
||||
version = "2022.0.1814523869";
|
||||
sha256 = "hXTVZ7gbu234zyAg0ZrZPUo6oULB98apxe79U2yQHD4=";
|
||||
}
|
||||
{
|
||||
name = "mono-debug";
|
||||
publisher = "ms-vscode";
|
||||
version = "0.16.2";
|
||||
sha256 = "10hixqkw5r3cg52xkbky395lv72sb9d9wrngdvmrwx62hkbk5465";
|
||||
}
|
||||
{
|
||||
name = "Theme-MarkdownKit";
|
||||
publisher = "ms-vscode";
|
||||
version = "0.1.4";
|
||||
sha256 = "1im78k2gaj6cri2jcvy727qdy25667v0f7vv3p3hv13apzxgzl0l";
|
||||
}
|
||||
{
|
||||
name = "trailing-spaces";
|
||||
publisher = "shardulm94";
|
||||
version = "0.3.1";
|
||||
sha256 = "0h30zmg5rq7cv7kjdr5yzqkkc1bs20d72yz9rjqag32gwf46s8b8";
|
||||
}
|
||||
{
|
||||
name = "debug";
|
||||
publisher = "webfreak";
|
||||
version = "0.25.1";
|
||||
sha256 = "1l01sv6kwh8dlv3kygkkd0z9m37hahflzd5bx1wwij5p61jg7np9";
|
||||
}
|
||||
]
|
Reference in New Issue
Block a user