mirror of
				https://github.com/Smaug123/nix-dotfiles
				synced 2025-10-30 18:38:58 +00:00 
			
		
		
		
	Compare commits
	
		
			33 Commits
		
	
	
		
			b9bb4c49fa
			...
			mail-on-li
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2a962e928b | ||
|  | ad6a4548c6 | ||
|  | b361bbcbcb | ||
|  | 02ceae3e22 | ||
|  | e7f68f24a3 | ||
|  | 3208bf16c5 | ||
|  | ccaa90d392 | ||
|  | fd71527762 | ||
|  | a210ee4301 | ||
|  | d3ec6b02c3 | ||
|  | aa3d08745a | ||
|  | bf1dfe3d6d | ||
|  | d867348640 | ||
|  | 7fb26eb707 | ||
|  | f723b64486 | ||
|  | 7b94e76589 | ||
|  | 68d57ea7cb | ||
|  | c6879ac254 | ||
|  | 59e1e8637c | ||
|  | 6256ad908f | ||
|  | d783fe475e | ||
|  | 77d7d402c3 | ||
|  | b69b9248f9 | ||
|  | e91fb514fe | ||
|  | 07b3034bc0 | ||
|  | a734e7f73f | ||
|  | 010498edce | ||
|  | 87492c2abe | ||
|  | 15e603063a | ||
|  | 028817765e | ||
|  | 7b14690664 | ||
|  | 5647f009fb | ||
|  | 31e8d08da3 | 
							
								
								
									
										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@v26" | ||||||
|         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,48 @@ 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; | ||||||
|  |       }; | ||||||
|  |     }; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   # 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; | ||||||
|   | |||||||
							
								
								
									
										252
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										252
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							| @@ -7,11 +7,11 @@ | |||||||
|         "rust-overlay": "rust-overlay" |         "rust-overlay": "rust-overlay" | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1705557527, |         "lastModified": 1712279577, | ||||||
|         "narHash": "sha256-DuxxHTQ/W5KToFLWG4FUF8hLldNo9eXlbt7JgvhrMnY=", |         "narHash": "sha256-Bwn4rmQi2L2iX6g3ycQMA4baE3zgPHAO0xPBpr2T4/k=", | ||||||
|         "owner": "tpwrules", |         "owner": "tpwrules", | ||||||
|         "repo": "nixos-apple-silicon", |         "repo": "nixos-apple-silicon", | ||||||
|         "rev": "6e324ab06cb27a19409ebc1dc2664bf1e585490a", |         "rev": "d47afc3f0f8b3078c818da8609c41340af61a2ec", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -27,11 +27,11 @@ | |||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1705915768, |         "lastModified": 1711763326, | ||||||
|         "narHash": "sha256-+Jlz8OAqkOwJlioac9wtpsCnjgGYUhvLpgJR/5tP9po=", |         "narHash": "sha256-sXcesZWKXFlEQ8oyGHnfk4xc9f2Ip0X/+YZOq3sKviI=", | ||||||
|         "owner": "lnl7", |         "owner": "lnl7", | ||||||
|         "repo": "nix-darwin", |         "repo": "nix-darwin", | ||||||
|         "rev": "1e706ef323de76236eb183d7784f3bd57255ec0b", |         "rev": "36524adc31566655f2f4d55ad6b875fb5c1a4083", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -50,11 +50,11 @@ | |||||||
|         "nixpkgs-stable": "nixpkgs-stable" |         "nixpkgs-stable": "nixpkgs-stable" | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1706170797, |         "lastModified": 1712941527, | ||||||
|         "narHash": "sha256-oGuFylWYU9OY5DaEJEK+Z7EL81Ln27xz01LN9+8U0P0=", |         "narHash": "sha256-wD9XQFGW0qzRW1YHj6oklCHzgKNxjwS0tZ/hFGgiHX4=", | ||||||
|         "owner": "nix-community", |         "owner": "nix-community", | ||||||
|         "repo": "emacs-overlay", |         "repo": "emacs-overlay", | ||||||
|         "rev": "dd5d758f69dd1ae6d0399763aa73ca34974ce9e3", |         "rev": "9f4406718ada7af83892e17355ef7fd202c20897", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -78,16 +78,74 @@ | |||||||
|         "type": "github" |         "type": "github" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "flake-compat_2": { | ||||||
|  |       "flake": false, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1696426674, | ||||||
|  |         "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", | ||||||
|  |         "owner": "edolstra", | ||||||
|  |         "repo": "flake-compat", | ||||||
|  |         "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "edolstra", | ||||||
|  |         "repo": "flake-compat", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "flake-parts": { | ||||||
|  |       "inputs": { | ||||||
|  |         "nixpkgs-lib": [ | ||||||
|  |           "neovim-nightly", | ||||||
|  |           "nixpkgs" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1712014858, | ||||||
|  |         "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", | ||||||
|  |         "owner": "hercules-ci", | ||||||
|  |         "repo": "flake-parts", | ||||||
|  |         "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "hercules-ci", | ||||||
|  |         "repo": "flake-parts", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "flake-parts_2": { | ||||||
|  |       "inputs": { | ||||||
|  |         "nixpkgs-lib": [ | ||||||
|  |           "neovim-nightly", | ||||||
|  |           "hercules-ci-effects", | ||||||
|  |           "nixpkgs" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1709336216, | ||||||
|  |         "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", | ||||||
|  |         "owner": "hercules-ci", | ||||||
|  |         "repo": "flake-parts", | ||||||
|  |         "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "id": "flake-parts", | ||||||
|  |         "type": "indirect" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "flake-utils": { |     "flake-utils": { | ||||||
|       "inputs": { |       "inputs": { | ||||||
|         "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": { | ||||||
| @@ -114,6 +172,46 @@ | |||||||
|         "type": "github" |         "type": "github" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "flake-utils_3": { | ||||||
|  |       "inputs": { | ||||||
|  |         "systems": "systems_3" | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1701680307, | ||||||
|  |         "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", | ||||||
|  |         "owner": "numtide", | ||||||
|  |         "repo": "flake-utils", | ||||||
|  |         "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "numtide", | ||||||
|  |         "repo": "flake-utils", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "hercules-ci-effects": { | ||||||
|  |       "inputs": { | ||||||
|  |         "flake-parts": "flake-parts_2", | ||||||
|  |         "nixpkgs": [ | ||||||
|  |           "neovim-nightly", | ||||||
|  |           "nixpkgs" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1710478346, | ||||||
|  |         "narHash": "sha256-Xjf8BdnQG0tLhPMlqQdwCIjOp7Teox0DP3N/jjyiGM4=", | ||||||
|  |         "owner": "hercules-ci", | ||||||
|  |         "repo": "hercules-ci-effects", | ||||||
|  |         "rev": "64e7763d72c1e4c1e5e6472640615b6ae2d40fbf", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "hercules-ci", | ||||||
|  |         "repo": "hercules-ci-effects", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "home-manager": { |     "home-manager": { | ||||||
|       "inputs": { |       "inputs": { | ||||||
|         "nixpkgs": [ |         "nixpkgs": [ | ||||||
| @@ -121,11 +219,11 @@ | |||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1706134977, |         "lastModified": 1712759992, | ||||||
|         "narHash": "sha256-KwNb1Li3K6vuVwZ77tFjZ89AWBo7AiCs9t0Cens4BsM=", |         "narHash": "sha256-2APpO3ZW4idlgtlb8hB04u/rmIcKA8O7pYqxF66xbNY=", | ||||||
|         "owner": "nix-community", |         "owner": "nix-community", | ||||||
|         "repo": "home-manager", |         "repo": "home-manager", | ||||||
|         "rev": "6359d40f6ec0b72a38e02b333f343c3d4929ec10", |         "rev": "31357486b0ef6f4e161e002b6893eeb4fafc3ca9", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -146,29 +244,77 @@ | |||||||
|         "url": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin?download=true" |         "url": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin?download=true" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "neovim-flake": { | ||||||
|  |       "inputs": { | ||||||
|  |         "flake-utils": "flake-utils_2", | ||||||
|  |         "nixpkgs": [ | ||||||
|  |           "neovim-nightly", | ||||||
|  |           "nixpkgs" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "dir": "contrib", | ||||||
|  |         "lastModified": 1712877603, | ||||||
|  |         "narHash": "sha256-8JesAgnsv1bD+xHNoqefz0Gv243wSiCKnzh4rhZLopU=", | ||||||
|  |         "owner": "neovim", | ||||||
|  |         "repo": "neovim", | ||||||
|  |         "rev": "18ee9f9e7dbbc9709ee9c1572870b4ad31443569", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "dir": "contrib", | ||||||
|  |         "owner": "neovim", | ||||||
|  |         "repo": "neovim", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "neovim-nightly": { | ||||||
|  |       "inputs": { | ||||||
|  |         "flake-compat": "flake-compat_2", | ||||||
|  |         "flake-parts": "flake-parts", | ||||||
|  |         "hercules-ci-effects": "hercules-ci-effects", | ||||||
|  |         "neovim-flake": "neovim-flake", | ||||||
|  |         "nixpkgs": [ | ||||||
|  |           "nixpkgs" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1712880226, | ||||||
|  |         "narHash": "sha256-2CGLzsFft8zF/gEY4qDN0uAjRCWUqvNJ9yV118NlzTg=", | ||||||
|  |         "owner": "nix-community", | ||||||
|  |         "repo": "neovim-nightly-overlay", | ||||||
|  |         "rev": "58d367a1924bf0d02bcc5bd2c5af8ac97f178381", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "nix-community", | ||||||
|  |         "repo": "neovim-nightly-overlay", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "nixpkgs": { |     "nixpkgs": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1705316053, |         "lastModified": 1712163089, | ||||||
|         "narHash": "sha256-J2Ey5mPFT8gdfL2XC0JTZvKaBw/b2pnyudEXFvl+dQM=", |         "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370", |         "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370", |         "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "nixpkgs-stable": { |     "nixpkgs-stable": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1705916986, |         "lastModified": 1712741485, | ||||||
|         "narHash": "sha256-iBpfltu6QvN4xMpen6jGGEb6jOqmmVQKUrXdOJ32u8w=", |         "narHash": "sha256-bCs0+MSTra80oXAsnM6Oq62WsirOIaijQ/BbUY59tR4=", | ||||||
|         "owner": "NixOS", |         "owner": "NixOS", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "d7f206b723e42edb09d9d753020a84b3061a79d8", |         "rev": "b2cf36f43f9ef2ded5711b30b1f393ac423d8f72", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -178,29 +324,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": 1712849433, | ||||||
|         "narHash": "sha256-nDPz0fj0IFcDhSTlXBU2aixcnGs2Jm4Zcuoj0QtmiXQ=", |         "narHash": "sha256-flQtf/ZPJgkLY/So3Fd+dGilw2DKIsiwgMEn7BbBHL0=", | ||||||
|         "owner": "NixOS", |         "owner": "NixOS", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "b43bb235efeab5324c5e486882ef46749188eee2", |         "rev": "f173d0881eff3b21ebb29a2ef8bedbc106c86ea5", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -231,8 +361,8 @@ | |||||||
|         "darwin": "darwin", |         "darwin": "darwin", | ||||||
|         "emacs": "emacs", |         "emacs": "emacs", | ||||||
|         "home-manager": "home-manager", |         "home-manager": "home-manager", | ||||||
|  |         "neovim-nightly": "neovim-nightly", | ||||||
|         "nixpkgs": "nixpkgs_2", |         "nixpkgs": "nixpkgs_2", | ||||||
|         "sops-nix": "sops-nix", |  | ||||||
|         "whisper": "whisper" |         "whisper": "whisper" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
| @@ -252,27 +382,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, | ||||||
| @@ -303,9 +412,24 @@ | |||||||
|         "type": "github" |         "type": "github" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "systems_3": { | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1681028828, | ||||||
|  |         "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", | ||||||
|  |         "owner": "nix-systems", | ||||||
|  |         "repo": "default", | ||||||
|  |         "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "nix-systems", | ||||||
|  |         "repo": "default", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "whisper": { |     "whisper": { | ||||||
|       "inputs": { |       "inputs": { | ||||||
|         "flake-utils": "flake-utils_2", |         "flake-utils": "flake-utils_3", | ||||||
|         "model": "model", |         "model": "model", | ||||||
|         "nixpkgs": "nixpkgs_3" |         "nixpkgs": "nixpkgs_3" | ||||||
|       }, |       }, | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								flake.nix
									
									
									
									
									
								
							| @@ -18,36 +18,35 @@ | |||||||
|       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"; | ||||||
|     }; |     }; | ||||||
|     whisper = { |     whisper = { | ||||||
|       url = "github:Smaug123/whisper.cpp/nix"; |       url = "github:Smaug123/whisper.cpp/nix"; | ||||||
|     }; |     }; | ||||||
|  |     neovim-nightly = { | ||||||
|  |       url = "github:nix-community/neovim-nightly-overlay"; | ||||||
|  |       inputs.nixpkgs.follows = "nixpkgs"; | ||||||
|  |     }; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   outputs = { |   outputs = { | ||||||
|     self, |     neovim-nightly, | ||||||
|     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 neovim-nightly.overlay]; | ||||||
|     recursiveMerge = attrList: let |     recursiveMerge = attrList: let | ||||||
|       f = attrPath: |       f = attrPath: | ||||||
|         builtins.zipAttrsWith (n: values: |         builtins.zipAttrsWith (n: values: | ||||||
| @@ -62,6 +61,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 +98,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 +108,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 +127,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 +148,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 +171,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,112 +0,0 @@ | |||||||
| { |  | ||||||
|   nixpkgs, |  | ||||||
|   username, |  | ||||||
|   dotnet, |  | ||||||
|   ... |  | ||||||
| }: { |  | ||||||
|   home.packages = [nixpkgs.firefox-wayland nixpkgs.wl-clipboard]; |  | ||||||
|   nixpkgs.config.firefox = { |  | ||||||
|     speechSynthesisSupport = true; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   # Sadly not implemented on Darwin |  | ||||||
|   programs.firefox = { |  | ||||||
|     enable = true; |  | ||||||
|     profiles = { |  | ||||||
|       patrick = { |  | ||||||
|         isDefault = true; |  | ||||||
|         name = "patrick"; |  | ||||||
|         search = {default = "Google";}; |  | ||||||
|         settings = { |  | ||||||
|           # see https://github.com/TLATER/dotfiles/blob/b39af91fbd13d338559a05d69f56c5a97f8c905d/home-config/config/graphical-applications/firefox.nix |  | ||||||
|           # see https://www.ghacks.net/2015/08/18/a-comprehensive-list-of-firefox-privacy-and-security-settings/ |  | ||||||
|           "browser.search.isUS" = false; |  | ||||||
|           "browser.search.region" = "GB"; |  | ||||||
|           "gfx.webrender.all" = true; # enable GPU acceleration |  | ||||||
|           "media.ffmpeg.vaapi.enabled" = true; |  | ||||||
|           "widget.dmabuf.force-enabled" = true; |  | ||||||
|           "privacy.webrtc.legacyGlobalIndicator" = false; |  | ||||||
|           "app.shield.optoutstudies.enabled" = false; |  | ||||||
|           "app.update.enabled" = false; |  | ||||||
|           "app.update.auto" = false; |  | ||||||
|           "app.update.silent" = false; |  | ||||||
|           "app.update.service.enabled" = false; |  | ||||||
|           "app.update.staging.enabled" = false; |  | ||||||
|           "browser.discovery.enabled" = false; |  | ||||||
|           "browser.laterrun.enabled" = false; |  | ||||||
|           "browser.shell.checkDefaultBrowser" = false; |  | ||||||
|           "browser.rights.3.shown" = true; |  | ||||||
|           "browser.search.update" = false; |  | ||||||
|           "extensions.update.enabled" = false; |  | ||||||
|           "extensions.update.autoUpdateDefault" = false; |  | ||||||
|           "extensions.getAddons.cache.enabled" = false; |  | ||||||
|           "dom.ipc.plugins.reportCrashURL" = false; |  | ||||||
|           "extensions.webservice.discoverURL" = "http://127.0.0.1"; |  | ||||||
|           "toolkit.telemetry.unified" = false; |  | ||||||
|           "toolkit.telemetry.unifiedIsOptIn" = true; |  | ||||||
|           "toolkit.telemetry.enabled" = false; |  | ||||||
|           "toolkit.telemetry.server" = ""; |  | ||||||
|           "toolkit.telemetry.archive.enabled" = false; |  | ||||||
|           "lightweightThemes.update.enabled" = false; |  | ||||||
|           "startup.homepage_welcome_url" = ""; |  | ||||||
|           "startup.homepage_welcome_url.additional" = ""; |  | ||||||
|           "startup.homepage_override_url" = ""; |  | ||||||
|           "datareporting.healthreport.uploadEnabled" = false; |  | ||||||
|           "datareporting.healthreport.documentServerURI" = ""; |  | ||||||
|           "datareporting.healthreport.service.enabled" = false; |  | ||||||
|           "datareporting.healthreport.about.reportUrl" = "data:text/plain,"; |  | ||||||
|           "toolkit.telemetry.cachedClientID" = ""; |  | ||||||
|           "browser.selfsupport.url" = ""; |  | ||||||
|           "browser.selfsupport.enabled" = false; |  | ||||||
|           "experiments.enabled" = false; |  | ||||||
|           "experiments.supported" = false; |  | ||||||
|           "experiments.activeExperiment" = false; |  | ||||||
|           "experiments.manifest.uri" = ""; |  | ||||||
|           "network.allow-experiments" = false; |  | ||||||
|           "breakpad.reportURL" = ""; |  | ||||||
|           "browser.tabs.crashReporting.sendReport" = false; |  | ||||||
|           "browser.newtab.preload" = false; |  | ||||||
|           "browser.newtabpage.directory.ping" = "data:text/plain,"; |  | ||||||
|           "browser.newtabpage.directory.source" = "data:text/plain,"; |  | ||||||
|           "browser.newtabpage.enabled" = false; |  | ||||||
|           "browser.newtabpage.enhanced" = false; |  | ||||||
|           "browser.newtabpage.introShown" = true; |  | ||||||
|           "browser.aboutHomeSnippets.updateUrl" = "https://127.0.0.1"; |  | ||||||
|           "extensions.pocket.enabled" = false; |  | ||||||
|           "extensions.pocket.api" = ""; |  | ||||||
|           "extensions.pocket.site" = ""; |  | ||||||
|           "extensions.pocket.oAuthConsumerKey" = ""; |  | ||||||
|           "social.whitelist" = ""; |  | ||||||
|           "social.toast-notifications.enabled" = false; |  | ||||||
|           "social.shareDirectory" = ""; |  | ||||||
|           "social.remote-install.enabled" = false; |  | ||||||
|           "social.directories" = ""; |  | ||||||
|           "social.share.activationPanelEnabled" = false; |  | ||||||
|           "social.enabled" = false; |  | ||||||
|           "dom.flyweb.enabled" = false; |  | ||||||
|           "services.sync.enabled" = false; |  | ||||||
|         }; |  | ||||||
|       }; |  | ||||||
|     }; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   wayland.windowManager.sway = { |  | ||||||
|     enable = true; |  | ||||||
|     config = { |  | ||||||
|       focus = {followMouse = false;}; |  | ||||||
|       modifier = "Mod4"; |  | ||||||
|       terminal = "alacritty"; |  | ||||||
|       window = {border = 5;}; |  | ||||||
|     }; |  | ||||||
|     extraConfig = '' |  | ||||||
|       output Unknown-1 scale 2 |  | ||||||
|     ''; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   services.swayidle = {enable = true;}; |  | ||||||
|   services.cbatticon = { |  | ||||||
|     lowLevelPercent = 20; |  | ||||||
|     iconType = "standard"; |  | ||||||
|     enable = true; |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -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. | ||||||
| @@ -23,60 +25,40 @@ | |||||||
|  |  | ||||||
|   fonts.fontconfig.enable = true; |   fonts.fontconfig.enable = true; | ||||||
|  |  | ||||||
|   programs.tmux = { |   imports = [ | ||||||
|     shell = "${nixpkgs.zsh}/bin/zsh"; |     # ./modules/agda.nix | ||||||
|     escapeTime = 50; |     # ./modules/emacs.nix | ||||||
|     mouse = false; |     ./modules/direnv.nix | ||||||
|     prefix = "C-b"; |     ./modules/tmux.nix | ||||||
|     enable = true; |     ./modules/zsh.nix | ||||||
|     terminal = "screen-256color"; |     ./modules/ripgrep.nix | ||||||
|     extraConfig = '' |     ./modules/alacritty.nix | ||||||
|       set-option -sa terminal-features ',xterm-256color:RGB' |     ./modules/rust.nix | ||||||
|     ''; |     (import ./modules/mail.nix | ||||||
|   }; |       { | ||||||
|  |         inherit mbsync secretsPath; | ||||||
|   programs.zsh = { |         pkgs = nixpkgs; | ||||||
|     enable = true; |       }) | ||||||
|     autocd = true; |   ]; | ||||||
|     enableAutosuggestions = true; |  | ||||||
|     enableCompletion = true; |  | ||||||
|     history = { |  | ||||||
|       expireDuplicatesFirst = true; |  | ||||||
|     }; |  | ||||||
|     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"; |  | ||||||
|     }; |  | ||||||
|     initExtra = builtins.readFile ./.zshrc; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   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;}; |     delta = {enable = true;}; | ||||||
|     extraConfig = { |     extraConfig = { | ||||||
|  |       commit.gpgsign = true; | ||||||
|  |       gpg.program = "${nixpkgs.gnupg}/bin/gpg"; | ||||||
|  |       user.signingkey = "7C97D679CF3BC4F9"; | ||||||
|       core = { |       core = { | ||||||
|         autocrlf = "input"; |         autocrlf = "input"; | ||||||
|       }; |       }; | ||||||
| @@ -138,89 +120,164 @@ | |||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   services.syncthing = { | ||||||
|  |     enable = true; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   programs.neovim = let |   programs.neovim = let | ||||||
|  |     pynvimpp = nixpkgs.python3.pkgs.buildPythonPackage { | ||||||
|  |       pname = "pynvim-pp"; | ||||||
|  |       version = "unstable-2024-03-24"; | ||||||
|  |       pyproject = true; | ||||||
|  |  | ||||||
|  |       src = nixpkgs.fetchFromGitHub { | ||||||
|  |         owner = "ms-jpq"; | ||||||
|  |         repo = "pynvim_pp"; | ||||||
|  |         rev = "34e3a027c595981886d7efd1c91071f3eaa4715d"; | ||||||
|  |         hash = "sha256-2+jDRJXlg9q4MN9vOhmeq4cWVJ0wp5r5xAh3G8lqgOg="; | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       nativeBuildInputs = [nixpkgs.python3.pkgs.setuptools]; | ||||||
|  |  | ||||||
|  |       propagatedBuildInputs = [nixpkgs.python3.pkgs.pynvim]; | ||||||
|  |     }; | ||||||
|  |   in let | ||||||
|     pythonEnv = nixpkgs.python3.withPackages (ps: [ |     pythonEnv = nixpkgs.python3.withPackages (ps: [ | ||||||
|       ps.pynvim |       ps.pynvim | ||||||
|       ps.pynvim-pp |       pynvimpp | ||||||
|       ps.pyyaml |       ps.pyyaml | ||||||
|       ps.std2 |       ps.std2 | ||||||
|     ]); |     ]); | ||||||
|  |     debugPyEnv = nixpkgs.python3.withPackages (ps: [ps.debugpy]); | ||||||
|   in { |   in { | ||||||
|     enable = true; |     enable = true; | ||||||
|     plugins = [ |     plugins = [ | ||||||
|       nixpkgs.vimPlugins.molokai |       { | ||||||
|  |         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.tagbar | ||||||
|       nixpkgs.vimPlugins.fzf-vim |       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; |         plugin = nixpkgs.vimPlugins.Ionide-vim; | ||||||
|         config = '' |         type = "lua"; | ||||||
|           let g:fsharp#fsautocomplete_command = ['dotnet', 'fsautocomplete', '--background-service-enabled'] |         config = builtins.readFile ./nvim/ionide-vim.lua; | ||||||
|           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 |  | ||||||
|         ''; |  | ||||||
|       } |       } | ||||||
|       { |       { | ||||||
|         plugin = nixpkgs.vimPlugins.chadtree; |         plugin = nixpkgs.vimPlugins.chadtree; | ||||||
|         config = "let g:chadtree_settings = {'xdg': v:true}"; |         config = builtins.readFile ./nvim/chadtree.lua; | ||||||
|  |         type = "lua"; | ||||||
|       } |       } | ||||||
|       { |       { | ||||||
|         plugin = nixpkgs.vimPlugins.coq_nvim; |         plugin = nixpkgs.vimPlugins.coq_nvim; | ||||||
|         config = ''let g:coq_settings = { 'auto_start': v:true, 'xdg': v:true }''; |         config = ''let g:coq_settings = { 'auto_start': 'shut-up', 'xdg': v:true }''; | ||||||
|       } |       } | ||||||
|       { |       { | ||||||
|         plugin = nixpkgs.vimPlugins.rust-vim; |         plugin = nixpkgs.vimPlugins.rustaceanvim; | ||||||
|         config = "let g:rustfmt_autosave = 1"; |  | ||||||
|       } |       } | ||||||
|       { |       { | ||||||
|         plugin = nixpkgs.vimPlugins.LanguageClient-neovim; |         plugin = nixpkgs.vimPlugins.LanguageClient-neovim; | ||||||
|         config = "let g:LanguageClient_serverCommands = { 'nix': ['rnix-lsp'] }"; |  | ||||||
|       } |       } | ||||||
|       { |       { | ||||||
|         plugin = nixpkgs.vimPlugins.syntastic; |         plugin = nixpkgs.vimPlugins.nvim-dap; | ||||||
|         config = ''          let g:syntastic_rust_checkers = ['cargo'] |         config = builtins.readFile ./nvim/nvim-dap.lua; | ||||||
|           let g:syntastic_always_populate_loc_list = 1 |         type = "lua"; | ||||||
|           let g:syntastic_auto_loc_list = 1 |       } | ||||||
|           let g:syntastic_check_on_open = 1 |       { | ||||||
|           let g:syntastic_check_on_wq = 0''; |         plugin = nixpkgs.vimPlugins.nvim-dap-python; | ||||||
|  |         config = builtins.replaceStrings ["%PYTHONENV%"] ["${debugPyEnv}"] (builtins.readFile ./nvim/nvim-dap-python.lua); | ||||||
|  |         type = "lua"; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       nixpkgs.vimPlugins.tagbar |  | ||||||
|     ]; |     ]; | ||||||
|     viAlias = true; |     viAlias = true; | ||||||
|     vimAlias = true; |     vimAlias = true; | ||||||
|     vimdiffAlias = true; |     vimdiffAlias = true; | ||||||
|     withPython3 = true; |     withPython3 = true; | ||||||
|  |     withRuby = true; | ||||||
|  |  | ||||||
|     extraLuaConfig = ''vim.g.python3_host_prog="${pythonEnv}/bin/python"''; |     extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua) + "\n" + builtins.readFile ./nvim/python.lua; | ||||||
|     extraConfig = builtins.readFile ./init.vim; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   programs.direnv = { |     package = nixpkgs.neovim-nightly; | ||||||
|     enable = true; |  | ||||||
|     enableZshIntegration = true; |  | ||||||
|     nix-direnv.enable = true; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   programs.alacritty = { |  | ||||||
|     enable = true; |  | ||||||
|     settings = { |  | ||||||
|       font = { |  | ||||||
|         normal = { |  | ||||||
|           family = "FiraCode Nerd Font Mono"; |  | ||||||
|         }; |  | ||||||
|       }; |  | ||||||
|     }; |  | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   home.packages = [ |   home.packages = [ | ||||||
|  |     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 | ||||||
| @@ -231,54 +288,29 @@ | |||||||
|     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.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) |     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; |  | ||||||
|  |  | ||||||
|   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; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,351 +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> |  | ||||||
|  |  | ||||||
| " 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 |  | ||||||
							
								
								
									
										24
									
								
								home-manager/linux.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								home-manager/linux.nix
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | {nixpkgs, ...}: { | ||||||
|  |   home.packages = [nixpkgs.firefox-wayland]; | ||||||
|  |   nixpkgs.config.firefox.speechSynthesisSupport = true; | ||||||
|  |  | ||||||
|  |   wayland.windowManager.sway = { | ||||||
|  |     enable = true; | ||||||
|  |     config = { | ||||||
|  |       focus = {followMouse = false;}; | ||||||
|  |       modifier = "Mod4"; | ||||||
|  |       terminal = "alacritty"; | ||||||
|  |       window = {border = 5;}; | ||||||
|  |     }; | ||||||
|  |     extraConfig = '' | ||||||
|  |       output Unknown-1 scale 2 | ||||||
|  |     ''; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   services.swayidle = {enable = true;}; | ||||||
|  |   services.cbatticon = { | ||||||
|  |     lowLevelPercent = 20; | ||||||
|  |     iconType = "standard"; | ||||||
|  |     enable = true; | ||||||
|  |   }; | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								home-manager/modules/agda.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								home-manager/modules/agda.nix
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | {pkgs, ...}: { | ||||||
|  |   imports = [./emacs.nix]; | ||||||
|  |  | ||||||
|  |   home.packages = [ | ||||||
|  |     pkgs.agda | ||||||
|  |   ]; | ||||||
|  | } | ||||||
							
								
								
									
										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) | ||||||
|  |     ''; | ||||||
|  |   }; | ||||||
|  | } | ||||||
							
								
								
									
										184
									
								
								home-manager/modules/mail.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								home-manager/modules/mail.nix
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | |||||||
|  | { | ||||||
|  |   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 = "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. | ||||||
|  |       "cat ${secretsPath}/proton.txt"; | ||||||
|  |     smtp = { | ||||||
|  |       host = "127.0.0.1"; | ||||||
|  |       port = 1025; # 8126; if using hydroxide | ||||||
|  |       tls = {enable = false;}; | ||||||
|  |     }; | ||||||
|  |     userName = address; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   services.mbsync = { | ||||||
|  |     enable = pkgs.stdenv.isLinux; | ||||||
|  |     package = mbsync; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   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; | ||||||
|  | } | ||||||
| @@ -21,3 +21,12 @@ prompt_custom() { | |||||||
| RPROMPT='%F{155}%~%f' | RPROMPT='%F{155}%~%f' | ||||||
| 
 | 
 | ||||||
| precmd_functions+=(prompt_custom) | 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, | ||||||
|  | }) | ||||||
							
								
								
									
										290
									
								
								home-manager/nvim/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								home-manager/nvim/init.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,290 @@ | |||||||
|  | vim.g.python3_host_prog = "%PYTHONENV%/bin/python" | ||||||
|  | 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") }) | ||||||
							
								
								
									
										256
									
								
								home-manager/nvim/ionide-vim.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								home-manager/nvim/ionide-vim.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | |||||||
|  | vim.g["fsharp#fsautocomplete_command"] = { "fsautocomplete" } | ||||||
|  | vim.g["fsharp#show_signature_on_cursor_move"] = 1 | ||||||
|  | vim.g["fsharp#fsi_keymap"] = "none" | ||||||
|  |  | ||||||
|  | -- MASSIVE HACK - raised https://github.com/ionide/Ionide-vim/pull/78 | ||||||
|  | local function captureLoadedProjects() | ||||||
|  | 	vim.fn.execute("redir => g:massive_hack_patrick_capture") | ||||||
|  | 	vim.fn.execute("call fsharp#showLoadedProjects()") | ||||||
|  | 	vim.fn.execute("redir END") | ||||||
|  | 	local output = vim.fn.eval("g:massive_hack_patrick_capture") | ||||||
|  |  | ||||||
|  | 	local projects = {} | ||||||
|  |  | ||||||
|  | 	for line in output:gmatch("[^\r\n]+") do | ||||||
|  | 		local project = line:gsub("^%s*-%s*", "") | ||||||
|  | 		table.insert(projects, project) | ||||||
|  | 	end | ||||||
|  |  | ||||||
|  | 	return projects | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- 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 = captureLoadedProjects() | ||||||
|  | 	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 = captureLoadedProjects(), | ||||||
|  | 				}), | ||||||
|  | 				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 = captureLoadedProjects(), | ||||||
|  | 				}), | ||||||
|  | 				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 = captureLoadedProjects(), | ||||||
|  | 				}), | ||||||
|  | 				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 [ | ||||||
|     { |     { | ||||||
| @@ -83,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