mirror of
https://github.com/Smaug123/nix-dotfiles
synced 2025-10-08 07:58:40 +00:00
Merge main
This commit is contained in:
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v4"
|
uses: "actions/checkout@v4"
|
||||||
- name: "Install Nix"
|
- name: "Install Nix"
|
||||||
uses: "cachix/install-nix-action@v25"
|
uses: "cachix/install-nix-action@V27"
|
||||||
with: { "extra_nix_config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" }
|
with: { "extra_nix_config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" }
|
||||||
- name: "Check flake"
|
- name: "Check flake"
|
||||||
run: "nix flake check --all-systems"
|
run: "nix flake check --all-systems"
|
||||||
|
@@ -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)
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{pkgs, ...}: let
|
{pkgs, ...}: let
|
||||||
python = import ./python.nix {inherit pkgs;};
|
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||||
in {
|
in {
|
||||||
nix.useDaemon = true;
|
nix.useDaemon = true;
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ in {
|
|||||||
pkgs.rustup
|
pkgs.rustup
|
||||||
pkgs.libiconv
|
pkgs.libiconv
|
||||||
pkgs.clang
|
pkgs.clang
|
||||||
python
|
pkgs.python3
|
||||||
];
|
];
|
||||||
|
|
||||||
users.users.patrick = {
|
users.users.patrick = {
|
||||||
@@ -21,16 +21,58 @@ in {
|
|||||||
|
|
||||||
# This line is required; otherwise, on shell startup, you won't have Nix stuff in the PATH.
|
# This line is required; otherwise, on shell startup, you won't have Nix stuff in the PATH.
|
||||||
programs.zsh.enable = true;
|
programs.zsh.enable = true;
|
||||||
|
programs.gnupg.agent.enable = true;
|
||||||
|
|
||||||
# Use a custom configuration.nix location.
|
# Use a custom configuration.nix location.
|
||||||
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
|
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
|
||||||
environment.darwinConfig = "$HOME/.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";
|
||||||
|
StartInterval = 60;
|
||||||
|
RunAtLoad = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
backup-calendar = {
|
||||||
|
command = ''${pkgs.bash}/bin/bash -c "mkdir -p /Users/patrick/Backups && if [ ! -d /Users/patrick/Backups/radicale ] ; then ${pkgs.git}/bin/git clone root@patrickstevens.co.uk:/preserve/radicale/data/.git /Users/patrick/Backups/radicale >/tmp/radicale.out.log 2>/tmp/radicale.err.log; fi && ${pkgs.git}/bin/git --git-dir /Users/patrick/Backups/radicale/.git pull 2>>/tmp/radicale.err.log"'';
|
||||||
|
serviceConfig = {
|
||||||
|
KeepAlive = false;
|
||||||
|
UserName = "patrick";
|
||||||
|
StartInterval = 3600;
|
||||||
|
RunAtLoad = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Auto upgrade nix package and the daemon service.
|
# Auto upgrade nix package and the daemon service.
|
||||||
services.nix-daemon.enable = true;
|
services.nix-daemon.enable = true;
|
||||||
nix.package = pkgs.nixVersions.stable;
|
nix.package = pkgs.nixVersions.stable;
|
||||||
nix.gc.automatic = true;
|
nix.gc.automatic = true;
|
||||||
nix.nixPath = ["darwin=/nix/store/zq4v3pi2wsfsrjkpk71kcn8srhbwjabf-nix-darwin"];
|
|
||||||
|
|
||||||
# Sandbox causes failure: https://github.com/NixOS/nix/issues/4119
|
# Sandbox causes failure: https://github.com/NixOS/nix/issues/4119
|
||||||
nix.settings.sandbox = false;
|
nix.settings.sandbox = false;
|
||||||
|
88
flake.lock
generated
88
flake.lock
generated
@@ -7,11 +7,11 @@
|
|||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705557527,
|
"lastModified": 1716699938,
|
||||||
"narHash": "sha256-DuxxHTQ/W5KToFLWG4FUF8hLldNo9eXlbt7JgvhrMnY=",
|
"narHash": "sha256-AzTYm22tTDJy0tSqMDf95rmCxxoGTPClu0paGPeh5a0=",
|
||||||
"owner": "tpwrules",
|
"owner": "tpwrules",
|
||||||
"repo": "nixos-apple-silicon",
|
"repo": "nixos-apple-silicon",
|
||||||
"rev": "6e324ab06cb27a19409ebc1dc2664bf1e585490a",
|
"rev": "842306255856d7f5677c113e699101eb253e2e3f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -27,11 +27,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705915768,
|
"lastModified": 1716511055,
|
||||||
"narHash": "sha256-+Jlz8OAqkOwJlioac9wtpsCnjgGYUhvLpgJR/5tP9po=",
|
"narHash": "sha256-5Fe/DGgvMhPEMl9VdVxv3zvwRcwNDmW5eRJ0gk72w7U=",
|
||||||
"owner": "lnl7",
|
"owner": "lnl7",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "1e706ef323de76236eb183d7784f3bd57255ec0b",
|
"rev": "0bea8222f6e83247dd13b055d83e64bce02ee532",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -50,11 +50,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706170797,
|
"lastModified": 1716800073,
|
||||||
"narHash": "sha256-oGuFylWYU9OY5DaEJEK+Z7EL81Ln27xz01LN9+8U0P0=",
|
"narHash": "sha256-ZznQFA/Mjomt1phpfwVFtp3F2b6yvlyslmjJ6bgBB5w=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "emacs-overlay",
|
"repo": "emacs-overlay",
|
||||||
"rev": "dd5d758f69dd1ae6d0399763aa73ca34974ce9e3",
|
"rev": "165e4bc50493e402cf296c413edb479b04aeb339",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -83,11 +83,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705309234,
|
"lastModified": 1710146030,
|
||||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -121,11 +121,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706134977,
|
"lastModified": 1716736760,
|
||||||
"narHash": "sha256-KwNb1Li3K6vuVwZ77tFjZ89AWBo7AiCs9t0Cens4BsM=",
|
"narHash": "sha256-h3RmnNknKYtVA+EvUSra6QAwfZjC2q1G8YA7W0gat8Y=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "6359d40f6ec0b72a38e02b333f343c3d4929ec10",
|
"rev": "5d151429e1e79107acf6d06dcc5ace4e642ec239",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -148,27 +148,27 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705316053,
|
"lastModified": 1716293225,
|
||||||
"narHash": "sha256-J2Ey5mPFT8gdfL2XC0JTZvKaBw/b2pnyudEXFvl+dQM=",
|
"narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370",
|
"rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370",
|
"rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705916986,
|
"lastModified": 1716633019,
|
||||||
"narHash": "sha256-iBpfltu6QvN4xMpen6jGGEb6jOqmmVQKUrXdOJ32u8w=",
|
"narHash": "sha256-xim1b5/HZYbWaZKyI7cn9TJCM6ewNVZnesRr00mXeS4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d7f206b723e42edb09d9d753020a84b3061a79d8",
|
"rev": "9d29cd266cebf80234c98dd0b87256b6be0af44e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -178,29 +178,13 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705033721,
|
|
||||||
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "release-23.05",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706006310,
|
"lastModified": 1716715802,
|
||||||
"narHash": "sha256-nDPz0fj0IFcDhSTlXBU2aixcnGs2Jm4Zcuoj0QtmiXQ=",
|
"narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b43bb235efeab5324c5e486882ef46749188eee2",
|
"rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -232,7 +216,6 @@
|
|||||||
"emacs": "emacs",
|
"emacs": "emacs",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"sops-nix": "sops-nix",
|
|
||||||
"whisper": "whisper"
|
"whisper": "whisper"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -252,27 +235,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sops-nix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1706130372,
|
|
||||||
"narHash": "sha256-fHZxKH1DhsXPP36a2vJ91Zy6S+q6+QRIFlpLr9fZHU8=",
|
|
||||||
"owner": "Mic92",
|
|
||||||
"repo": "sops-nix",
|
|
||||||
"rev": "4606d9b1595e42ffd9b75b9e69667708c70b1d68",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "Mic92",
|
|
||||||
"repo": "sops-nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
|
47
flake.nix
47
flake.nix
@@ -18,10 +18,6 @@
|
|||||||
url = "github:nix-community/emacs-overlay";
|
url = "github:nix-community/emacs-overlay";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
sops-nix = {
|
|
||||||
url = "github:Mic92/sops-nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
apple-silicon = {
|
apple-silicon = {
|
||||||
url = "github:tpwrules/nixos-apple-silicon";
|
url = "github:tpwrules/nixos-apple-silicon";
|
||||||
};
|
};
|
||||||
@@ -31,23 +27,21 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
self,
|
|
||||||
darwin,
|
darwin,
|
||||||
emacs,
|
emacs,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
home-manager,
|
home-manager,
|
||||||
sops-nix,
|
|
||||||
apple-silicon,
|
apple-silicon,
|
||||||
whisper,
|
whisper,
|
||||||
...
|
...
|
||||||
} @ inputs: let
|
}: let
|
||||||
config = {
|
config = {
|
||||||
# contentAddressedByDefault = true;
|
# contentAddressedByDefault = true;
|
||||||
allowUnfree = true;
|
allowUnfree = true;
|
||||||
};
|
};
|
||||||
systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"];
|
systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"];
|
||||||
in let
|
in let
|
||||||
overlays = [emacs.overlay] ++ import ./overlays.nix;
|
overlays = [emacs.overlay];
|
||||||
recursiveMerge = attrList: let
|
recursiveMerge = attrList: let
|
||||||
f = attrPath:
|
f = attrPath:
|
||||||
builtins.zipAttrsWith (n: values:
|
builtins.zipAttrsWith (n: values:
|
||||||
@@ -62,6 +56,31 @@
|
|||||||
f [] attrList;
|
f [] attrList;
|
||||||
in {
|
in {
|
||||||
nixosConfigurations = {
|
nixosConfigurations = {
|
||||||
|
capybara = let
|
||||||
|
system = "x86_64-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/";
|
||||||
|
};
|
||||||
|
in [
|
||||||
|
./home-manager/capybara-config.nix
|
||||||
|
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)];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
earthworm = let
|
earthworm = let
|
||||||
system = "aarch64-linux";
|
system = "aarch64-linux";
|
||||||
in let
|
in let
|
||||||
@@ -74,6 +93,8 @@
|
|||||||
nixpkgs = pkgs;
|
nixpkgs = pkgs;
|
||||||
username = "patrick";
|
username = "patrick";
|
||||||
dotnet = pkgs.dotnet-sdk_8;
|
dotnet = pkgs.dotnet-sdk_8;
|
||||||
|
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||||
|
secretsPath = "/home/patrick/.secrets/";
|
||||||
};
|
};
|
||||||
in [
|
in [
|
||||||
./home-manager/earthworm-config.nix
|
./home-manager/earthworm-config.nix
|
||||||
@@ -82,7 +103,7 @@
|
|||||||
{
|
{
|
||||||
home-manager.useGlobalPkgs = true;
|
home-manager.useGlobalPkgs = true;
|
||||||
home-manager.useUserPackages = true;
|
home-manager.useUserPackages = true;
|
||||||
home-manager.users.patrick = recursiveMerge [(import ./home-manager/earthworm.nix args) (import ./home-manager/home.nix args)];
|
home-manager.users.patrick = recursiveMerge [(import ./home-manager/linux.nix args) (import ./home-manager/home.nix args)];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -101,10 +122,11 @@
|
|||||||
username = "patrick";
|
username = "patrick";
|
||||||
dotnet = pkgs.dotnet-sdk_8;
|
dotnet = pkgs.dotnet-sdk_8;
|
||||||
whisper = whisper.packages.${system};
|
whisper = whisper.packages.${system};
|
||||||
|
mbsync = import ./mbsync.nix {inherit pkgs;};
|
||||||
|
secretsPath = "/Users/patrick/.secrets/";
|
||||||
};
|
};
|
||||||
in [
|
in [
|
||||||
./darwin-configuration.nix
|
./darwin-configuration.nix
|
||||||
sops-nix.nixosModules.sops
|
|
||||||
home-manager.darwinModules.home-manager
|
home-manager.darwinModules.home-manager
|
||||||
{
|
{
|
||||||
home-manager.useGlobalPkgs = true;
|
home-manager.useGlobalPkgs = true;
|
||||||
@@ -121,11 +143,12 @@
|
|||||||
pkgs.stdenvNoCC.mkDerivation {
|
pkgs.stdenvNoCC.mkDerivation {
|
||||||
name = "fmt-check";
|
name = "fmt-check";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
nativeBuildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.shfmt];
|
nativeBuildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.shfmt pkgs.stylua];
|
||||||
checkPhase = ''
|
checkPhase = ''
|
||||||
find . -type f -name '*.sh' | xargs shfmt -d -s -i 2 -ci
|
find . -type f -name '*.sh' | xargs shfmt -d -s -i 2 -ci
|
||||||
alejandra -c .
|
alejandra -c .
|
||||||
find . -type f -name '*.sh' -exec shellcheck -x {} \;
|
find . -type f -name '*.sh' -exec shellcheck -x {} \;
|
||||||
|
find . -type f -name '*.lua' -exec stylua --check {} \;
|
||||||
'';
|
'';
|
||||||
installPhase = "mkdir $out";
|
installPhase = "mkdir $out";
|
||||||
dontBuild = true;
|
dontBuild = true;
|
||||||
@@ -143,7 +166,7 @@
|
|||||||
pkgs = import nixpkgs {inherit config system;};
|
pkgs = import nixpkgs {inherit config system;};
|
||||||
in {
|
in {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
buildInputs = [pkgs.alejandra pkgs.shellcheck];
|
buildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.stylua];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
34
hardware/capybara.nix
Normal file
34
hardware/capybara.nix
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# 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 = ["xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod"];
|
||||||
|
boot.initrd.kernelModules = [];
|
||||||
|
boot.kernelModules = ["kvm-intel"];
|
||||||
|
boot.extraModulePackages = [];
|
||||||
|
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/disk/by-uuid/63c5394d-55ce-48a9-8d7c-2b68f3b5f834";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" = {
|
||||||
|
device = "/dev/nvme0n1p2";
|
||||||
|
fsType = "vfat";
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices = [];
|
||||||
|
|
||||||
|
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
|
||||||
|
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||||
|
}
|
70
home-manager/capybara-config.nix
Normal file
70
home-manager/capybara-config.nix
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
imports = [
|
||||||
|
../hardware/capybara.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
boot.loader.grub.useOSProber = true;
|
||||||
|
|
||||||
|
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.vim
|
||||||
|
pkgs.wget
|
||||||
|
pkgs.tmux
|
||||||
|
pkgs.home-manager
|
||||||
|
pkgs.firefox
|
||||||
|
pkgs.steam-run
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.loginShellInit = ''
|
||||||
|
[[ "$(tty)" == /dev/tty1 ]] && 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
|
||||||
|
'';
|
||||||
|
|
||||||
|
programs.steam = {
|
||||||
|
enable = true;
|
||||||
|
remotePlay.openFirewall = true;
|
||||||
|
};
|
||||||
|
}
|
@@ -1,8 +1,4 @@
|
|||||||
{
|
{pkgs, ...}: {
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
imports = [
|
imports = [
|
||||||
../hardware/earthworm.nix
|
../hardware/earthworm.nix
|
||||||
];
|
];
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
username,
|
username,
|
||||||
|
mbsync,
|
||||||
dotnet,
|
dotnet,
|
||||||
|
secretsPath,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
# Let Home Manager install and manage itself.
|
# Let Home Manager install and manage itself.
|
||||||
@@ -21,59 +23,42 @@
|
|||||||
# changes in each release.
|
# changes in each release.
|
||||||
home.stateVersion = "22.05";
|
home.stateVersion = "22.05";
|
||||||
|
|
||||||
programs.tmux = {
|
fonts.fontconfig.enable = true;
|
||||||
shell = "\${nixpkgs.zsh}/bin/zsh";
|
|
||||||
escapeTime = 50;
|
|
||||||
mouse = false;
|
|
||||||
prefix = "C-b";
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.zsh = {
|
imports = [
|
||||||
enable = true;
|
# ./modules/agda.nix
|
||||||
autocd = true;
|
# ./modules/emacs.nix
|
||||||
enableAutosuggestions = true;
|
./modules/direnv.nix
|
||||||
enableCompletion = true;
|
./modules/tmux.nix
|
||||||
history = {
|
./modules/zsh.nix
|
||||||
expireDuplicatesFirst = true;
|
./modules/ripgrep.nix
|
||||||
};
|
./modules/alacritty.nix
|
||||||
oh-my-zsh = {
|
./modules/rust.nix
|
||||||
enable = true;
|
(import ./modules/mail.nix
|
||||||
plugins = ["git" "macos" "dircycle" "timer"];
|
{
|
||||||
theme = "robbyrussell";
|
inherit mbsync secretsPath;
|
||||||
};
|
pkgs = nixpkgs;
|
||||||
sessionVariables = {
|
})
|
||||||
EDITOR = "vim";
|
];
|
||||||
LC_ALL = "en_US.UTF-8";
|
|
||||||
LC_CTYPE = "en_US.UTF-8";
|
|
||||||
RUSTFLAGS = "-L ${nixpkgs.libiconv}/lib -L ${nixpkgs.libcxxabi}/lib -L ${nixpkgs.libcxx}/lib";
|
|
||||||
RUST_BACKTRACE = "full";
|
|
||||||
};
|
|
||||||
shellAliases = {
|
|
||||||
vim = "nvim";
|
|
||||||
view = "vim -R";
|
|
||||||
grep = "${nixpkgs.ripgrep}/bin/rg";
|
|
||||||
};
|
|
||||||
sessionVariables = {
|
|
||||||
RIPGREP_CONFIG_PATH = "/Users/${username}/.config/ripgrep/config";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.fzf = {
|
programs.fzf = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableZshIntegration = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
package = nixpkgs.gitAndTools.gitFull;
|
package = nixpkgs.gitAndTools.gitFull;
|
||||||
enable = true;
|
enable = true;
|
||||||
userName = "Smaug123";
|
userName = "Smaug123";
|
||||||
userEmail = "patrick+github@patrickstevens.co.uk";
|
userEmail = "3138005+Smaug123@users.noreply.github.com";
|
||||||
aliases = {
|
aliases = {
|
||||||
co = "checkout";
|
co = "checkout";
|
||||||
st = "status";
|
st = "status";
|
||||||
};
|
};
|
||||||
delta = {enable = true;};
|
difftastic.enable = true;
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
|
commit.gpgsign = true;
|
||||||
|
gpg.program = "${nixpkgs.gnupg}/bin/gpg";
|
||||||
|
user.signingkey = "7C97D679CF3BC4F9";
|
||||||
core = {
|
core = {
|
||||||
autocrlf = "input";
|
autocrlf = "input";
|
||||||
};
|
};
|
||||||
@@ -135,49 +120,146 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.neovim.enable = true;
|
services.syncthing = {
|
||||||
programs.neovim.plugins = with nixpkgs.vimPlugins; [
|
|
||||||
molokai
|
|
||||||
tagbar
|
|
||||||
fzf-vim
|
|
||||||
Ionide-vim
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
programs.direnv = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
enableZshIntegration = true;
|
};
|
||||||
nix-direnv.enable = true;
|
|
||||||
|
programs.neovim = let
|
||||||
|
debugPyEnv = nixpkgs.python3.withPackages (ps: [ps.debugpy]);
|
||||||
|
in {
|
||||||
|
enable = true;
|
||||||
|
plugins = [
|
||||||
|
{
|
||||||
|
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.pynvim
|
||||||
|
ps.pynvim-pp
|
||||||
|
ps.pyyaml
|
||||||
|
ps.std2
|
||||||
|
];
|
||||||
|
withRuby = true;
|
||||||
|
|
||||||
|
extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.readFile ./nvim/init.lua + "\n" + builtins.readFile ./nvim/python.lua;
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [
|
||||||
|
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.ltex-ls
|
||||||
|
nixpkgs.yaml-language-server
|
||||||
|
nixpkgs.csharp-ls
|
||||||
|
nixpkgs.netcoredbg
|
||||||
|
nixpkgs.nil
|
||||||
|
nixpkgs.fsautocomplete
|
||||||
nixpkgs.keepassxc
|
nixpkgs.keepassxc
|
||||||
nixpkgs.rust-analyzer
|
|
||||||
nixpkgs.tmux
|
|
||||||
nixpkgs.wget
|
nixpkgs.wget
|
||||||
nixpkgs.yt-dlp
|
nixpkgs.yt-dlp
|
||||||
nixpkgs.cmake
|
nixpkgs.cmake
|
||||||
@@ -188,54 +270,33 @@
|
|||||||
nixpkgs.hledger-web
|
nixpkgs.hledger-web
|
||||||
dotnet
|
dotnet
|
||||||
nixpkgs.jitsi-meet
|
nixpkgs.jitsi-meet
|
||||||
nixpkgs.ripgrep
|
|
||||||
nixpkgs.elan
|
nixpkgs.elan
|
||||||
nixpkgs.coreutils-prefixed
|
nixpkgs.coreutils-prefixed
|
||||||
nixpkgs.shellcheck
|
nixpkgs.shellcheck
|
||||||
nixpkgs.html-tidy
|
|
||||||
nixpkgs.hugo
|
|
||||||
nixpkgs.agda
|
|
||||||
nixpkgs.pijul
|
|
||||||
nixpkgs.universal-ctags
|
nixpkgs.universal-ctags
|
||||||
nixpkgs.asciinema
|
nixpkgs.asciinema
|
||||||
nixpkgs.git-lfs
|
nixpkgs.git-lfs
|
||||||
nixpkgs.imagemagick
|
nixpkgs.imagemagick
|
||||||
nixpkgs.nixpkgs-fmt
|
nixpkgs.nixpkgs-fmt
|
||||||
nixpkgs.rnix-lsp
|
|
||||||
nixpkgs.grpc-tools
|
|
||||||
nixpkgs.element-desktop
|
|
||||||
nixpkgs.ihp-new
|
|
||||||
nixpkgs.direnv
|
|
||||||
nixpkgs.lnav
|
nixpkgs.lnav
|
||||||
nixpkgs.age
|
nixpkgs.age
|
||||||
nixpkgs.nodejs
|
nixpkgs.nodejs
|
||||||
nixpkgs.sqlitebrowser
|
nixpkgs.nodePackages.pyright
|
||||||
nixpkgs.typst
|
|
||||||
nixpkgs.poetry
|
|
||||||
nixpkgs.woodpecker-agent
|
nixpkgs.woodpecker-agent
|
||||||
nixpkgs.alacritty
|
|
||||||
nixpkgs.lynx
|
nixpkgs.lynx
|
||||||
nixpkgs.alejandra
|
nixpkgs.alejandra
|
||||||
nixpkgs.ffmpeg
|
nixpkgs.ffmpeg
|
||||||
nixpkgs.bat
|
nixpkgs.bat
|
||||||
nixpkgs.pandoc
|
nixpkgs.pandoc
|
||||||
|
nixpkgs.fd
|
||||||
|
nixpkgs.sumneko-lua-language-server
|
||||||
|
nixpkgs.gnupg
|
||||||
];
|
];
|
||||||
|
|
||||||
home.file.".mailcap".source = ./mailcap;
|
|
||||||
home.file.".ideavimrc".source = ./ideavimrc;
|
home.file.".ideavimrc".source = ./ideavimrc;
|
||||||
home.file.".config/yt-dlp/config".source = ./youtube-dl.conf;
|
home.file.".config/yt-dlp/config".source = ./youtube-dl.conf;
|
||||||
home.file.".config/ripgrep/config".source = ./ripgrep.conf;
|
home.file.".config/ripgrep/config".source = ./ripgrep.conf;
|
||||||
|
|
||||||
programs.emacs = {
|
|
||||||
enable = true;
|
|
||||||
package = nixpkgs.emacs;
|
|
||||||
extraPackages = epkgs: [];
|
|
||||||
extraConfig = ''
|
|
||||||
(load-file (let ((coding-system-for-read 'utf-8))
|
|
||||||
(shell-command-to-string "agda-mode locate")))
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.firefox = {
|
programs.firefox = {
|
||||||
enable = true;
|
enable = true;
|
||||||
profiles = {
|
profiles = {
|
||||||
@@ -316,5 +377,15 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
programs.emacs = {
|
||||||
|
enable = true;
|
||||||
|
package = nixpkgs.emacs;
|
||||||
|
extraPackages = epkgs: [];
|
||||||
|
extraConfig = ''
|
||||||
|
(load-file (let ((coding-system-for-read 'utf-8))
|
||||||
|
(shell-command-to-string "agda-mode locate")))
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
home.file.".cargo/config.toml".source = ./cargo-config.toml;
|
home.file.".cargo/config.toml".source = ./cargo-config.toml;
|
||||||
}
|
}
|
||||||
|
@@ -1,371 +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,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
|
|
||||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
||||||
|
|
||||||
" 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
|
|
||||||
nnoremap <leader>j :%!python -m json.tool
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
set fileformat=unix
|
|
||||||
|
|
||||||
let g:fsharp#fsautocomplete_command =
|
|
||||||
\ [ 'dotnet',
|
|
||||||
\ 'fsautocomplete',
|
|
||||||
\ '--background-service-enabled'
|
|
||||||
\ ]
|
|
||||||
|
|
||||||
let g:fsharp#show_signature_on_cursor_move = 1
|
|
||||||
if has('nvim') && exists('*nvim_open_win')
|
|
||||||
augroup FSharpGroup
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType fsharp nnoremap <leader>t :call fsharp#showTooltip()<CR>
|
|
||||||
augroup END
|
|
||||||
endif
|
|
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
|
||||||
|
];
|
||||||
|
}
|
17
home-manager/modules/alacritty.nix
Normal file
17
home-manager/modules/alacritty.nix
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{pkgs, ...}: {
|
||||||
|
programs.alacritty = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
font = {
|
||||||
|
normal = {
|
||||||
|
family = "FiraCode Nerd Font Mono";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
179
home-manager/modules/mail.nix
Normal file
179
home-manager/modules/mail.nix
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
{
|
||||||
|
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'';
|
||||||
|
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 = 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 @@
|
|||||||
|
|
||||||
|
|
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;
|
||||||
|
}
|
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
|
||||||
|
];
|
||||||
|
}
|
18
home-manager/modules/tmux.nix
Normal file
18
home-manager/modules/tmux.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{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'
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
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;
|
||||||
|
}
|
32
home-manager/modules/zsh/zshrc
Normal file
32
home-manager/modules/zsh/zshrc
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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"
|
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,
|
||||||
|
})
|
160
home-manager/nvim/dotnet.lua
Normal file
160
home-manager/nvim/dotnet.lua
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
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.register({
|
||||||
|
s = {
|
||||||
|
name = ".NET solution",
|
||||||
|
b = { BuildDotNetSolution, "Build .NET solution" },
|
||||||
|
t = { TestDotNetSolution, "Test .NET solution" },
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader"), 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" },
|
||||||
|
callback = function()
|
||||||
|
FindAndRegisterSolution(false)
|
||||||
|
end,
|
||||||
|
})
|
296
home-manager/nvim/init.lua
Normal file
296
home-manager/nvim/init.lua
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
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)
|
||||||
|
-- Note: we could (if desired) view all groups, because the `node.mapping` table looks like this:
|
||||||
|
-- { prefix = "g", group = true, keys = {...}}
|
||||||
|
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.register({
|
||||||
|
[vim.api.nvim_get_var("maplocalleader")] = {
|
||||||
|
DisplayAllMappingsWithTelescope,
|
||||||
|
"View all mappings",
|
||||||
|
},
|
||||||
|
m = {
|
||||||
|
p = { MarkdownPreview, "Preview Markdown in Lynx" },
|
||||||
|
d = { RemoveCarriageReturn, "Delete carriage returns from file" },
|
||||||
|
},
|
||||||
|
["j"] = {
|
||||||
|
FormatJson,
|
||||||
|
"Auto-format JSON",
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
|
||||||
|
whichkey.register({
|
||||||
|
g = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").grep_string()
|
||||||
|
end,
|
||||||
|
"Find instances of text under cursor",
|
||||||
|
},
|
||||||
|
h = {
|
||||||
|
name = "Find historical...",
|
||||||
|
f = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").oldfiles()
|
||||||
|
end,
|
||||||
|
"List previously open files",
|
||||||
|
},
|
||||||
|
c = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").command_history()
|
||||||
|
end,
|
||||||
|
"List previously run commands",
|
||||||
|
},
|
||||||
|
s = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").search_history()
|
||||||
|
end,
|
||||||
|
"List previously run searches",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
m = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").marks()
|
||||||
|
end,
|
||||||
|
"List marks",
|
||||||
|
},
|
||||||
|
["cd"] = {
|
||||||
|
ChangeToCurrentDirectory,
|
||||||
|
"Switch CWD to the directory of the open buffer",
|
||||||
|
},
|
||||||
|
["ss"] = {
|
||||||
|
ToggleSpell,
|
||||||
|
"Toggle spell-checker on or off",
|
||||||
|
},
|
||||||
|
[vim.api.nvim_get_var("mapleader")] = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").find_files()
|
||||||
|
end,
|
||||||
|
"Find files by name",
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("mapleader") })
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd({"BufRead","BufNewFile"}, {
|
||||||
|
pattern = {"Directory.Build.props", "*.fsproj", "*.csproj"},
|
||||||
|
callback = function()
|
||||||
|
vim.bo.filetype = "xml"
|
||||||
|
end,
|
||||||
|
})
|
239
home-manager/nvim/ionide-vim.lua
Normal file
239
home-manager/nvim/ionide-vim.lua
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
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.register({
|
||||||
|
f = {
|
||||||
|
name = "F#",
|
||||||
|
t = { ":call fsharp#showTooltip()<CR>", "Show F# Tooltip" },
|
||||||
|
["si"] = { ":call fsharp#toggleFsi()<CR>", "Toggle FSI (F# Interactive)" },
|
||||||
|
["sl"] = { ":call fsharp#sendLineToFsi()<cr>", "Send line to FSI (F# Interactive)" },
|
||||||
|
r = {
|
||||||
|
name = "Run F# project",
|
||||||
|
d = { ":RunFSharpProject Debug", "Run F# project in debug configuration" },
|
||||||
|
r = { ":RunFSharpProject Release", "Run F# project in release configuration" },
|
||||||
|
},
|
||||||
|
p = {
|
||||||
|
":PublishFSharpProject",
|
||||||
|
"Publish F# project",
|
||||||
|
},
|
||||||
|
b = {
|
||||||
|
"Build F# project",
|
||||||
|
a = { BuildFSharpProjects, "Build all projects" },
|
||||||
|
s = { ":BuildFSharpProject", "Build specified project" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader"), 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").register({
|
||||||
|
l = {
|
||||||
|
i = { "<Cmd>LeanInfoviewToggle<CR>", "Toggle Lean info view" },
|
||||||
|
p = { "<Cmd>LeanInfoviewPinTogglePause<CR>", "Pause Lean info view" },
|
||||||
|
s = { "<Cmd>LeanSorryFill<CR>", "Fill open goals with sorry" },
|
||||||
|
w = { "<Cmd>LeanInfoviewEnableWidgets<CR>", "Enable Lean widgets" },
|
||||||
|
W = { "<Cmd>LeanInfoviewDisableWidgets<CR>", "Disable Lean widgets" },
|
||||||
|
["?"] = {
|
||||||
|
"<Cmd>LeanAbbreviationsReverseLookup<CR>",
|
||||||
|
"Show what Lean abbreviation produces the symbol under the cursor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
|
191
home-manager/nvim/lspconfig.lua
Normal file
191
home-manager/nvim/lspconfig.lua
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
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")["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")["bashls"].setup({})
|
||||||
|
require("lspconfig")["dockerls"].setup({})
|
||||||
|
require("lspconfig")["html"].setup({
|
||||||
|
capabilities = capabilities,
|
||||||
|
})
|
||||||
|
require("lspconfig")["ltex"].setup({})
|
||||||
|
|
||||||
|
require("lspconfig")["lua_ls"].setup({
|
||||||
|
on_init = function(client)
|
||||||
|
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.register({
|
||||||
|
l = {
|
||||||
|
name = "loclist-related commands",
|
||||||
|
p = { vim.diagnostic.goto_prev, "Go to previous entry in loclist" },
|
||||||
|
n = { vim.diagnostic.goto_next, "Go to next entry in loclist" },
|
||||||
|
l = { ToggleLocList, "Toggle loclist" },
|
||||||
|
f = { vim.diagnostic.open_float, "Open current loclist entry in floating window" },
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("mapleader") })
|
||||||
|
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.register({
|
||||||
|
g = {
|
||||||
|
name = "Go-to related commands",
|
||||||
|
D = { vim.lsp.buf.declaration, "Go to declaration" },
|
||||||
|
d = { vim.lsp.buf.definition, "Go to definition" },
|
||||||
|
i = { vim.lsp.buf.implementation, "Go to implementation" },
|
||||||
|
r = {
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").lsp_references()
|
||||||
|
end,
|
||||||
|
"Find references",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
K = { vim.lsp.buf.hover, "Display information about symbol under cursor" },
|
||||||
|
})
|
||||||
|
whichkey.register({
|
||||||
|
["<C-k>"] = { vim.lsp.buf.signature_help, "Display signature information about symbol under cursor" },
|
||||||
|
})
|
||||||
|
whichkey.register({
|
||||||
|
w = {
|
||||||
|
a = { vim.lsp.buf.add_workspace_folder, "Add a path to the workspace folders list" },
|
||||||
|
r = { vim.lsp.buf.add_workspace_folder, "Remove a path from the workspace folders list" },
|
||||||
|
l = {
|
||||||
|
function()
|
||||||
|
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
|
||||||
|
end,
|
||||||
|
"Show the workspace folders list",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
f = {
|
||||||
|
function()
|
||||||
|
vim.lsp.buf.format({ async = true })
|
||||||
|
end,
|
||||||
|
"Autoformat",
|
||||||
|
},
|
||||||
|
c = {
|
||||||
|
a = { vim.lsp.buf.code_action, "Select a code action" },
|
||||||
|
},
|
||||||
|
r = {
|
||||||
|
n = { vim.lsp.buf.rename, "Rename variable" },
|
||||||
|
},
|
||||||
|
D = { vim.lsp.buf.type_definition, "Go to type definition" },
|
||||||
|
}, { prefix = vim.api.nvim_get_var("mapleader") })
|
||||||
|
end,
|
||||||
|
})
|
15
home-manager/nvim/nvim-dap-python.lua
Normal file
15
home-manager/nvim/nvim-dap-python.lua
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
require("dap-python").setup("%PYTHONENV%/bin/python")
|
||||||
|
|
||||||
|
do
|
||||||
|
local whichkey = require("which-key")
|
||||||
|
whichkey.register({
|
||||||
|
['pd'] = {
|
||||||
|
"Debugger-related commands",
|
||||||
|
t = {
|
||||||
|
"Tests",
|
||||||
|
f = { require("dap-python").test_class, "Run Python tests in the current file" },
|
||||||
|
c = { require("dap-python").test_method, "Run the Python test under the cursor" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
|
||||||
|
end
|
66
home-manager/nvim/nvim-dap.lua
Normal file
66
home-manager/nvim/nvim-dap.lua
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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.register({
|
||||||
|
d = {
|
||||||
|
name = "Debugger-related commands",
|
||||||
|
o = { dap.step_over, "Step over" },
|
||||||
|
i = { dap.step_into, "Step into" },
|
||||||
|
c = { dap.continue, "Continue" },
|
||||||
|
C = { dap.run_last, "Run with last debug configuration" },
|
||||||
|
b = { dap.toggle_breakpoint, "Toggle breakpoint" },
|
||||||
|
r = { dap.repl.open, "Open debug repl" },
|
||||||
|
v = {
|
||||||
|
name = "Commands to view debugger state",
|
||||||
|
v = {
|
||||||
|
function()
|
||||||
|
dap_ui.hover()
|
||||||
|
end,
|
||||||
|
"View value of expression under cursor",
|
||||||
|
},
|
||||||
|
s = {
|
||||||
|
function()
|
||||||
|
dap_ui.sidebar(dap_ui.scopes).open()
|
||||||
|
end,
|
||||||
|
"View values of all variables in all scopes",
|
||||||
|
},
|
||||||
|
f = {
|
||||||
|
function()
|
||||||
|
dap_ui.sidebar(dap_ui.frames).open()
|
||||||
|
end,
|
||||||
|
"View stack frames",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
t = { dap.terminate, "Terminate/stop/end debug session" },
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
66
home-manager/nvim/python.lua
Normal file
66
home-manager/nvim/python.lua
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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.register({
|
||||||
|
['pt'] = {
|
||||||
|
"Run Python tests",
|
||||||
|
f = { RunPythonTestsInFile, "Run Python tests in the current file" },
|
||||||
|
a = { RunAllPythonTests, "Run all Python tests" },
|
||||||
|
c = { RunPythonTestAtCursor, "Run the Python test under the cursor" },
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
|
||||||
|
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(),
|
||||||
|
})
|
5
home-manager/nvim/tokyonight.lua
Normal file
5
home-manager/nvim/tokyonight.lua
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
require("tokyonight").setup({
|
||||||
|
style = "night",
|
||||||
|
})
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
})
|
100
home-manager/nvim/venv-selector.lua
Normal file
100
home-manager/nvim/venv-selector.lua
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
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.register({
|
||||||
|
['pv'] = {
|
||||||
|
name = "Python virtual environment-related commands",
|
||||||
|
c = { CreateVenv, "Create virtual environment" },
|
||||||
|
l = { SelectVenv, "Load virtual environment" },
|
||||||
|
o = {
|
||||||
|
function()
|
||||||
|
vim.cmd("VenvSelect")
|
||||||
|
end,
|
||||||
|
"Choose (override) new virtual environment",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
|
||||||
|
end
|
88
home-manager/nvim/which-key.lua
Normal file
88
home-manager/nvim/which-key.lua
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
-- add operators that will trigger motion and text object completion
|
||||||
|
-- to enable all native operators, set the preset / operators plugin above
|
||||||
|
operators = { gc = "Comments" },
|
||||||
|
key_labels = {
|
||||||
|
-- override the label used to display some keys. It doesn't effect WK in any other way.
|
||||||
|
-- For example:
|
||||||
|
-- ["<space>"] = "SPC",
|
||||||
|
-- ["<cr>"] = "RET",
|
||||||
|
-- ["<tab>"] = "TAB",
|
||||||
|
},
|
||||||
|
motions = {
|
||||||
|
count = true,
|
||||||
|
},
|
||||||
|
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
|
||||||
|
},
|
||||||
|
popup_mappings = {
|
||||||
|
scroll_down = "<c-d>", -- binding to scroll down inside the popup
|
||||||
|
scroll_up = "<c-u>", -- binding to scroll up inside the popup
|
||||||
|
},
|
||||||
|
window = {
|
||||||
|
border = "none", -- none, single, double, shadow
|
||||||
|
position = "bottom", -- bottom, top
|
||||||
|
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size.
|
||||||
|
padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left]
|
||||||
|
winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent
|
||||||
|
zindex = 1000, -- positive value to position WhichKey above other floating windows.
|
||||||
|
},
|
||||||
|
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
|
||||||
|
},
|
||||||
|
ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label
|
||||||
|
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate
|
||||||
|
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
|
||||||
|
triggers = "auto", -- automatically setup triggers
|
||||||
|
-- triggers = {"<leader>"} -- or specifiy a list manually
|
||||||
|
-- list of triggers, where WhichKey should not wait for timeoutlen and show immediately
|
||||||
|
triggers_nowait = {
|
||||||
|
-- marks
|
||||||
|
"`",
|
||||||
|
"'",
|
||||||
|
"g`",
|
||||||
|
"g'",
|
||||||
|
-- registers
|
||||||
|
'"',
|
||||||
|
"<c-r>",
|
||||||
|
-- spelling
|
||||||
|
"z=",
|
||||||
|
},
|
||||||
|
triggers_blacklist = {
|
||||||
|
-- list of mode / prefixes that should never be hooked by WhichKey
|
||||||
|
-- this is mostly relevant for keymaps that start with a native binding
|
||||||
|
i = { "j", "k" },
|
||||||
|
v = { "j", "k" },
|
||||||
|
},
|
||||||
|
-- disable the WhichKey popup for certain buf types and file types.
|
||||||
|
-- Disabled by default for Telescope
|
||||||
|
disable = {
|
||||||
|
buftypes = {},
|
||||||
|
filetypes = {},
|
||||||
|
},
|
||||||
|
})
|
@@ -14,6 +14,8 @@ with pkgs.vscode-extensions;
|
|||||||
shardulm94.trailing-spaces
|
shardulm94.trailing-spaces
|
||||||
nvarner.typst-lsp
|
nvarner.typst-lsp
|
||||||
arrterian.nix-env-selector
|
arrterian.nix-env-selector
|
||||||
|
# Doesn't build on arm64
|
||||||
|
# vadimcn.vscode-lldb
|
||||||
]
|
]
|
||||||
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
|
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
|
||||||
{
|
{
|
||||||
@@ -41,22 +43,16 @@ with pkgs.vscode-extensions;
|
|||||||
sha256 = "yk7buEyQIw6aiUizAm+sgalWxUibIuP9crhyBaOjC2E=";
|
sha256 = "yk7buEyQIw6aiUizAm+sgalWxUibIuP9crhyBaOjC2E=";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "Ionide-Paket";
|
name = "ionide-fsharp";
|
||||||
publisher = "Ionide";
|
publisher = "ionide";
|
||||||
version = "2.0.0";
|
version = "7.18.1";
|
||||||
sha256 = "1455zx5p0d30b1agdi1zw22hj0d3zqqglw98ga8lj1l1d757gv6v";
|
sha256 = "sha256-6NPMQncoZhZYtx5c+qzarjuSzUXMb5HdKCzcHPCFUhU=";
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "lean";
|
|
||||||
publisher = "jroesch";
|
|
||||||
version = "0.16.58";
|
|
||||||
sha256 = "sha256-e5+C6dAcpet4xOiifmTJ1vm2pNrcPhx/mjl70il5NG0=";
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "lean4";
|
name = "lean4";
|
||||||
publisher = "leanprover";
|
publisher = "leanprover";
|
||||||
version = "0.0.101";
|
version = "0.0.128";
|
||||||
sha256 = "sha256-tHxP6X6qp3qVkkCn5TjhHrYHHvGGWJ4kYE7la6bPT6w=";
|
sha256 = "sha256-odRDOrlDFahweLzoQtpufY8UUwAutPFunqg7atTxnPo=";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "vscode-clang";
|
name = "vscode-clang";
|
||||||
@@ -89,23 +85,3 @@ with pkgs.vscode-extensions;
|
|||||||
sha256 = "sha256-lLLa8SN+Sf9Tbi7HeWYWa2KhPQFJyQWrf9l3EUljwYo=";
|
sha256 = "sha256-lLLa8SN+Sf9Tbi7HeWYWa2KhPQFJyQWrf9l3EUljwYo=";
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
++ [
|
|
||||||
(let
|
|
||||||
vsix = builtins.fetchurl {
|
|
||||||
name = "vadimcn-vscode-lldb.zip";
|
|
||||||
url = "https://github.com/vadimcn/codelldb/releases/download/v1.9.0/codelldb-aarch64-darwin.vsix";
|
|
||||||
sha256 = "sha256:1kxrxxlzasa9jl73lqh3n36fzpdgh2hbxpzp8fk6xyzcc5vm9zfb";
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.vscode-utils.buildVscodeExtension
|
|
||||||
{
|
|
||||||
vsix = vsix;
|
|
||||||
src = vsix;
|
|
||||||
vscodeExtPublisher = "vadimcn";
|
|
||||||
vscodeExtName = "vscode-lldb";
|
|
||||||
vscodeExtUniqueId = "vadimcn-vscode-lldb";
|
|
||||||
publisher = "vadimcn";
|
|
||||||
version = "1.9.0";
|
|
||||||
name = "vadimcn-vscode-lldb-1.9.0";
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
]
|
|
14
python.nix
14
python.nix
@@ -1,14 +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,2 +0,0 @@
|
|||||||
{nixpkgs, ...}: {
|
|
||||||
}
|
|
Reference in New Issue
Block a user