mirror of
				https://github.com/Smaug123/nix-dotfiles
				synced 2025-10-31 02:48:59 +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" | ||||
|         uses: "actions/checkout@v4" | ||||
|       - 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 }}" } | ||||
|       - name: "Check flake" | ||||
|         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) | ||||
|  | ||||
| Copyright (c) 2016 Amir Salihefendic | ||||
| Copyright (c) 2024 Patrick Stevens | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| {pkgs, ...}: let | ||||
|   python = import ./python.nix {inherit pkgs;}; | ||||
|   mbsync = import ./mbsync.nix {inherit pkgs;}; | ||||
| in { | ||||
|   nix.useDaemon = true; | ||||
|  | ||||
| @@ -11,7 +11,7 @@ in { | ||||
|     pkgs.rustup | ||||
|     pkgs.libiconv | ||||
|     pkgs.clang | ||||
|     python | ||||
|     pkgs.python3 | ||||
|   ]; | ||||
|  | ||||
|   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. | ||||
|   programs.zsh.enable = true; | ||||
|   programs.gnupg.agent.enable = true; | ||||
|  | ||||
|   # Use a custom configuration.nix location. | ||||
|   # $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix | ||||
|   environment.darwinConfig = "$HOME/.nixpkgs/darwin-configuration.nix"; | ||||
|  | ||||
|   launchd.agents = { | ||||
|     mbsync-btinternet = { | ||||
|       command = "${mbsync}/bin/mbsync BTInternet > /tmp/mbsync.btinternet.log 2>/tmp/mbsync.btinternet.2.log"; | ||||
|       serviceConfig = { | ||||
|         KeepAlive = false; | ||||
|         UserName = "patrick"; | ||||
|         StartInterval = 60; | ||||
|         RunAtLoad = true; | ||||
|       }; | ||||
|     }; | ||||
|  | ||||
|     mbsync-proton = { | ||||
|       command = "${mbsync}/bin/mbsync Proton > /tmp/mbsync.proton.1.log 2>/tmp/mbsync.proton.2.log"; | ||||
|       serviceConfig = { | ||||
|         KeepAlive = false; | ||||
|         UserName = "patrick"; | ||||
|         StartInterval = 60; | ||||
|         RunAtLoad = true; | ||||
|       }; | ||||
|     }; | ||||
|  | ||||
|     mbsync-gmail = { | ||||
|       command = "${mbsync}/bin/mbsync Gmail > /tmp/mbsync.gmail.1.log 2>/tmp/mbsync.gmail.2.log"; | ||||
|       serviceConfig = { | ||||
|         KeepAlive = false; | ||||
|         UserName = "patrick"; | ||||
|         StartInterval = 60; | ||||
|         RunAtLoad = true; | ||||
|       }; | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   # Auto upgrade nix package and the daemon service. | ||||
|   services.nix-daemon.enable = true; | ||||
|   nix.package = pkgs.nixVersions.stable; | ||||
|   nix.gc.automatic = true; | ||||
|   nix.nixPath = ["darwin=/nix/store/zq4v3pi2wsfsrjkpk71kcn8srhbwjabf-nix-darwin"]; | ||||
|  | ||||
|   # Sandbox causes failure: https://github.com/NixOS/nix/issues/4119 | ||||
|   nix.settings.sandbox = false; | ||||
|   | ||||
							
								
								
									
										252
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										252
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							| @@ -7,11 +7,11 @@ | ||||
|         "rust-overlay": "rust-overlay" | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1705557527, | ||||
|         "narHash": "sha256-DuxxHTQ/W5KToFLWG4FUF8hLldNo9eXlbt7JgvhrMnY=", | ||||
|         "lastModified": 1712279577, | ||||
|         "narHash": "sha256-Bwn4rmQi2L2iX6g3ycQMA4baE3zgPHAO0xPBpr2T4/k=", | ||||
|         "owner": "tpwrules", | ||||
|         "repo": "nixos-apple-silicon", | ||||
|         "rev": "6e324ab06cb27a19409ebc1dc2664bf1e585490a", | ||||
|         "rev": "d47afc3f0f8b3078c818da8609c41340af61a2ec", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -27,11 +27,11 @@ | ||||
|         ] | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1705915768, | ||||
|         "narHash": "sha256-+Jlz8OAqkOwJlioac9wtpsCnjgGYUhvLpgJR/5tP9po=", | ||||
|         "lastModified": 1711763326, | ||||
|         "narHash": "sha256-sXcesZWKXFlEQ8oyGHnfk4xc9f2Ip0X/+YZOq3sKviI=", | ||||
|         "owner": "lnl7", | ||||
|         "repo": "nix-darwin", | ||||
|         "rev": "1e706ef323de76236eb183d7784f3bd57255ec0b", | ||||
|         "rev": "36524adc31566655f2f4d55ad6b875fb5c1a4083", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -50,11 +50,11 @@ | ||||
|         "nixpkgs-stable": "nixpkgs-stable" | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1706170797, | ||||
|         "narHash": "sha256-oGuFylWYU9OY5DaEJEK+Z7EL81Ln27xz01LN9+8U0P0=", | ||||
|         "lastModified": 1712941527, | ||||
|         "narHash": "sha256-wD9XQFGW0qzRW1YHj6oklCHzgKNxjwS0tZ/hFGgiHX4=", | ||||
|         "owner": "nix-community", | ||||
|         "repo": "emacs-overlay", | ||||
|         "rev": "dd5d758f69dd1ae6d0399763aa73ca34974ce9e3", | ||||
|         "rev": "9f4406718ada7af83892e17355ef7fd202c20897", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -78,16 +78,74 @@ | ||||
|         "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": { | ||||
|       "inputs": { | ||||
|         "systems": "systems" | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1705309234, | ||||
|         "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", | ||||
|         "lastModified": 1710146030, | ||||
|         "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", | ||||
|         "owner": "numtide", | ||||
|         "repo": "flake-utils", | ||||
|         "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", | ||||
|         "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -114,6 +172,46 @@ | ||||
|         "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": { | ||||
|       "inputs": { | ||||
|         "nixpkgs": [ | ||||
| @@ -121,11 +219,11 @@ | ||||
|         ] | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1706134977, | ||||
|         "narHash": "sha256-KwNb1Li3K6vuVwZ77tFjZ89AWBo7AiCs9t0Cens4BsM=", | ||||
|         "lastModified": 1712759992, | ||||
|         "narHash": "sha256-2APpO3ZW4idlgtlb8hB04u/rmIcKA8O7pYqxF66xbNY=", | ||||
|         "owner": "nix-community", | ||||
|         "repo": "home-manager", | ||||
|         "rev": "6359d40f6ec0b72a38e02b333f343c3d4929ec10", | ||||
|         "rev": "31357486b0ef6f4e161e002b6893eeb4fafc3ca9", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -146,29 +244,77 @@ | ||||
|         "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": { | ||||
|       "locked": { | ||||
|         "lastModified": 1705316053, | ||||
|         "narHash": "sha256-J2Ey5mPFT8gdfL2XC0JTZvKaBw/b2pnyudEXFvl+dQM=", | ||||
|         "lastModified": 1712163089, | ||||
|         "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", | ||||
|         "owner": "nixos", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370", | ||||
|         "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "nixos", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370", | ||||
|         "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "nixpkgs-stable": { | ||||
|       "locked": { | ||||
|         "lastModified": 1705916986, | ||||
|         "narHash": "sha256-iBpfltu6QvN4xMpen6jGGEb6jOqmmVQKUrXdOJ32u8w=", | ||||
|         "lastModified": 1712741485, | ||||
|         "narHash": "sha256-bCs0+MSTra80oXAsnM6Oq62WsirOIaijQ/BbUY59tR4=", | ||||
|         "owner": "NixOS", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "d7f206b723e42edb09d9d753020a84b3061a79d8", | ||||
|         "rev": "b2cf36f43f9ef2ded5711b30b1f393ac423d8f72", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -178,29 +324,13 @@ | ||||
|         "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": { | ||||
|       "locked": { | ||||
|         "lastModified": 1706006310, | ||||
|         "narHash": "sha256-nDPz0fj0IFcDhSTlXBU2aixcnGs2Jm4Zcuoj0QtmiXQ=", | ||||
|         "lastModified": 1712849433, | ||||
|         "narHash": "sha256-flQtf/ZPJgkLY/So3Fd+dGilw2DKIsiwgMEn7BbBHL0=", | ||||
|         "owner": "NixOS", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "b43bb235efeab5324c5e486882ef46749188eee2", | ||||
|         "rev": "f173d0881eff3b21ebb29a2ef8bedbc106c86ea5", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
| @@ -231,8 +361,8 @@ | ||||
|         "darwin": "darwin", | ||||
|         "emacs": "emacs", | ||||
|         "home-manager": "home-manager", | ||||
|         "neovim-nightly": "neovim-nightly", | ||||
|         "nixpkgs": "nixpkgs_2", | ||||
|         "sops-nix": "sops-nix", | ||||
|         "whisper": "whisper" | ||||
|       } | ||||
|     }, | ||||
| @@ -252,27 +382,6 @@ | ||||
|         "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": { | ||||
|       "locked": { | ||||
|         "lastModified": 1681028828, | ||||
| @@ -303,9 +412,24 @@ | ||||
|         "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": { | ||||
|       "inputs": { | ||||
|         "flake-utils": "flake-utils_2", | ||||
|         "flake-utils": "flake-utils_3", | ||||
|         "model": "model", | ||||
|         "nixpkgs": "nixpkgs_3" | ||||
|       }, | ||||
|   | ||||
							
								
								
									
										52
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								flake.nix
									
									
									
									
									
								
							| @@ -18,36 +18,35 @@ | ||||
|       url = "github:nix-community/emacs-overlay"; | ||||
|       inputs.nixpkgs.follows = "nixpkgs"; | ||||
|     }; | ||||
|     sops-nix = { | ||||
|       url = "github:Mic92/sops-nix"; | ||||
|       inputs.nixpkgs.follows = "nixpkgs"; | ||||
|     }; | ||||
|     apple-silicon = { | ||||
|       url = "github:tpwrules/nixos-apple-silicon"; | ||||
|     }; | ||||
|     whisper = { | ||||
|       url = "github:Smaug123/whisper.cpp/nix"; | ||||
|     }; | ||||
|     neovim-nightly = { | ||||
|       url = "github:nix-community/neovim-nightly-overlay"; | ||||
|       inputs.nixpkgs.follows = "nixpkgs"; | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   outputs = { | ||||
|     self, | ||||
|     neovim-nightly, | ||||
|     darwin, | ||||
|     emacs, | ||||
|     nixpkgs, | ||||
|     home-manager, | ||||
|     sops-nix, | ||||
|     apple-silicon, | ||||
|     whisper, | ||||
|     ... | ||||
|   } @ inputs: let | ||||
|   }: let | ||||
|     config = { | ||||
|       # contentAddressedByDefault = true; | ||||
|       allowUnfree = true; | ||||
|     }; | ||||
|     systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"]; | ||||
|   in let | ||||
|     overlays = [emacs.overlay] ++ import ./overlays.nix; | ||||
|     overlays = [emacs.overlay neovim-nightly.overlay]; | ||||
|     recursiveMerge = attrList: let | ||||
|       f = attrPath: | ||||
|         builtins.zipAttrsWith (n: values: | ||||
| @@ -62,6 +61,31 @@ | ||||
|       f [] attrList; | ||||
|   in { | ||||
|     nixosConfigurations = { | ||||
|       capybara = let | ||||
|         system = "x86_64-linux"; | ||||
|       in let | ||||
|         pkgs = import nixpkgs {inherit system config overlays;}; | ||||
|       in | ||||
|         nixpkgs.lib.nixosSystem { | ||||
|           inherit system; | ||||
|           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 | ||||
|         system = "aarch64-linux"; | ||||
|       in let | ||||
| @@ -74,6 +98,8 @@ | ||||
|               nixpkgs = pkgs; | ||||
|               username = "patrick"; | ||||
|               dotnet = pkgs.dotnet-sdk_8; | ||||
|               mbsync = import ./mbsync.nix {inherit pkgs;}; | ||||
|               secretsPath = "/home/patrick/.secrets/"; | ||||
|             }; | ||||
|           in [ | ||||
|             ./home-manager/earthworm-config.nix | ||||
| @@ -82,7 +108,7 @@ | ||||
|             { | ||||
|               home-manager.useGlobalPkgs = 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"; | ||||
|             dotnet = pkgs.dotnet-sdk_8; | ||||
|             whisper = whisper.packages.${system}; | ||||
|             mbsync = import ./mbsync.nix {inherit pkgs;}; | ||||
|             secretsPath = "/Users/patrick/.secrets/"; | ||||
|           }; | ||||
|         in [ | ||||
|           ./darwin-configuration.nix | ||||
|           sops-nix.nixosModules.sops | ||||
|           home-manager.darwinModules.home-manager | ||||
|           { | ||||
|             home-manager.useGlobalPkgs = true; | ||||
| @@ -121,11 +148,12 @@ | ||||
|         pkgs.stdenvNoCC.mkDerivation { | ||||
|           name = "fmt-check"; | ||||
|           src = ./.; | ||||
|           nativeBuildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.shfmt]; | ||||
|           nativeBuildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.shfmt pkgs.stylua]; | ||||
|           checkPhase = '' | ||||
|             find . -type f -name '*.sh' | xargs shfmt -d -s -i 2 -ci | ||||
|             alejandra -c . | ||||
|             find . -type f -name '*.sh' -exec shellcheck -x {} \; | ||||
|             find . -type f -name '*.lua' -exec stylua --check {} \; | ||||
|           ''; | ||||
|           installPhase = "mkdir $out"; | ||||
|           dontBuild = true; | ||||
| @@ -143,7 +171,7 @@ | ||||
|           pkgs = import nixpkgs {inherit config system;}; | ||||
|         in { | ||||
|           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 @@ | ||||
| { | ||||
|   config, | ||||
|   pkgs, | ||||
|   ... | ||||
| }: { | ||||
| {pkgs, ...}: { | ||||
|   imports = [ | ||||
|     ../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, | ||||
|   username, | ||||
|   mbsync, | ||||
|   dotnet, | ||||
|   secretsPath, | ||||
|   ... | ||||
| }: { | ||||
|   # Let Home Manager install and manage itself. | ||||
| @@ -23,60 +25,40 @@ | ||||
|  | ||||
|   fonts.fontconfig.enable = true; | ||||
|  | ||||
|   programs.tmux = { | ||||
|     shell = "${nixpkgs.zsh}/bin/zsh"; | ||||
|     escapeTime = 50; | ||||
|     mouse = false; | ||||
|     prefix = "C-b"; | ||||
|     enable = true; | ||||
|     terminal = "screen-256color"; | ||||
|     extraConfig = '' | ||||
|       set-option -sa terminal-features ',xterm-256color:RGB' | ||||
|     ''; | ||||
|   }; | ||||
|  | ||||
|   programs.zsh = { | ||||
|     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; | ||||
|   }; | ||||
|   imports = [ | ||||
|     # ./modules/agda.nix | ||||
|     # ./modules/emacs.nix | ||||
|     ./modules/direnv.nix | ||||
|     ./modules/tmux.nix | ||||
|     ./modules/zsh.nix | ||||
|     ./modules/ripgrep.nix | ||||
|     ./modules/alacritty.nix | ||||
|     ./modules/rust.nix | ||||
|     (import ./modules/mail.nix | ||||
|       { | ||||
|         inherit mbsync secretsPath; | ||||
|         pkgs = nixpkgs; | ||||
|       }) | ||||
|   ]; | ||||
|  | ||||
|   programs.fzf = { | ||||
|     enable = true; | ||||
|     enableZshIntegration = true; | ||||
|   }; | ||||
|  | ||||
|   programs.git = { | ||||
|     package = nixpkgs.gitAndTools.gitFull; | ||||
|     enable = true; | ||||
|     userName = "Smaug123"; | ||||
|     userEmail = "patrick+github@patrickstevens.co.uk"; | ||||
|     userEmail = "3138005+Smaug123@users.noreply.github.com"; | ||||
|     aliases = { | ||||
|       co = "checkout"; | ||||
|       st = "status"; | ||||
|     }; | ||||
|     delta = {enable = true;}; | ||||
|     extraConfig = { | ||||
|       commit.gpgsign = true; | ||||
|       gpg.program = "${nixpkgs.gnupg}/bin/gpg"; | ||||
|       user.signingkey = "7C97D679CF3BC4F9"; | ||||
|       core = { | ||||
|         autocrlf = "input"; | ||||
|       }; | ||||
| @@ -138,89 +120,164 @@ | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   services.syncthing = { | ||||
|     enable = true; | ||||
|   }; | ||||
|  | ||||
|   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: [ | ||||
|       ps.pynvim | ||||
|       ps.pynvim-pp | ||||
|       pynvimpp | ||||
|       ps.pyyaml | ||||
|       ps.std2 | ||||
|     ]); | ||||
|     debugPyEnv = nixpkgs.python3.withPackages (ps: [ps.debugpy]); | ||||
|   in { | ||||
|     enable = true; | ||||
|     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.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; | ||||
|         config = '' | ||||
|           let g:fsharp#fsautocomplete_command = ['dotnet', 'fsautocomplete', '--background-service-enabled'] | ||||
|           let g:fsharp#show_signature_on_cursor_move = 1 | ||||
|           if has('nvim') && exists('*nvim_open_win') | ||||
|             augroup FSharpGroup | ||||
|               autocmd! | ||||
|               autocmd FileType fsharp nnoremap <leader>t :call fsharp#showTooltip()<CR> | ||||
|             augroup END | ||||
|           endif | ||||
|         ''; | ||||
|         type = "lua"; | ||||
|         config = builtins.readFile ./nvim/ionide-vim.lua; | ||||
|       } | ||||
|       { | ||||
|         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; | ||||
|         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; | ||||
|         config = "let g:rustfmt_autosave = 1"; | ||||
|         plugin = nixpkgs.vimPlugins.rustaceanvim; | ||||
|       } | ||||
|       { | ||||
|         plugin = nixpkgs.vimPlugins.LanguageClient-neovim; | ||||
|         config = "let g:LanguageClient_serverCommands = { 'nix': ['rnix-lsp'] }"; | ||||
|       } | ||||
|       { | ||||
|         plugin = nixpkgs.vimPlugins.syntastic; | ||||
|         config = ''          let g:syntastic_rust_checkers = ['cargo'] | ||||
|           let g:syntastic_always_populate_loc_list = 1 | ||||
|           let g:syntastic_auto_loc_list = 1 | ||||
|           let g:syntastic_check_on_open = 1 | ||||
|           let g:syntastic_check_on_wq = 0''; | ||||
|         plugin = nixpkgs.vimPlugins.nvim-dap; | ||||
|         config = builtins.readFile ./nvim/nvim-dap.lua; | ||||
|         type = "lua"; | ||||
|       } | ||||
|       { | ||||
|         plugin = nixpkgs.vimPlugins.nvim-dap-python; | ||||
|         config = builtins.replaceStrings ["%PYTHONENV%"] ["${debugPyEnv}"] (builtins.readFile ./nvim/nvim-dap-python.lua); | ||||
|         type = "lua"; | ||||
|       } | ||||
|  | ||||
|       nixpkgs.vimPlugins.tagbar | ||||
|     ]; | ||||
|     viAlias = true; | ||||
|     vimAlias = true; | ||||
|     vimdiffAlias = true; | ||||
|     withPython3 = true; | ||||
|     withRuby = true; | ||||
|  | ||||
|     extraLuaConfig = ''vim.g.python3_host_prog="${pythonEnv}/bin/python"''; | ||||
|     extraConfig = builtins.readFile ./init.vim; | ||||
|   }; | ||||
|     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; | ||||
|  | ||||
|   programs.direnv = { | ||||
|     enable = true; | ||||
|     enableZshIntegration = true; | ||||
|     nix-direnv.enable = true; | ||||
|   }; | ||||
|  | ||||
|   programs.alacritty = { | ||||
|     enable = true; | ||||
|     settings = { | ||||
|       font = { | ||||
|         normal = { | ||||
|           family = "FiraCode Nerd Font Mono"; | ||||
|         }; | ||||
|       }; | ||||
|     }; | ||||
|     package = nixpkgs.neovim-nightly; | ||||
|   }; | ||||
|  | ||||
|   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.rust-analyzer | ||||
|     nixpkgs.tmux | ||||
|     nixpkgs.wget | ||||
|     nixpkgs.yt-dlp | ||||
|     nixpkgs.cmake | ||||
| @@ -231,54 +288,29 @@ | ||||
|     nixpkgs.hledger-web | ||||
|     dotnet | ||||
|     nixpkgs.jitsi-meet | ||||
|     nixpkgs.ripgrep | ||||
|     nixpkgs.elan | ||||
|     nixpkgs.coreutils-prefixed | ||||
|     nixpkgs.shellcheck | ||||
|     nixpkgs.html-tidy | ||||
|     nixpkgs.hugo | ||||
|     nixpkgs.agda | ||||
|     nixpkgs.pijul | ||||
|     nixpkgs.universal-ctags | ||||
|     nixpkgs.asciinema | ||||
|     nixpkgs.git-lfs | ||||
|     nixpkgs.imagemagick | ||||
|     nixpkgs.nixpkgs-fmt | ||||
|     nixpkgs.rnix-lsp | ||||
|     nixpkgs.grpc-tools | ||||
|     nixpkgs.element-desktop | ||||
|     nixpkgs.ihp-new | ||||
|     nixpkgs.direnv | ||||
|     nixpkgs.lnav | ||||
|     nixpkgs.age | ||||
|     nixpkgs.nodejs | ||||
|     nixpkgs.sqlitebrowser | ||||
|     nixpkgs.typst | ||||
|     nixpkgs.poetry | ||||
|     nixpkgs.nodePackages.pyright | ||||
|     nixpkgs.woodpecker-agent | ||||
|     nixpkgs.alacritty | ||||
|     nixpkgs.lynx | ||||
|     nixpkgs.alejandra | ||||
|     nixpkgs.ffmpeg | ||||
|     nixpkgs.bat | ||||
|     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.".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' | ||||
| 
 | ||||
| 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 | ||||
|     nvarner.typst-lsp | ||||
|     arrterian.nix-env-selector | ||||
|     # Doesn't build on arm64 | ||||
|     # vadimcn.vscode-lldb | ||||
|   ] | ||||
|   ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [ | ||||
|     { | ||||
| @@ -83,23 +85,3 @@ with pkgs.vscode-extensions; | ||||
|       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