From a3a8a5598aac93a79aaa01d10b9f6329ca9d70b2 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:11:31 +0000 Subject: [PATCH 01/42] Add some extensions (#30) --- home-manager/home.nix | 97 ++++++++++++++++++++---------- home-manager/init.vim | 14 ----- home-manager/vscode-extensions.nix | 18 ++---- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/home-manager/home.nix b/home-manager/home.nix index 8e93d09..1721e04 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -22,10 +22,15 @@ home.stateVersion = "22.05"; programs.tmux = { - shell = "\${nixpkgs.zsh}/bin/zsh"; + 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 = { @@ -135,38 +140,67 @@ }; }; - programs.neovim.enable = true; - programs.neovim.plugins = with nixpkgs.vimPlugins; [ - molokai - tagbar - fzf-vim - Ionide-vim - { - plugin = rust-vim; - config = "let g:rustfmt_autosave = 1"; - } - { - plugin = LanguageClient-neovim; - config = "let g:LanguageClient_serverCommands = { 'nix': ['rnix-lsp'] }"; - } - { - plugin = syntastic; - config = '' let g:syntastic_rust_checkers = ['cargo'] - let g:syntastic_always_populate_loc_list = 1 - let g:syntastic_auto_loc_list = 1 - let g:syntastic_check_on_open = 1 - let g:syntastic_check_on_wq = 0''; - } + programs.neovim = let + pythonEnv = nixpkgs.python3.withPackages (ps: [ + ps.pynvim + ps.pynvim-pp + ps.pyyaml + ps.std2 + ]); + in { + enable = true; + plugins = [ + nixpkgs.vimPlugins.molokai + nixpkgs.vimPlugins.tagbar + nixpkgs.vimPlugins.fzf-vim + { + 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 t :call fsharp#showTooltip() + augroup END + endif + ''; + } + { + plugin = nixpkgs.vimPlugins.chadtree; + config = "let g:chadtree_settings = {'xdg': v:true}"; + } + { + plugin = nixpkgs.vimPlugins.coq_nvim; + config = ''let g:coq_settings = { 'auto_start': v:true, 'xdg': v:true }''; + } + { + plugin = nixpkgs.vimPlugins.rust-vim; + config = "let g:rustfmt_autosave = 1"; + } + { + 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''; + } - # YouCompleteMe - tagbar - ]; - programs.neovim.viAlias = true; - programs.neovim.vimAlias = true; - programs.neovim.vimdiffAlias = true; - programs.neovim.withPython3 = true; + nixpkgs.vimPlugins.tagbar + ]; + viAlias = true; + vimAlias = true; + vimdiffAlias = true; + withPython3 = true; - programs.neovim.extraConfig = builtins.readFile ./init.vim; + extraLuaConfig = ''vim.g.python3_host_prog="${pythonEnv}/bin/python"''; + extraConfig = builtins.readFile ./init.vim; + }; programs.direnv = { enable = true; @@ -225,6 +259,7 @@ 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; diff --git a/home-manager/init.vim b/home-manager/init.vim index 3b8a818..e1f19bc 100644 --- a/home-manager/init.vim +++ b/home-manager/init.vim @@ -355,17 +355,3 @@ set statusline+=%* set statusline=\ %{HasPaste()}%F%m%r%h\ %w\ \ CWD:\ %r%{getcwd()}%h\ \ \ Line:\ %l\ \ Column:\ %c set fileformat=unix - -let g:fsharp#fsautocomplete_command = - \ [ 'dotnet', - \ 'fsautocomplete', - \ '--background-service-enabled' - \ ] - -let g:fsharp#show_signature_on_cursor_move = 1 -if has('nvim') && exists('*nvim_open_win') - augroup FSharpGroup - autocmd! - autocmd FileType fsharp nnoremap t :call fsharp#showTooltip() - augroup END -endif diff --git a/home-manager/vscode-extensions.nix b/home-manager/vscode-extensions.nix index 8be0fba..c026972 100644 --- a/home-manager/vscode-extensions.nix +++ b/home-manager/vscode-extensions.nix @@ -41,22 +41,16 @@ with pkgs.vscode-extensions; sha256 = "yk7buEyQIw6aiUizAm+sgalWxUibIuP9crhyBaOjC2E="; } { - name = "Ionide-Paket"; - publisher = "Ionide"; - version = "2.0.0"; - sha256 = "1455zx5p0d30b1agdi1zw22hj0d3zqqglw98ga8lj1l1d757gv6v"; - } - { - name = "lean"; - publisher = "jroesch"; - version = "0.16.58"; - sha256 = "sha256-e5+C6dAcpet4xOiifmTJ1vm2pNrcPhx/mjl70il5NG0="; + name = "ionide-fsharp"; + publisher = "ionide"; + version = "7.18.1"; + sha256 = "sha256-6NPMQncoZhZYtx5c+qzarjuSzUXMb5HdKCzcHPCFUhU="; } { name = "lean4"; publisher = "leanprover"; - version = "0.0.101"; - sha256 = "sha256-tHxP6X6qp3qVkkCn5TjhHrYHHvGGWJ4kYE7la6bPT6w="; + version = "0.0.128"; + sha256 = "sha256-odRDOrlDFahweLzoQtpufY8UUwAutPFunqg7atTxnPo="; } { name = "vscode-clang"; From ef6d3d4445fa2275a20e0ef84dd235e989affcb3 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 23 Feb 2024 14:18:18 +0000 Subject: [PATCH 02/42] FiraCode (#31) --- home-manager/home.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/home-manager/home.nix b/home-manager/home.nix index 1721e04..25dc658 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -21,6 +21,8 @@ # changes in each release. home.stateVersion = "22.05"; + fonts.fontconfig.enable = true; + programs.tmux = { shell = "${nixpkgs.zsh}/bin/zsh"; escapeTime = 50; @@ -208,6 +210,17 @@ nix-direnv.enable = true; }; + programs.alacritty = { + enable = true; + settings = { + font = { + normal = { + family = "FiraCode Nerd Font Mono"; + }; + }; + }; + }; + home.packages = [ nixpkgs.keepassxc nixpkgs.rust-analyzer @@ -253,6 +266,7 @@ nixpkgs.ffmpeg nixpkgs.bat nixpkgs.pandoc + (nixpkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) ]; home.file.".mailcap".source = ./mailcap; From 75c77e99c0e280400f6aa4a61b35dad96e273eff Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 23 Feb 2024 22:47:37 +0000 Subject: [PATCH 03/42] Rem oh-my-zsh (#32) --- home-manager/.zshrc | 23 +++++++++++++++++++++++ home-manager/home.nix | 6 +----- 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 home-manager/.zshrc diff --git a/home-manager/.zshrc b/home-manager/.zshrc new file mode 100644 index 0000000..c52a59a --- /dev/null +++ b/home-manager/.zshrc @@ -0,0 +1,23 @@ +prompt_custom() { + local cyan='%F{cyan}' + local red='%F{red}' + local blue='%F{blue}' + local reset_color='%f' + + local git_info=$(git symbolic-ref --short HEAD 2> /dev/null || git describe --tags --exact-match 2> /dev/null || git rev-parse --short HEAD 2> /dev/null) + if [[ -n "$git_info" ]]; then + # escape the percent character, which is the only zsh prompt metacharacter + git_info=$git_info:s/%/%%/ + git_info=" ${blue}git:${reset_color}${red}(${git_info})${reset_color}" + else + git_info="" + fi + + # %1 is the name of cwd + PROMPT="${cyan}%1~${reset_color}${git_info} > " +} + +# Full path to cwd, with `~` for any initial home component, in light green, +RPROMPT='%F{155}%~%f' + +precmd_functions+=(prompt_custom) diff --git a/home-manager/home.nix b/home-manager/home.nix index 25dc658..b039a36 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -43,11 +43,6 @@ history = { expireDuplicatesFirst = true; }; - oh-my-zsh = { - enable = true; - plugins = ["git" "macos" "dircycle" "timer"]; - theme = "robbyrussell"; - }; sessionVariables = { EDITOR = "vim"; LC_ALL = "en_US.UTF-8"; @@ -63,6 +58,7 @@ sessionVariables = { RIPGREP_CONFIG_PATH = "/Users/${username}/.config/ripgrep/config"; }; + initExtra = builtins.readFile ./.zshrc; }; programs.fzf = { From 31e8d08da3b73c55aeca04fb0b6187e4fb0d276b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 07:30:57 +0000 Subject: [PATCH 04/42] Bump cachix/install-nix-action from 25 to 26 (#33) --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index e77ea86..48483bf 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -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" From 5647f009fb9a5a6b5a0d5736efeb159185c31a16 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:49:39 +0000 Subject: [PATCH 05/42] Reduce dependence on oh-my-zsh (#34) --- home-manager/.zshrc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/home-manager/.zshrc b/home-manager/.zshrc index c52a59a..a89f648 100644 --- a/home-manager/.zshrc +++ b/home-manager/.zshrc @@ -21,3 +21,11 @@ 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 + +PATH="$PATH:$HOME/.cargo/bin" From 7b14690664e2469be1190159b97a021060800e3c Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:51:12 +0000 Subject: [PATCH 06/42] Remove vscode-lldb, it doesn't actually work (#35) --- home-manager/vscode-extensions.nix | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/home-manager/vscode-extensions.nix b/home-manager/vscode-extensions.nix index c026972..68c1396 100644 --- a/home-manager/vscode-extensions.nix +++ b/home-manager/vscode-extensions.nix @@ -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"; - }) - ] From 028817765eb7bddd493d62426fc5a674d548f3b3 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:55:45 +0000 Subject: [PATCH 07/42] Evil-mode (#36) --- home-manager/home.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/home-manager/home.nix b/home-manager/home.nix index b039a36..4732cca 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -273,10 +273,15 @@ programs.emacs = { enable = true; package = nixpkgs.emacs; - extraPackages = epkgs: []; + 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) + ;; Allow hash to be entered + (global set-key (kbd "M-3") '(lambda () (interactive) (insert "#"))) ''; }; From 15e603063ac9fcfb1107238240e6abfeaccc47f8 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Tue, 26 Mar 2024 00:00:04 +0000 Subject: [PATCH 08/42] Remove sops (#37) --- flake.lock | 38 -------------------------------------- flake.nix | 5 +---- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/flake.lock b/flake.lock index 6caa361..82efca9 100644 --- a/flake.lock +++ b/flake.lock @@ -178,22 +178,6 @@ "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, @@ -232,7 +216,6 @@ "emacs": "emacs", "home-manager": "home-manager", "nixpkgs": "nixpkgs_2", - "sops-nix": "sops-nix", "whisper": "whisper" } }, @@ -252,27 +235,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, diff --git a/flake.nix b/flake.nix index b7ae31e..85379b0 100644 --- a/flake.nix +++ b/flake.nix @@ -31,16 +31,14 @@ }; outputs = { - self, darwin, emacs, nixpkgs, home-manager, - sops-nix, apple-silicon, whisper, ... - } @ inputs: let + }: let config = { # contentAddressedByDefault = true; allowUnfree = true; @@ -104,7 +102,6 @@ }; in [ ./darwin-configuration.nix - sops-nix.nixosModules.sops home-manager.darwinModules.home-manager { home-manager.useGlobalPkgs = true; From 87492c2abea6211011a522bd123552edb5b7d65a Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Tue, 26 Mar 2024 00:04:01 +0000 Subject: [PATCH 09/42] Big neovim overhaul (#38) --- flake.lock | 214 +++++++++++++-- flake.nix | 11 +- home-manager/home.nix | 133 +++++++--- home-manager/init.vim | 357 -------------------------- home-manager/nvim/chadtree.vim | 4 + home-manager/nvim/init.lua | 256 ++++++++++++++++++ home-manager/nvim/ionide-vim.lua | 263 +++++++++++++++++++ home-manager/nvim/lspconfig.lua | 170 ++++++++++++ home-manager/nvim/nvim-dap-python.lua | 1 + home-manager/nvim/nvim-dap.lua | 99 +++++++ home-manager/nvim/roslyn-nvim.lua | 4 + home-manager/nvim/tokyonight.lua | 5 + home-manager/nvim/treesitter.lua | 9 + home-manager/nvim/venv-selector.lua | 199 ++++++++++++++ home-manager/nvim/which-key.lua | 88 +++++++ 15 files changed, 1395 insertions(+), 418 deletions(-) delete mode 100644 home-manager/init.vim create mode 100644 home-manager/nvim/chadtree.vim create mode 100644 home-manager/nvim/init.lua create mode 100644 home-manager/nvim/ionide-vim.lua create mode 100644 home-manager/nvim/lspconfig.lua create mode 100644 home-manager/nvim/nvim-dap-python.lua create mode 100644 home-manager/nvim/nvim-dap.lua create mode 100644 home-manager/nvim/roslyn-nvim.lua create mode 100644 home-manager/nvim/tokyonight.lua create mode 100644 home-manager/nvim/treesitter.lua create mode 100644 home-manager/nvim/venv-selector.lua create mode 100644 home-manager/nvim/which-key.lua diff --git a/flake.lock b/flake.lock index 82efca9..3db20e4 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1705557527, - "narHash": "sha256-DuxxHTQ/W5KToFLWG4FUF8hLldNo9eXlbt7JgvhrMnY=", + "lastModified": 1710209440, + "narHash": "sha256-1JwFo3u2aVrvpz12OotjCK51EQ0hEDI7xSG7CEvTSk8=", "owner": "tpwrules", "repo": "nixos-apple-silicon", - "rev": "6e324ab06cb27a19409ebc1dc2664bf1e585490a", + "rev": "bdc68b494d6a26c9457f4841ab1a6109b12a33e6", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1705915768, - "narHash": "sha256-+Jlz8OAqkOwJlioac9wtpsCnjgGYUhvLpgJR/5tP9po=", + "lastModified": 1710717205, + "narHash": "sha256-Wf3gHh5uV6W1TV/A8X8QJf99a5ypDSugY4sNtdJDe0A=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "1e706ef323de76236eb183d7784f3bd57255ec0b", + "rev": "bcc8afd06e237df060c85bad6af7128e05fd61a3", "type": "github" }, "original": { @@ -50,11 +50,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1706170797, - "narHash": "sha256-oGuFylWYU9OY5DaEJEK+Z7EL81Ln27xz01LN9+8U0P0=", + "lastModified": 1711271005, + "narHash": "sha256-JrhnnutZvHowEJFIrA/rQAFgGAc83WOx+BVy97teqKM=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "dd5d758f69dd1ae6d0399763aa73ca34974ce9e3", + "rev": "d6bbd32eb3e0f167f312e1031c1beee452dc9174", "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": 1709336216, + "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "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": 1711133180, + "narHash": "sha256-WJOahf+6115+GMl3wUfURu8fszuNeJLv9qAWFQl3Vmo=", "owner": "nix-community", "repo": "home-manager", - "rev": "6359d40f6ec0b72a38e02b333f343c3d4929ec10", + "rev": "1c2c5e4cabba4c43504ef0f8cc3f3dfa284e2dbb", "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": 1711323947, + "narHash": "sha256-Vc478rxwJkMuOcgBXm+brraWk9lbFqrGEdXVuST2l/A=", + "owner": "neovim", + "repo": "neovim", + "rev": "02d00cf3eed6681c6dde40585551c8243d7c003f", + "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": 1711325009, + "narHash": "sha256-c5OJdyuXYzTP+k+PN73X+0pvgXR1yYMYok+72x4SLVg=", + "owner": "nix-community", + "repo": "neovim-nightly-overlay", + "rev": "119bbc295f56b531cb87502f5d2fff13dcc35a35", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "neovim-nightly-overlay", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1705316053, - "narHash": "sha256-J2Ey5mPFT8gdfL2XC0JTZvKaBw/b2pnyudEXFvl+dQM=", + "lastModified": 1709961763, + "narHash": "sha256-6H95HGJHhEZtyYA3rIQpvamMKAGoa8Yh2rFV29QnuGw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370", + "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "c3e128f3c0ecc1fb04aef9f72b3dcc2f6cecf370", + "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1705916986, - "narHash": "sha256-iBpfltu6QvN4xMpen6jGGEb6jOqmmVQKUrXdOJ32u8w=", + "lastModified": 1711124224, + "narHash": "sha256-l0zlN/3CiodvWDtfBOVxeTwYSRz93muVbXWSpaMjXxM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d7f206b723e42edb09d9d753020a84b3061a79d8", + "rev": "56528ee42526794d413d6f244648aaee4a7b56c0", "type": "github" }, "original": { @@ -180,11 +326,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1706006310, - "narHash": "sha256-nDPz0fj0IFcDhSTlXBU2aixcnGs2Jm4Zcuoj0QtmiXQ=", + "lastModified": 1711231723, + "narHash": "sha256-dARJQ8AJOv6U+sdRePkbcVyVbXJTi1tReCrkkOeusiA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b43bb235efeab5324c5e486882ef46749188eee2", + "rev": "e1d501922fd7351da4200e1275dfcf5faaad1220", "type": "github" }, "original": { @@ -215,6 +361,7 @@ "darwin": "darwin", "emacs": "emacs", "home-manager": "home-manager", + "neovim-nightly": "neovim-nightly", "nixpkgs": "nixpkgs_2", "whisper": "whisper" } @@ -265,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" }, diff --git a/flake.nix b/flake.nix index 85379b0..ba6ba03 100644 --- a/flake.nix +++ b/flake.nix @@ -18,19 +18,20 @@ 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 = { + neovim-nightly, darwin, emacs, nixpkgs, @@ -45,7 +46,7 @@ }; systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"]; in let - overlays = [emacs.overlay] ++ import ./overlays.nix; + overlays = [emacs.overlay neovim-nightly.overlay] ++ import ./overlays.nix; recursiveMerge = attrList: let f = attrPath: builtins.zipAttrsWith (n: values: diff --git a/home-manager/home.nix b/home-manager/home.nix index 4732cca..b089597 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -38,7 +38,7 @@ programs.zsh = { enable = true; autocd = true; - enableAutosuggestions = true; + autosuggestion.enable = true; enableCompletion = true; history = { expireDuplicatesFirst = true; @@ -47,7 +47,7 @@ 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"; + RUSTFLAGS = "-L ${nixpkgs.libiconv}/lib -L ${nixpkgs.libcxx}/lib"; RUST_BACKTRACE = "full"; }; shellAliases = { @@ -139,65 +139,132 @@ }; 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.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 t :call fsharp#showTooltip() - 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.vim; } { 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; - extraLuaConfig = ''vim.g.python3_host_prog="${pythonEnv}/bin/python"''; - extraConfig = builtins.readFile ./init.vim; + extraLuaConfig = builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua); + + package = nixpkgs.neovim-nightly; }; programs.direnv = { @@ -218,6 +285,10 @@ }; home.packages = [ + nixpkgs.csharp-ls + nixpkgs.netcoredbg + nixpkgs.nil + nixpkgs.fsautocomplete nixpkgs.keepassxc nixpkgs.rust-analyzer nixpkgs.tmux @@ -244,7 +315,6 @@ nixpkgs.git-lfs nixpkgs.imagemagick nixpkgs.nixpkgs-fmt - nixpkgs.rnix-lsp nixpkgs.grpc-tools nixpkgs.element-desktop nixpkgs.ihp-new @@ -252,6 +322,7 @@ nixpkgs.lnav nixpkgs.age nixpkgs.nodejs + nixpkgs.nodePackages.pyright nixpkgs.sqlitebrowser nixpkgs.typst nixpkgs.poetry @@ -262,6 +333,8 @@ nixpkgs.ffmpeg nixpkgs.bat nixpkgs.pandoc + nixpkgs.fd + nixpkgs.sumneko-lua-language-server (nixpkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) ]; diff --git a/home-manager/init.vim b/home-manager/init.vim deleted file mode 100644 index e1f19bc..0000000 --- a/home-manager/init.vim +++ /dev/null @@ -1,357 +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 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 to / (search) and Ctrl- to ? (backwards search) -map / -map ? - -" Disable highlight when is pressed -map :noh - -" Smart way to move between windows -map j -map k -map h -map l - -" Close the current buffer -map bd :Bclose:tabclosegT - -" Close all the buffers -map ba :bufdo bd - -map l :bnext -map h :bprevious - -" Useful mappings for managing tabs -map tn :tabnew -map to :tabonly -map tc :tabclose -map tm :tabmove -map t :tabnext - -" Let 'tl' toggle between this and the last accessed tab -let g:lasttab = 1 -nmap tl :exe "tabn ".g:lasttab -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 te :tabedit =expand("%:p:h")/ - -" Switch CWD to the directory of the open buffer -map cd :cd %:p:h:pwd - -" 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 ss :setlocal spell! - -" Shortcuts using -map sn ]s -map sp [s -map sa zg -map s? z= - - -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" => Misc -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Remove the Windows ^M - when the encodings gets messed up -noremap m mmHmt:%s///ge'tzt'm - -" Quickly open a buffer for scribble -map q :e ~/buffer - -" Quickly open a markdown buffer for scribble -map x :e ~/buffer.md - -" Toggle paste mode on and off -map pp :setlocal paste! - - -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" => 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 BufcloseCloseIt() -function! 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 c :!cargo clippy -nnoremap 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 diff --git a/home-manager/nvim/chadtree.vim b/home-manager/nvim/chadtree.vim new file mode 100644 index 0000000..f86b780 --- /dev/null +++ b/home-manager/nvim/chadtree.vim @@ -0,0 +1,4 @@ +let g:chadtree_settings = {'xdg': v:true} + +autocmd VimEnter * CHADopen --nofocus +autocmd bufenter * if (winnr("$") == 1 && &filetype == 'CHADtree') | q | endif diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua new file mode 100644 index 0000000..bc4f1a7 --- /dev/null +++ b/home-manager/nvim/init.lua @@ -0,0 +1,256 @@ +vim.g.python3_host_prog = "%PYTHONENV%/bin/python" +vim.opt.mouse = "" +vim.opt.history = 500 +vim.opt.background = "dark" + +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 CWD: %r%{getcwd()}%h Line: %l Column: %c" + +-------------------------------------------------------------- + +vim.api.nvim_set_keymap("n", ";", "", { noremap = true }) +vim.api.nvim_set_var("maplocalleader", ";") + +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("") + 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 status, whichkey = pcall(require, "which-key") +if status then + 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 + -- TODO: If a command is a prefix of an existing command, prepend its description to those commands' descriptions, and append a '...' to the parent's description. + 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", "", 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({ + -- TODO: this isn't working for the FSI ones - maybe we've moved to a different buffer by the time we ask for the keymap? + [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", + }, + ["cd"] = { + ChangeToCurrentDirectory, + "Switch CWD to the directory of the open buffer", + }, + ["ss"] = { + ToggleSpell, + "Toggle spell-checker on or off", + }, + }, { prefix = vim.api.nvim_get_var("maplocalleader") }) +else + vim.api.nvim_set_keymap("n", "mp", ":lua MarkdownPreview()", { noremap = true, silent = true }) + -- Remove the Windows ^M - when the encodings gets messed up + vim.api.nvim_set_keymap("n", "md", ":lua RemoveCarriageReturn()", { noremap = true }) + vim.api.nvim_set_keymap("n", "j", ":lua FormatJson()", { noremap = true }) + vim.api.nvim_set_keymap("n", "cd", ":lua ChangeToCurrentDirectory()", { noremap = true }) +end diff --git a/home-manager/nvim/ionide-vim.lua b/home-manager/nvim/ionide-vim.lua new file mode 100644 index 0000000..43fef16 --- /dev/null +++ b/home-manager/nvim/ionide-vim.lua @@ -0,0 +1,263 @@ +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_output(context, prefix, err, data) + 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.buf) + vim.api.nvim_buf_set_lines(context.buf, 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.buf) + vim.api.nvim_buf_set_lines( + context.buf, + 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.buf then + local new_line_count = vim.api.nvim_buf_line_count(context.buf) + vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 }) + end + end + -- 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) + end + end + + local function spawn_next(context) + if context.completed == context.expected then + if context.errs == 0 and context.warn == 0 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.buf then + vim.api.nvim_win_close(context.window, 1) + end + print("All builds successful") + end + else + local handle + local stdout = vim.loop.new_pipe(false) + local stderr = vim.loop.new_pipe(false) + + handle, _ = vim.loop.spawn( + "dotnet", + { + args = { "build", context.projects[context.completed + 1] }, + stdio = { nil, stdout, stderr }, + }, + vim.schedule_wrap(function(code, signal) + stdout:read_stop() + stderr:read_stop() + stdout:close() + stderr:close() + handle:close() + 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 + .. ")" + ) + + spawn_next(context) + end) + ) + + if not handle then + print("Failed to start build process.") + return + end + + vim.loop.read_start(stdout, function(err, data) + on_output(context, "OUT", err, data) + end) + vim.loop.read_start(stderr, function(err, data) + on_output(context, "ERR", err, data) + end) + 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 + + -- 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("columns") + local height = vim.api.nvim_get_option("lines") + local win_height = math.min(10, math.floor(height * 0.2)) -- 20% of total height or 10 lines + local original_win = vim.api.nvim_get_current_win() + 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, true, win_opts) + -- Switch back to the original window + vim.api.nvim_set_current_win(original_win) + + local build_context = { + warn = 0, + errs = 0, + completed = 0, + expected = total_projects, + window = win, + projects = projects, + buf = buf, + } + + spawn_next(build_context) + end +end + +-- local function fsprojAndDirCompletion(ArgLead, _, _) +-- local results = {} +-- local loc = ArgLead +-- if not loc then +-- loc = "." +-- end +-- local command = string.format( +-- "find " +-- .. vim.fn.shellescape(loc) +-- .. " -maxdepth 2 \\( -type f -name '*.fsproj' -o -type d \\) -print0 2> /dev/null" +-- ) +-- local handle = io.popen(command) +-- if handle then +-- local stdout = handle:read("*all") +-- handle:close() +-- +-- local allResults = {} +-- for match in string.gmatch(stdout, "([^%z]+)") do +-- table.insert(allResults, match) +-- end +-- table.sort(allResults, function(a, b) +-- local aEndsWithProj = a:match("proj$") +-- local bEndsWithProj = b:match("proj$") +-- if aEndsWithProj and not bEndsWithProj then +-- return true +-- elseif not aEndsWithProj and bEndsWithProj then +-- return false +-- else +-- return a < b -- If both or neither end with 'proj', sort alphabetically +-- end +-- end) +-- +-- for _, line in ipairs(allResults) do +-- table.insert(results, line) +-- end +-- end +-- return results +-- 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 = "Actions", + 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" }) + +vim.api.nvim_create_autocmd("FileType", { + pattern = "fsharp", + callback = function() + local status, whichkey = pcall(require, "which-key") + if status then + whichkey.register({ + f = { + t = { ":call fsharp#showTooltip()", "Show F# Tooltip" }, + ["si"] = { ":call fsharp#toggleFsi()", "Toggle FSI (F# Interactive)" }, + ["sl"] = { ":call fsharp#sendLineToFsi()", "Send line to FSI (F# Interactive)" }, + }, + b = { + p = { + 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", "ft", ":call fsharp#showTooltip()", { noremap = true }) + vim.api.nvim_set_keymap("n", "fsi", ":call fsharp#toggleFsi()", { noremap = true }) + vim.api.nvim_set_keymap("n", "fsl", ":call fsharp#sendLineToFsi()", { noremap = true }) + vim.api.nvim_set_keymap("n", "bpa", BuildFSharpProjects, { noremap = true }) + vim.api.nvim_set_keymap("n", "bps", ":BuildFSharpProject", { noremap = true }) + end + end, +}) diff --git a/home-manager/nvim/lspconfig.lua b/home-manager/nvim/lspconfig.lua new file mode 100644 index 0000000..e8f1275 --- /dev/null +++ b/home-manager/nvim/lspconfig.lua @@ -0,0 +1,170 @@ +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({}) + +require("lspconfig")["lua_ls"].setup({ + on_init = function(client) + local path = client.workspace_folders[1].name + if vim.loop.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("maplocalleader") }) + else + vim.keymap.set("n", "lp", vim.diagnostic.goto_prev) + vim.keymap.set("n", "ln", vim.diagnostic.goto_next) + vim.keymap.set("n", "ll", ToggleLocList) + vim.keymap.set("n", "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_status, whichkey = pcall(require, "which-key") + -- Enable completion triggered by + 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 + local opts = { buffer = ev.buf } + if whichkey_status then + 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({ + [""] = { 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 = "" }) + else + vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts) + vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts) + vim.keymap.set("n", "K", vim.lsp.buf.hover, opts) + vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts) + vim.keymap.set("n", "", vim.lsp.buf.signature_help, opts) + vim.keymap.set("n", "wa", vim.lsp.buf.add_workspace_folder, opts) + vim.keymap.set("n", "wr", vim.lsp.buf.remove_workspace_folder, opts) + vim.keymap.set("n", "wl", function() + print(vim.inspect(vim.lsp.buf.list_workspace_folders())) + end, opts) + vim.keymap.set("n", "D", vim.lsp.buf.type_definition, opts) + vim.keymap.set("n", "rn", vim.lsp.buf.rename, opts) + vim.keymap.set({ "n", "v" }, "ca", vim.lsp.buf.code_action, opts) + vim.keymap.set("n", "gr", function() + require("telescope.builtin").lsp_references() + end, opts) + vim.keymap.set("n", "f", function() + vim.lsp.buf.format({ async = true }) + end, opts) + end + end, +}) diff --git a/home-manager/nvim/nvim-dap-python.lua b/home-manager/nvim/nvim-dap-python.lua new file mode 100644 index 0000000..cd88bac --- /dev/null +++ b/home-manager/nvim/nvim-dap-python.lua @@ -0,0 +1 @@ +require("dap-python").setup("%PYTHONENV%/bin/python") diff --git a/home-manager/nvim/nvim-dap.lua b/home-manager/nvim/nvim-dap.lua new file mode 100644 index 0000000..e4ab02e --- /dev/null +++ b/home-manager/nvim/nvim-dap.lua @@ -0,0 +1,99 @@ +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 status, whichkey = pcall(require, "which-key") + if status then + 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") }) + else + vim.api.nvim_set_keymap("n", "do", ":lua require('dap').step_over()", { noremap = true }) + vim.api.nvim_set_keymap("n", "di", ":lua require('dap').step_into()", { noremap = true }) + vim.api.nvim_set_keymap("n", "dc", ":lua require('dap').continue()", { noremap = true }) + vim.api.nvim_set_keymap("n", "dC", ":lua require('dap').run_last()", { noremap = true }) + vim.api.nvim_set_keymap( + "n", + "db", + ":lua require('dap').toggle_breakpoint()", + { noremap = true } + ) + vim.api.nvim_set_keymap("n", "dr", ":lua require('dap').repl.open()", { noremap = true }) + vim.api.nvim_set_keymap( + "n", + "dvv", + ":lua require('dap.ui.widgets').hover()", + { noremap = true } + ) + vim.api.nvim_set_keymap( + "n", + "dvs", + ":lua require('dap.ui.widgets').sidebar(require('dap.ui.widgets').scopes).open()", + { noremap = true } + ) + vim.api.nvim_set_keymap( + "n", + "dvf", + ":lua require('dap.ui.widgets').sidebar(require('dap.ui.widgets').frames).open()", + { noremap = true } + ) + vim.api.nvim_set_keymap("n", "dt", ":lua require('dap').terminate()", { noremap = true }) + end +end diff --git a/home-manager/nvim/roslyn-nvim.lua b/home-manager/nvim/roslyn-nvim.lua new file mode 100644 index 0000000..7a78a9f --- /dev/null +++ b/home-manager/nvim/roslyn-nvim.lua @@ -0,0 +1,4 @@ +require("roslyn").setup({ + on_attach = function(_, _) end, + capabilities = vim.lsp.protocol.make_client_capabilities(), +}) diff --git a/home-manager/nvim/tokyonight.lua b/home-manager/nvim/tokyonight.lua new file mode 100644 index 0000000..229ff32 --- /dev/null +++ b/home-manager/nvim/tokyonight.lua @@ -0,0 +1,5 @@ +require("tokyonight").setup({ + style = "night", +}) + +vim.cmd([[colorscheme tokyonight]]) diff --git a/home-manager/nvim/treesitter.lua b/home-manager/nvim/treesitter.lua new file mode 100644 index 0000000..c33e5af --- /dev/null +++ b/home-manager/nvim/treesitter.lua @@ -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, + }, +}) diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua new file mode 100644 index 0000000..84f6cd7 --- /dev/null +++ b/home-manager/nvim/venv-selector.lua @@ -0,0 +1,199 @@ +local venv_selector = require("venv-selector") + +venv_selector.setup({ + changed_venv_hooks = { venv_selector.hooks.pyright }, + name = { "venv", ".venv" }, +}) + +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 handle + local stdout = vim.loop.new_pipe(false) + local stderr = vim.loop.new_pipe(false) + + local function on_output(context, prefix, err, data) + 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.buf) + vim.api.nvim_buf_set_lines( + context.buf, + 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.buf) + vim.api.nvim_buf_set_lines( + context.buf, + 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.buf then + local new_line_count = vim.api.nvim_buf_line_count(context.buf) + vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 }) + end + end + end) + end + end + + -- TODO: commonise wth what's in ionide-vim + + -- 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("columns") + local height = vim.api.nvim_get_option("lines") + local win_height = math.min(10, math.floor(height * 0.2)) -- 20% of total height or 10 lines + local original_win = vim.api.nvim_get_current_win() + 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, true, win_opts) + -- Switch back to the original window + vim.api.nvim_set_current_win(original_win) + + local context = { + window = win, + buf = buf, + } + + handle, _ = vim.loop.spawn( + -- TODO: do we need to escape this? Don't know whether spawn goes via a shell + venv_dir .. "/bin/python", + { + -- TODO: and do we need to escape this? + args = { "-m", "pip", "install", "-r", requirements_path }, + stdio = { nil, stdout, stderr }, + }, + vim.schedule_wrap(function(code, signal) + stdout:read_stop() + stderr:read_stop() + stdout:close() + stderr:close() + handle:close() + print("Venv creation completed, exit code " .. code .. " and signal " .. signal) + load_venv(venv_dir) + end) + ) + + if not handle then + print("Failed to start venv install process.") + return + end + + vim.loop.read_start(stdout, function(err, data) + on_output(context, "OUT", err, data) + end) + vim.loop.read_start(stderr, function(err, data) + on_output(context, "ERR", err, data) + end) + else + load_venv(venv_dir) + end +end + +do + local status, whichkey = pcall(require, "which-key") + if status then + whichkey.register({ + p = { + name = "Python-related commands", + v = { + name = "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"), buffer = vim.api.nvim_get_current_buf() }) + else + vim.api.nvim_set_keymap("n", "pvc", ":lua CreateVenv()", { noremap = true }) + vim.api.nvim_set_keymap("n", "pvl", ":lua SelectVenv()", { noremap = true }) + vim.api.nvim_set_keymap("n", "pvo", ":VenvSelect", { noremap = true }) + end +end diff --git a/home-manager/nvim/which-key.lua b/home-manager/nvim/which-key.lua new file mode 100644 index 0000000..c94e855 --- /dev/null +++ b/home-manager/nvim/which-key.lua @@ -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 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 + 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: + -- [""] = "SPC", + -- [""] = "RET", + -- [""] = "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 = "", -- binding to scroll down inside the popup + scroll_up = "", -- 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 = { "", "", "", "", "^:", "^ ", "^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 = {""} -- or specifiy a list manually + -- list of triggers, where WhichKey should not wait for timeoutlen and show immediately + triggers_nowait = { + -- marks + "`", + "'", + "g`", + "g'", + -- registers + '"', + "", + -- 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 = {}, + }, +}) From 010498edcea0dafd1a05df60c604acee7b06f6c8 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Tue, 26 Mar 2024 00:15:10 +0000 Subject: [PATCH 10/42] Also style-check Lua (#39) --- flake.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index ba6ba03..a4028ed 100644 --- a/flake.nix +++ b/flake.nix @@ -119,11 +119,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; @@ -141,7 +142,7 @@ pkgs = import nixpkgs {inherit config system;}; in { default = pkgs.mkShell { - buildInputs = [pkgs.alejandra pkgs.shellcheck]; + buildInputs = [pkgs.alejandra pkgs.shellcheck pkgs.stylua]; }; } ); From a734e7f73f7c23fe2278f748814e82687164bf8a Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Tue, 26 Mar 2024 00:27:53 +0000 Subject: [PATCH 11/42] Fix up neovim deprecations (#40) --- home-manager/nvim/init.lua | 1 - home-manager/nvim/ionide-vim.lua | 16 ++++++++-------- home-manager/nvim/lspconfig.lua | 2 +- home-manager/nvim/venv-selector.lua | 14 +++++++------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index bc4f1a7..b26a7be 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -225,7 +225,6 @@ if status then end whichkey.register({ - -- TODO: this isn't working for the FSI ones - maybe we've moved to a different buffer by the time we ask for the keymap? [vim.api.nvim_get_var("maplocalleader")] = { DisplayAllMappingsWithTelescope, "View all mappings", diff --git a/home-manager/nvim/ionide-vim.lua b/home-manager/nvim/ionide-vim.lua index 43fef16..ecd9067 100644 --- a/home-manager/nvim/ionide-vim.lua +++ b/home-manager/nvim/ionide-vim.lua @@ -64,16 +64,16 @@ local function BuildFSharpProjects(projects) local cur_win = vim.api.nvim_get_current_win() local cur_buf = vim.api.nvim_win_get_buf(cur_win) if cur_buf ~= context.buf then - vim.api.nvim_win_close(context.window, 1) + vim.api.nvim_win_close(context.window, true) end print("All builds successful") end else local handle - local stdout = vim.loop.new_pipe(false) - local stderr = vim.loop.new_pipe(false) + local stdout = vim.uv.new_pipe(false) + local stderr = vim.uv.new_pipe(false) - handle, _ = vim.loop.spawn( + handle, _ = vim.uv.spawn( "dotnet", { args = { "build", context.projects[context.completed + 1] }, @@ -112,10 +112,10 @@ local function BuildFSharpProjects(projects) return end - vim.loop.read_start(stdout, function(err, data) + vim.uv.read_start(stdout, function(err, data) on_output(context, "OUT", err, data) end) - vim.loop.read_start(stderr, function(err, data) + vim.uv.read_start(stderr, function(err, data) on_output(context, "ERR", err, data) end) end @@ -134,8 +134,8 @@ local function BuildFSharpProjects(projects) 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("columns") - local height = vim.api.nvim_get_option("lines") + 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 original_win = vim.api.nvim_get_current_win() local win_opts = { diff --git a/home-manager/nvim/lspconfig.lua b/home-manager/nvim/lspconfig.lua index e8f1275..c30821b 100644 --- a/home-manager/nvim/lspconfig.lua +++ b/home-manager/nvim/lspconfig.lua @@ -7,7 +7,7 @@ local coq = require("coq") require("lspconfig")["lua_ls"].setup({ on_init = function(client) local path = client.workspace_folders[1].name - if vim.loop.fs_stat(path .. "/.luarc.json") or vim.loop.fs_stat(path .. "/.luarc.jsonc") then + if vim.uv.fs_stat(path .. "/.luarc.json") or vim.loop.fs_stat(path .. "/.luarc.jsonc") then return end diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua index 84f6cd7..b8c8eb5 100644 --- a/home-manager/nvim/venv-selector.lua +++ b/home-manager/nvim/venv-selector.lua @@ -66,8 +66,8 @@ function CreateVenv() if requirements_path then print("Installing requirements from " .. requirements_path) local handle - local stdout = vim.loop.new_pipe(false) - local stderr = vim.loop.new_pipe(false) + local stdout = vim.uv.new_pipe(false) + local stderr = vim.uv.new_pipe(false) local function on_output(context, prefix, err, data) if err or data then @@ -114,8 +114,8 @@ function CreateVenv() 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("columns") - local height = vim.api.nvim_get_option("lines") + 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 original_win = vim.api.nvim_get_current_win() local win_opts = { @@ -137,7 +137,7 @@ function CreateVenv() buf = buf, } - handle, _ = vim.loop.spawn( + handle, _ = vim.uv.spawn( -- TODO: do we need to escape this? Don't know whether spawn goes via a shell venv_dir .. "/bin/python", { @@ -161,10 +161,10 @@ function CreateVenv() return end - vim.loop.read_start(stdout, function(err, data) + vim.uv.read_start(stdout, function(err, data) on_output(context, "OUT", err, data) end) - vim.loop.read_start(stderr, function(err, data) + vim.uv.read_start(stderr, function(err, data) on_output(context, "ERR", err, data) end) else From 07b3034bc0fa396508bf564db09cfcfcaf458913 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Tue, 26 Mar 2024 09:31:39 +0000 Subject: [PATCH 12/42] Commonise floating-window logic (#41) --- home-manager/home.nix | 2 +- home-manager/nvim/build-utils.lua | 110 +++++++++++++++++++ home-manager/nvim/ionide-vim.lua | 160 ++++++++-------------------- home-manager/nvim/venv-selector.lua | 107 ++----------------- 4 files changed, 163 insertions(+), 216 deletions(-) create mode 100644 home-manager/nvim/build-utils.lua diff --git a/home-manager/home.nix b/home-manager/home.nix index b089597..e5d2982 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -262,7 +262,7 @@ vimdiffAlias = true; withPython3 = true; - extraLuaConfig = builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua); + extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua); package = nixpkgs.neovim-nightly; }; diff --git a/home-manager/nvim/build-utils.lua b/home-manager/nvim/build-utils.lua new file mode 100644 index 0000000..767f641 --- /dev/null +++ b/home-manager/nvim/build-utils.lua @@ -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 diff --git a/home-manager/nvim/ionide-vim.lua b/home-manager/nvim/ionide-vim.lua index ecd9067..a6e436e 100644 --- a/home-manager/nvim/ionide-vim.lua +++ b/home-manager/nvim/ionide-vim.lua @@ -21,46 +21,48 @@ end -- Supply nil to get all loaded F# projects and build them. local function BuildFSharpProjects(projects) - local function on_output(context, prefix, err, data) - 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.buf) - vim.api.nvim_buf_set_lines(context.buf, 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.buf) - vim.api.nvim_buf_set_lines( - context.buf, - 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.buf then - local new_line_count = vim.api.nvim_buf_line_count(context.buf) - vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 }) - end - end - -- 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_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.buf then @@ -69,55 +71,7 @@ local function BuildFSharpProjects(projects) print("All builds successful") end else - local handle - local stdout = vim.uv.new_pipe(false) - local stderr = vim.uv.new_pipe(false) - - handle, _ = vim.uv.spawn( - "dotnet", - { - args = { "build", context.projects[context.completed + 1] }, - stdio = { nil, stdout, stderr }, - }, - vim.schedule_wrap(function(code, signal) - stdout:read_stop() - stderr:read_stop() - stdout:close() - stderr:close() - handle:close() - 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 - .. ")" - ) - - spawn_next(context) - end) - ) - - if not handle then - print("Failed to start build process.") - return - end - - vim.uv.read_start(stdout, function(err, data) - on_output(context, "OUT", err, data) - end) - vim.uv.read_start(stderr, function(err, data) - on_output(context, "ERR", err, data) - end) + spawn_next(context) end end @@ -129,40 +83,14 @@ local function BuildFSharpProjects(projects) 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 - -- 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 original_win = vim.api.nvim_get_current_win() - 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, true, win_opts) - -- Switch back to the original window - vim.api.nvim_set_current_win(original_win) - - local build_context = { - warn = 0, - errs = 0, - completed = 0, - expected = total_projects, - window = win, - projects = projects, - buf = buf, - } - - spawn_next(build_context) + spawn_next(context) end end diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua index b8c8eb5..ac3171e 100644 --- a/home-manager/nvim/venv-selector.lua +++ b/home-manager/nvim/venv-selector.lua @@ -65,108 +65,17 @@ function CreateVenv() -- Install requirements if requirements_path then print("Installing requirements from " .. requirements_path) - local handle - local stdout = vim.uv.new_pipe(false) - local stderr = vim.uv.new_pipe(false) - - local function on_output(context, prefix, err, data) - 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.buf) - vim.api.nvim_buf_set_lines( - context.buf, - 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.buf) - vim.api.nvim_buf_set_lines( - context.buf, - 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.buf then - local new_line_count = vim.api.nvim_buf_line_count(context.buf) - vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 }) - end - end - end) - end - end - - -- TODO: commonise wth what's in ionide-vim - - -- 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 original_win = vim.api.nvim_get_current_win() - 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, true, win_opts) - -- Switch back to the original window - vim.api.nvim_set_current_win(original_win) - - local context = { - window = win, - buf = buf, - } - - handle, _ = vim.uv.spawn( - -- TODO: do we need to escape this? Don't know whether spawn goes via a shell + local context = BuildUtils.create_window() + BuildUtils.run( venv_dir .. "/bin/python", - { - -- TODO: and do we need to escape this? - args = { "-m", "pip", "install", "-r", requirements_path }, - stdio = { nil, stdout, stderr }, - }, - vim.schedule_wrap(function(code, signal) - stdout:read_stop() - stderr:read_stop() - stdout:close() - stderr:close() - handle:close() - print("Venv creation completed, exit code " .. code .. " and signal " .. signal) + { "-m", "pip", "install", "-r", requirements_path }, + "venv creation", + context, + function(_, _, _) end, + function(_, _, _) load_venv(venv_dir) - end) + end ) - - if not handle then - print("Failed to start venv install process.") - return - end - - vim.uv.read_start(stdout, function(err, data) - on_output(context, "OUT", err, data) - end) - vim.uv.read_start(stderr, function(err, data) - on_output(context, "ERR", err, data) - end) else load_venv(venv_dir) end From e91fb514febbe8d8e92114e8b877f62c488974d5 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:33:10 +0000 Subject: [PATCH 13/42] More language servers (#42) --- home-manager/home.nix | 3 ++- home-manager/nvim/lspconfig.lua | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/home-manager/home.nix b/home-manager/home.nix index e5d2982..8bf9039 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -181,7 +181,6 @@ config = builtins.readFile ./nvim/treesitter.lua; type = "lua"; } - { plugin = nixpkgs.vimPlugins.nvim-lspconfig; config = builtins.readFile ./nvim/lspconfig.lua; @@ -285,6 +284,8 @@ }; home.packages = [ + nixpkgs.nodePackages_latest.vscode-json-languageserver + nixpkgs.yaml-language-server nixpkgs.csharp-ls nixpkgs.netcoredbg nixpkgs.nil diff --git a/home-manager/nvim/lspconfig.lua b/home-manager/nvim/lspconfig.lua index c30821b..a0f7cdb 100644 --- a/home-manager/nvim/lspconfig.lua +++ b/home-manager/nvim/lspconfig.lua @@ -3,6 +3,43 @@ 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")["lua_ls"].setup({ on_init = function(client) From b69b9248f910052e0f108844b3eaed4f58a32b05 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:30:31 +0000 Subject: [PATCH 14/42] More F# and .NET stuff (#43) --- home-manager/home.nix | 2 +- home-manager/nvim/dotnet.lua | 158 +++++++++++++++++++++++++++++++ home-manager/nvim/init.lua | 3 +- home-manager/nvim/ionide-vim.lua | 153 +++++++++++++++++++++--------- 4 files changed, 269 insertions(+), 47 deletions(-) create mode 100644 home-manager/nvim/dotnet.lua diff --git a/home-manager/home.nix b/home-manager/home.nix index 8bf9039..dd8b000 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -261,7 +261,7 @@ vimdiffAlias = true; withPython3 = true; - extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua); + extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua); package = nixpkgs.neovim-nightly; }; diff --git a/home-manager/nvim/dotnet.lua b/home-manager/nvim/dotnet.lua new file mode 100644 index 0000000..8a595ab --- /dev/null +++ b/home-manager/nvim/dotnet.lua @@ -0,0 +1,158 @@ +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 status, whichkey = pcall(require, "which-key") + if status then + 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() }) + else + vim.api.nvim_set_keymap("n", "sb", ":call BuildDotNetSolution", { noremap = true }) + end +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() + 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() + RegisterSolution(vim.fn.expand("%:p")) + end, +}) + +vim.api.nvim_create_autocmd("FileType", { + pattern = { "fsharp", "cs" }, + callback = function() + FindAndRegisterSolution() + end, +}) diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index b26a7be..d63f1ed 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -99,7 +99,7 @@ function HasPaste() return "" end -vim.o.statusline = vim.o.statusline .. "%{v:lua.HasPaste()}%F%m%r%h %w CWD: %r%{getcwd()}%h Line: %l Column: %c" +vim.o.statusline = vim.o.statusline .. "%{v:lua.HasPaste()}%F%m%r%h %w Line: %l Column: %c" -------------------------------------------------------------- @@ -186,7 +186,6 @@ if status then print("-----") end end - -- TODO: If a command is a prefix of an existing command, prepend its description to those commands' descriptions, and append a '...' to the parent's description. end end) end diff --git a/home-manager/nvim/ionide-vim.lua b/home-manager/nvim/ionide-vim.lua index a6e436e..1e33b51 100644 --- a/home-manager/nvim/ionide-vim.lua +++ b/home-manager/nvim/ionide-vim.lua @@ -65,7 +65,7 @@ local function BuildFSharpProjects(projects) -- 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.buf then + if cur_buf ~= context.buffer then vim.api.nvim_win_close(context.window, true) end print("All builds successful") @@ -94,45 +94,6 @@ local function BuildFSharpProjects(projects) end end --- local function fsprojAndDirCompletion(ArgLead, _, _) --- local results = {} --- local loc = ArgLead --- if not loc then --- loc = "." --- end --- local command = string.format( --- "find " --- .. vim.fn.shellescape(loc) --- .. " -maxdepth 2 \\( -type f -name '*.fsproj' -o -type d \\) -print0 2> /dev/null" --- ) --- local handle = io.popen(command) --- if handle then --- local stdout = handle:read("*all") --- handle:close() --- --- local allResults = {} --- for match in string.gmatch(stdout, "([^%z]+)") do --- table.insert(allResults, match) --- end --- table.sort(allResults, function(a, b) --- local aEndsWithProj = a:match("proj$") --- local bEndsWithProj = b:match("proj$") --- if aEndsWithProj and not bEndsWithProj then --- return true --- elseif not aEndsWithProj and bEndsWithProj then --- return false --- else --- return a < b -- If both or neither end with 'proj', sort alphabetically --- end --- end) --- --- for _, line in ipairs(allResults) do --- table.insert(results, line) --- end --- end --- return results --- end - vim.api.nvim_create_user_command("BuildFSharpProject", function(opts) if opts.fargs and opts.fargs[1] then BuildFSharpProjects(opts.fargs) @@ -144,7 +105,7 @@ vim.api.nvim_create_user_command("BuildFSharpProject", function(opts) local actions = require("telescope.actions") pickers .new({}, { - prompt_title = "Actions", + prompt_title = "Projects", finder = finders.new_table({ results = captureLoadedProjects(), }), @@ -162,6 +123,101 @@ vim.api.nvim_create_user_command("BuildFSharpProject", function(opts) 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() @@ -169,12 +225,21 @@ vim.api.nvim_create_autocmd("FileType", { if status then whichkey.register({ f = { + name = "F#", t = { ":call fsharp#showTooltip()", "Show F# Tooltip" }, ["si"] = { ":call fsharp#toggleFsi()", "Toggle FSI (F# Interactive)" }, ["sl"] = { ":call fsharp#sendLineToFsi()", "Send line to FSI (F# Interactive)" }, - }, - b = { + 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" }, }, @@ -184,7 +249,7 @@ vim.api.nvim_create_autocmd("FileType", { vim.api.nvim_set_keymap("n", "ft", ":call fsharp#showTooltip()", { noremap = true }) vim.api.nvim_set_keymap("n", "fsi", ":call fsharp#toggleFsi()", { noremap = true }) vim.api.nvim_set_keymap("n", "fsl", ":call fsharp#sendLineToFsi()", { noremap = true }) - vim.api.nvim_set_keymap("n", "bpa", BuildFSharpProjects, { noremap = true }) + vim.api.nvim_set_keymap("n", "bpa", ":lua BuildFSharpProjects()", { noremap = true }) vim.api.nvim_set_keymap("n", "bps", ":BuildFSharpProject", { noremap = true }) end end, From 77d7d402c3b13aaabead6d514f2bf51179b85015 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Wed, 27 Mar 2024 23:41:43 +0000 Subject: [PATCH 15/42] More languages, clean up keybinds (#44) --- home-manager/home.nix | 15 ++ home-manager/nvim/dotnet.lua | 32 ++-- home-manager/nvim/init.lua | 212 ++++++++++++++++----------- home-manager/nvim/lean.lua | 17 +++ home-manager/nvim/lspconfig.lua | 124 +++++++--------- home-manager/nvim/nvim-dap.lua | 95 ++++-------- home-manager/nvim/nvim-lightbulb.lua | 9 ++ home-manager/nvim/venv-selector.lua | 36 ++--- 8 files changed, 281 insertions(+), 259 deletions(-) create mode 100644 home-manager/nvim/lean.lua create mode 100644 home-manager/nvim/nvim-lightbulb.lua diff --git a/home-manager/home.nix b/home-manager/home.nix index dd8b000..7efd7e6 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -166,6 +166,16 @@ in { enable = true; plugins = [ + { + plugin = nixpkgs.vimPlugins.nvim-lightbulb; + type = "lua"; + config = builtins.readFile ./nvim/nvim-lightbulb.lua; + } + { + plugin = nixpkgs.vimPlugins.lean-nvim; + type = "lua"; + config = builtins.readFile ./nvim/lean.lua; + } { plugin = nixpkgs.vimPlugins.which-key-nvim; type = "lua"; @@ -284,7 +294,12 @@ }; home.packages = [ + 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 diff --git a/home-manager/nvim/dotnet.lua b/home-manager/nvim/dotnet.lua index 8a595ab..eb117b2 100644 --- a/home-manager/nvim/dotnet.lua +++ b/home-manager/nvim/dotnet.lua @@ -72,18 +72,14 @@ function RegisterSolution(sln_path) vim.o.statusline = vim.o.statusline .. " %{v:lua.CurrentSlnOrEmpty()}" end - local status, whichkey = pcall(require, "which-key") - if status then - 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() }) - else - vim.api.nvim_set_keymap("n", "sb", ":call BuildDotNetSolution", { noremap = true }) - 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() @@ -100,7 +96,11 @@ local function find_nearest_slns() return {} end -local function FindAndRegisterSolution() +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.") @@ -146,13 +146,15 @@ end vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, { pattern = "*.sln", callback = function() - RegisterSolution(vim.fn.expand("%:p")) + if GetCurrentSln() == nil then + RegisterSolution(vim.fn.expand("%:p")) + end end, }) vim.api.nvim_create_autocmd("FileType", { pattern = { "fsharp", "cs" }, callback = function() - FindAndRegisterSolution() + FindAndRegisterSolution(false) end, }) diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index d63f1ed..36a4a79 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -105,6 +105,7 @@ vim.o.statusline = vim.o.statusline .. "%{v:lua.HasPaste()}%F%m%r%h %w Line: %l vim.api.nvim_set_keymap("n", ";", "", { noremap = true }) vim.api.nvim_set_var("maplocalleader", ";") +vim.api.nvim_set_var("mapleader", " ") function MarkdownPreview() local temp_file = vim.fn.tempname() .. ".md" @@ -154,101 +155,134 @@ vim.api.nvim_create_autocmd("WinClosed", { callback = close_loclist_if_orphaned, }) -local status, whichkey = pcall(require, "which-key") -if status then - 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 +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 +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("-----") + 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 - - 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", "", 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 + end) end - function ToggleSpell() - vim.cmd("setlocal spell!") - end + local cur_buf = vim.api.nvim_win_get_buf(0) - 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", - }, - ["cd"] = { - ChangeToCurrentDirectory, - "Switch CWD to the directory of the open buffer", - }, - ["ss"] = { - ToggleSpell, - "Toggle spell-checker on or off", - }, - }, { prefix = vim.api.nvim_get_var("maplocalleader") }) -else - vim.api.nvim_set_keymap("n", "mp", ":lua MarkdownPreview()", { noremap = true, silent = true }) - -- Remove the Windows ^M - when the encodings gets messed up - vim.api.nvim_set_keymap("n", "md", ":lua RemoveCarriageReturn()", { noremap = true }) - vim.api.nvim_set_keymap("n", "j", ":lua FormatJson()", { noremap = true }) - vim.api.nvim_set_keymap("n", "cd", ":lua ChangeToCurrentDirectory()", { noremap = true }) + 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", "", 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") }) diff --git a/home-manager/nvim/lean.lua b/home-manager/nvim/lean.lua new file mode 100644 index 0000000..667e9ba --- /dev/null +++ b/home-manager/nvim/lean.lua @@ -0,0 +1,17 @@ +require("lspconfig")["leanls"].setup({}) + +require("lean").setup({}) + +require("which-key").register({ + l = { + i = { "LeanInfoviewToggle", "Toggle Lean info view" }, + p = { "LeanInfoviewPinTogglePause", "Pause Lean info view" }, + s = { "LeanSorryFill", "Fill open goals with sorry" }, + w = { "LeanInfoviewEnableWidgets", "Enable Lean widgets" }, + W = { "LeanInfoviewDisableWidgets", "Disable Lean widgets" }, + ["?"] = { + "LeanAbbreviationsReverseLookup", + "Show what Lean abbreviation produces the symbol under the cursor", + }, + }, +}, { prefix = vim.api.nvim_get_var("maplocalleader") }) diff --git a/home-manager/nvim/lspconfig.lua b/home-manager/nvim/lspconfig.lua index a0f7cdb..9fac0f0 100644 --- a/home-manager/nvim/lspconfig.lua +++ b/home-manager/nvim/lspconfig.lua @@ -41,6 +41,13 @@ require("lspconfig")["jsonls"].setup({ }, }) +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 @@ -117,12 +124,12 @@ do l = { ToggleLocList, "Toggle loclist" }, f = { vim.diagnostic.open_float, "Open current loclist entry in floating window" }, }, - }, { prefix = vim.api.nvim_get_var("maplocalleader") }) + }, { prefix = vim.api.nvim_get_var("mapleader") }) else - vim.keymap.set("n", "lp", vim.diagnostic.goto_prev) - vim.keymap.set("n", "ln", vim.diagnostic.goto_next) - vim.keymap.set("n", "ll", ToggleLocList) - vim.keymap.set("n", "lf", vim.diagnostic.open_float) + vim.keymap.set("n", "lp", vim.diagnostic.goto_prev) + vim.keymap.set("n", "ln", vim.diagnostic.goto_next) + vim.keymap.set("n", "ll", ToggleLocList) + vim.keymap.set("n", "lf", vim.diagnostic.open_float) end end @@ -131,77 +138,54 @@ end vim.api.nvim_create_autocmd("LspAttach", { group = vim.api.nvim_create_augroup("UserLspConfig", {}), callback = function(ev) - local whichkey_status, whichkey = pcall(require, "which-key") + local whichkey = require("which-key") -- Enable completion triggered by 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 - local opts = { buffer = ev.buf } - if whichkey_status then - 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({ - [""] = { 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" }, - }, + 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 = { - n = { vim.lsp.buf.rename, "Rename variable" }, + function() + require("telescope.builtin").lsp_references() + end, + "Find references", }, - D = { vim.lsp.buf.type_definition, "Go to type definition" }, - }, { prefix = "" }) - else - vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts) - vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts) - vim.keymap.set("n", "K", vim.lsp.buf.hover, opts) - vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts) - vim.keymap.set("n", "", vim.lsp.buf.signature_help, opts) - vim.keymap.set("n", "wa", vim.lsp.buf.add_workspace_folder, opts) - vim.keymap.set("n", "wr", vim.lsp.buf.remove_workspace_folder, opts) - vim.keymap.set("n", "wl", function() - print(vim.inspect(vim.lsp.buf.list_workspace_folders())) - end, opts) - vim.keymap.set("n", "D", vim.lsp.buf.type_definition, opts) - vim.keymap.set("n", "rn", vim.lsp.buf.rename, opts) - vim.keymap.set({ "n", "v" }, "ca", vim.lsp.buf.code_action, opts) - vim.keymap.set("n", "gr", function() - require("telescope.builtin").lsp_references() - end, opts) - vim.keymap.set("n", "f", function() - vim.lsp.buf.format({ async = true }) - end, opts) - end + }, + K = { vim.lsp.buf.hover, "Display information about symbol under cursor" }, + }) + whichkey.register({ + [""] = { 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, }) diff --git a/home-manager/nvim/nvim-dap.lua b/home-manager/nvim/nvim-dap.lua index e4ab02e..49da93d 100644 --- a/home-manager/nvim/nvim-dap.lua +++ b/home-manager/nvim/nvim-dap.lua @@ -29,71 +29,38 @@ dap.configurations.cs = { } do - local status, whichkey = pcall(require, "which-key") - if status then - 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" }, + 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 = { - 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", - }, + 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") }) - else - vim.api.nvim_set_keymap("n", "do", ":lua require('dap').step_over()", { noremap = true }) - vim.api.nvim_set_keymap("n", "di", ":lua require('dap').step_into()", { noremap = true }) - vim.api.nvim_set_keymap("n", "dc", ":lua require('dap').continue()", { noremap = true }) - vim.api.nvim_set_keymap("n", "dC", ":lua require('dap').run_last()", { noremap = true }) - vim.api.nvim_set_keymap( - "n", - "db", - ":lua require('dap').toggle_breakpoint()", - { noremap = true } - ) - vim.api.nvim_set_keymap("n", "dr", ":lua require('dap').repl.open()", { noremap = true }) - vim.api.nvim_set_keymap( - "n", - "dvv", - ":lua require('dap.ui.widgets').hover()", - { noremap = true } - ) - vim.api.nvim_set_keymap( - "n", - "dvs", - ":lua require('dap.ui.widgets').sidebar(require('dap.ui.widgets').scopes).open()", - { noremap = true } - ) - vim.api.nvim_set_keymap( - "n", - "dvf", - ":lua require('dap.ui.widgets').sidebar(require('dap.ui.widgets').frames).open()", - { noremap = true } - ) - vim.api.nvim_set_keymap("n", "dt", ":lua require('dap').terminate()", { noremap = true }) - end + t = { dap.terminate, "Terminate/stop/end debug session" }, + }, + }, { prefix = vim.api.nvim_get_var("maplocalleader") }) end diff --git a/home-manager/nvim/nvim-lightbulb.lua b/home-manager/nvim/nvim-lightbulb.lua new file mode 100644 index 0000000..28ea3b6 --- /dev/null +++ b/home-manager/nvim/nvim-lightbulb.lua @@ -0,0 +1,9 @@ +require("nvim-lightbulb").setup({ + autocmd = { enabled = true }, + ignore = { + clients = { + -- This one is really noisy + "lua_ls", + }, + }, +}) diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua index ac3171e..6b76468 100644 --- a/home-manager/nvim/venv-selector.lua +++ b/home-manager/nvim/venv-selector.lua @@ -82,27 +82,21 @@ function CreateVenv() end do - local status, whichkey = pcall(require, "which-key") - if status then - whichkey.register({ - p = { - name = "Python-related commands", - v = { - name = "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", - }, + local whichkey = require("which-key") + whichkey.register({ + p = { + name = "Python-related commands", + v = { + name = "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"), buffer = vim.api.nvim_get_current_buf() }) - else - vim.api.nvim_set_keymap("n", "pvc", ":lua CreateVenv()", { noremap = true }) - vim.api.nvim_set_keymap("n", "pvl", ":lua SelectVenv()", { noremap = true }) - vim.api.nvim_set_keymap("n", "pvo", ":VenvSelect", { noremap = true }) - end + }, + }, { prefix = vim.api.nvim_get_var("maplocalleader") }) end From d783fe475e3c802327b6604e90f21c769eab2e08 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Thu, 28 Mar 2024 15:26:08 +0000 Subject: [PATCH 16/42] Don't allow CHADtree to change what it points to --- home-manager/home.nix | 3 +- home-manager/nvim/chadtree.lua | 57 ++++++++++++++++++++++++++++++++++ home-manager/nvim/chadtree.vim | 4 --- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 home-manager/nvim/chadtree.lua delete mode 100644 home-manager/nvim/chadtree.vim diff --git a/home-manager/home.nix b/home-manager/home.nix index 7efd7e6..a09a38f 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -243,7 +243,8 @@ } { plugin = nixpkgs.vimPlugins.chadtree; - config = builtins.readFile ./nvim/chadtree.vim; + config = builtins.readFile ./nvim/chadtree.lua; + type = "lua"; } { plugin = nixpkgs.vimPlugins.coq_nvim; diff --git a/home-manager/nvim/chadtree.lua b/home-manager/nvim/chadtree.lua new file mode 100644 index 0000000..74e833b --- /dev/null +++ b/home-manager/nvim/chadtree.lua @@ -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, +}) diff --git a/home-manager/nvim/chadtree.vim b/home-manager/nvim/chadtree.vim deleted file mode 100644 index f86b780..0000000 --- a/home-manager/nvim/chadtree.vim +++ /dev/null @@ -1,4 +0,0 @@ -let g:chadtree_settings = {'xdg': v:true} - -autocmd VimEnter * CHADopen --nofocus -autocmd bufenter * if (winnr("$") == 1 && &filetype == 'CHADtree') | q | endif From 6256ad908fd131dc8f2dce0a1cc7e079cb0bac94 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Thu, 28 Mar 2024 15:29:29 +0000 Subject: [PATCH 17/42] Add licence --- LICENCE.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/LICENCE.md b/LICENCE.md index acf936c..0d135e5 100644 --- a/LICENCE.md +++ b/LICENCE.md @@ -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 From 59e1e8637c0bb0945dc73c9d73b6d395054baccd Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 29 Mar 2024 11:58:52 +0000 Subject: [PATCH 18/42] More python stuff --- home-manager/home.nix | 2 +- home-manager/nvim/python.lua | 69 +++++++++++++++++++++++++++++ home-manager/nvim/venv-selector.lua | 1 + 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 home-manager/nvim/python.lua diff --git a/home-manager/home.nix b/home-manager/home.nix index a09a38f..a9fca46 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -272,7 +272,7 @@ vimdiffAlias = true; withPython3 = true; - extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua); + 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; package = nixpkgs.neovim-nightly; }; diff --git a/home-manager/nvim/python.lua b/home-manager/nvim/python.lua new file mode 100644 index 0000000..997f5de --- /dev/null +++ b/home-manager/nvim/python.lua @@ -0,0 +1,69 @@ +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({ + p = { + name = "Python-related commands", + t = { + "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 diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua index 6b76468..6f13291 100644 --- a/home-manager/nvim/venv-selector.lua +++ b/home-manager/nvim/venv-selector.lua @@ -3,6 +3,7 @@ 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", { From c6879ac2548dfaed862d4b3323c726afea63402b Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 29 Mar 2024 12:24:50 +0000 Subject: [PATCH 19/42] Delete unnecessary overlay --- flake.nix | 2 +- overlays.nix | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 overlays.nix diff --git a/flake.nix b/flake.nix index a4028ed..50357cf 100644 --- a/flake.nix +++ b/flake.nix @@ -46,7 +46,7 @@ }; systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"]; in let - overlays = [emacs.overlay neovim-nightly.overlay] ++ import ./overlays.nix; + overlays = [emacs.overlay neovim-nightly.overlay]; recursiveMerge = attrList: let f = attrPath: builtins.zipAttrsWith (n: values: diff --git a/overlays.nix b/overlays.nix deleted file mode 100644 index 1f45229..0000000 --- a/overlays.nix +++ /dev/null @@ -1,10 +0,0 @@ -[ - (self: super: { - # https://github.com/NixOS/nixpkgs/issues/153304 - alacritty = super.alacritty.overrideAttrs ( - o: rec { - doCheck = false; - } - ); - }) -] From 68d57ea7cbe643778922ed349fc3cebea03ba6aa Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:07:17 +0000 Subject: [PATCH 20/42] Add capybara config (#45) --- flake.lock | 56 +++++++++++----------- flake.nix | 25 +++++++++- hardware/capybara.nix | 34 ++++++++++++++ home-manager/capybara-config.nix | 57 +++++++++++++++++++++++ home-manager/earthworm-config.nix | 6 +-- home-manager/{earthworm.nix => linux.nix} | 7 +-- 6 files changed, 145 insertions(+), 40 deletions(-) create mode 100644 hardware/capybara.nix create mode 100644 home-manager/capybara-config.nix rename home-manager/{earthworm.nix => linux.nix} (91%) diff --git a/flake.lock b/flake.lock index 3db20e4..0e11556 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1710209440, - "narHash": "sha256-1JwFo3u2aVrvpz12OotjCK51EQ0hEDI7xSG7CEvTSk8=", + "lastModified": 1711325419, + "narHash": "sha256-dKZUWMB4py9rhefu1lsrCrwksK4WX/dtW8Ma807KyPA=", "owner": "tpwrules", "repo": "nixos-apple-silicon", - "rev": "bdc68b494d6a26c9457f4841ab1a6109b12a33e6", + "rev": "93e85575f63b32b9996676513d95288fc1c87ca9", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1710717205, - "narHash": "sha256-Wf3gHh5uV6W1TV/A8X8QJf99a5ypDSugY4sNtdJDe0A=", + "lastModified": 1711763326, + "narHash": "sha256-sXcesZWKXFlEQ8oyGHnfk4xc9f2Ip0X/+YZOq3sKviI=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "bcc8afd06e237df060c85bad6af7128e05fd61a3", + "rev": "36524adc31566655f2f4d55ad6b875fb5c1a4083", "type": "github" }, "original": { @@ -50,11 +50,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1711271005, - "narHash": "sha256-JrhnnutZvHowEJFIrA/rQAFgGAc83WOx+BVy97teqKM=", + "lastModified": 1711789603, + "narHash": "sha256-5c8prZYLBFgMDoBrBTMuAu6F33HHF9kfK+i4d39gUDA=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "d6bbd32eb3e0f167f312e1031c1beee452dc9174", + "rev": "f8ad90d467e2a48cf91aa0b3b75ac7929dc07425", "type": "github" }, "original": { @@ -219,11 +219,11 @@ ] }, "locked": { - "lastModified": 1711133180, - "narHash": "sha256-WJOahf+6115+GMl3wUfURu8fszuNeJLv9qAWFQl3Vmo=", + "lastModified": 1711625603, + "narHash": "sha256-W+9dfqA9bqUIBV5u7jaIARAzMe3kTq/Hp2SpSVXKRQw=", "owner": "nix-community", "repo": "home-manager", - "rev": "1c2c5e4cabba4c43504ef0f8cc3f3dfa284e2dbb", + "rev": "c0ef0dab55611c676ad7539bf4e41b3ec6fa87d2", "type": "github" }, "original": { @@ -254,11 +254,11 @@ }, "locked": { "dir": "contrib", - "lastModified": 1711323947, - "narHash": "sha256-Vc478rxwJkMuOcgBXm+brraWk9lbFqrGEdXVuST2l/A=", + "lastModified": 1711756269, + "narHash": "sha256-3e0j/bvXCI1eOxt6x/ENGkhlDRD3nxLhDU0Q8JViiw0=", "owner": "neovim", "repo": "neovim", - "rev": "02d00cf3eed6681c6dde40585551c8243d7c003f", + "rev": "2424c3e6967ef953222cf48564629ffe5812d415", "type": "github" }, "original": { @@ -279,11 +279,11 @@ ] }, "locked": { - "lastModified": 1711325009, - "narHash": "sha256-c5OJdyuXYzTP+k+PN73X+0pvgXR1yYMYok+72x4SLVg=", + "lastModified": 1711757045, + "narHash": "sha256-+85QI5/ABsk3ObUS2/uPkShQQuHbaq4bgzVBE5zAY94=", "owner": "nix-community", "repo": "neovim-nightly-overlay", - "rev": "119bbc295f56b531cb87502f5d2fff13dcc35a35", + "rev": "8c9dcf6816db1b976046e491dfb61eef8c15a202", "type": "github" }, "original": { @@ -294,27 +294,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709961763, - "narHash": "sha256-6H95HGJHhEZtyYA3rIQpvamMKAGoa8Yh2rFV29QnuGw=", + "lastModified": 1711163522, + "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", "owner": "nixos", "repo": "nixpkgs", - "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", + "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", + "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1711124224, - "narHash": "sha256-l0zlN/3CiodvWDtfBOVxeTwYSRz93muVbXWSpaMjXxM=", + "lastModified": 1711668574, + "narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "56528ee42526794d413d6f244648aaee4a7b56c0", + "rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659", "type": "github" }, "original": { @@ -326,11 +326,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1711231723, - "narHash": "sha256-dARJQ8AJOv6U+sdRePkbcVyVbXJTi1tReCrkkOeusiA=", + "lastModified": 1711715736, + "narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e1d501922fd7351da4200e1275dfcf5faaad1220", + "rev": "807c549feabce7eddbf259dbdcec9e0600a0660d", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 50357cf..a57e1e1 100644 --- a/flake.nix +++ b/flake.nix @@ -61,6 +61,29 @@ 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; + }; + 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 @@ -81,7 +104,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)]; } ]; }; diff --git a/hardware/capybara.nix b/hardware/capybara.nix new file mode 100644 index 0000000..77491b5 --- /dev/null +++ b/hardware/capybara.nix @@ -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; +} diff --git a/home-manager/capybara-config.nix b/home-manager/capybara-config.nix new file mode 100644 index 0000000..e38f93c --- /dev/null +++ b/home-manager/capybara-config.nix @@ -0,0 +1,57 @@ +{ + pkgs, + config, + ... +}: { + 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"]; + }; + + environment.systemPackages = [ + pkgs.vim + pkgs.wget + pkgs.tmux + pkgs.home-manager + pkgs.firefox + ]; + + 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 + ''; +} diff --git a/home-manager/earthworm-config.nix b/home-manager/earthworm-config.nix index 7bda66d..bab58ad 100644 --- a/home-manager/earthworm-config.nix +++ b/home-manager/earthworm-config.nix @@ -1,8 +1,4 @@ -{ - config, - pkgs, - ... -}: { +{pkgs, ...}: { imports = [ ../hardware/earthworm.nix ]; diff --git a/home-manager/earthworm.nix b/home-manager/linux.nix similarity index 91% rename from home-manager/earthworm.nix rename to home-manager/linux.nix index 058f941..7241334 100644 --- a/home-manager/earthworm.nix +++ b/home-manager/linux.nix @@ -1,9 +1,4 @@ -{ - nixpkgs, - username, - dotnet, - ... -}: { +{nixpkgs, ...}: { home.packages = [nixpkgs.firefox-wayland]; nixpkgs.config.firefox.speechSynthesisSupport = true; From 7b94e7658979664b2a8bb5e681149dd903d82f0b Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 2 Apr 2024 21:08:55 +0100 Subject: [PATCH 21/42] Always enable gutter --- home-manager/nvim/init.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index 36a4a79..56a5d06 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -3,6 +3,8 @@ 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" } From f723b644865d0e5179f2cbe76ab21e1e15a46591 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 3 Apr 2024 21:21:46 +0100 Subject: [PATCH 22/42] Add more debugger-related commands in Python --- home-manager/nvim/nvim-dap-python.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/home-manager/nvim/nvim-dap-python.lua b/home-manager/nvim/nvim-dap-python.lua index cd88bac..31945b1 100644 --- a/home-manager/nvim/nvim-dap-python.lua +++ b/home-manager/nvim/nvim-dap-python.lua @@ -1 +1,17 @@ require("dap-python").setup("%PYTHONENV%/bin/python") + +do + local whichkey = require("which-key") + whichkey.register({ + p = { + d = { + "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 From 7fb26eb70785e1e57ac78601e686dfd18f9c3615 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 5 Apr 2024 22:12:12 +0100 Subject: [PATCH 23/42] Add prerequisites for a mail setup --- home-manager/mail/mutt-oauth2.py | 402 +++++++++++++++++++++++++++++++ mbsync.nix | 11 + 2 files changed, 413 insertions(+) create mode 100644 home-manager/mail/mutt-oauth2.py create mode 100644 mbsync.nix diff --git a/home-manager/mail/mutt-oauth2.py b/home-manager/mail/mutt-oauth2.py new file mode 100644 index 0000000..df3e8bb --- /dev/null +++ b/home-manager/mail/mutt-oauth2.py @@ -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'Authorizaton result') + self.wfile.write(b'

Authorization redirect completed. You may ' + b'close this window.

') + 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) + diff --git a/mbsync.nix b/mbsync.nix new file mode 100644 index 0000000..990c119 --- /dev/null +++ b/mbsync.nix @@ -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" + ''; +} From d867348640929b59da7214b89fd925c12c51130f Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 5 Apr 2024 22:14:26 +0100 Subject: [PATCH 24/42] Move mailcap --- home-manager/home.nix | 2 +- home-manager/{ => mail}/mailcap | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename home-manager/{ => mail}/mailcap (100%) diff --git a/home-manager/home.nix b/home-manager/home.nix index a9fca46..86c2e3a 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -355,7 +355,7 @@ (nixpkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) ]; - home.file.".mailcap".source = ./mailcap; + home.file.".mailcap".source = ./mail/mailcap; home.file.".ideavimrc".source = ./ideavimrc; home.file.".config/yt-dlp/config".source = ./youtube-dl.conf; home.file.".config/ripgrep/config".source = ./ripgrep.conf; diff --git a/home-manager/mailcap b/home-manager/mail/mailcap similarity index 100% rename from home-manager/mailcap rename to home-manager/mail/mailcap From bf1dfe3d6da69a888339840c7adb702d534db56d Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 5 Apr 2024 22:16:39 +0100 Subject: [PATCH 25/42] Rename some vim bindings --- home-manager/nvim/nvim-dap-python.lua | 4 +--- home-manager/nvim/python.lua | 5 +---- home-manager/nvim/venv-selector.lua | 7 ++----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/home-manager/nvim/nvim-dap-python.lua b/home-manager/nvim/nvim-dap-python.lua index 31945b1..059ab3e 100644 --- a/home-manager/nvim/nvim-dap-python.lua +++ b/home-manager/nvim/nvim-dap-python.lua @@ -3,15 +3,13 @@ require("dap-python").setup("%PYTHONENV%/bin/python") do local whichkey = require("which-key") whichkey.register({ - p = { - d = { + ['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 diff --git a/home-manager/nvim/python.lua b/home-manager/nvim/python.lua index 997f5de..5476f60 100644 --- a/home-manager/nvim/python.lua +++ b/home-manager/nvim/python.lua @@ -56,14 +56,11 @@ end do local whichkey = require("which-key") whichkey.register({ - p = { - name = "Python-related commands", - t = { + ['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 diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua index 6f13291..37ed4cc 100644 --- a/home-manager/nvim/venv-selector.lua +++ b/home-manager/nvim/venv-selector.lua @@ -85,10 +85,8 @@ end do local whichkey = require("which-key") whichkey.register({ - p = { - name = "Python-related commands", - v = { - name = "Virtual environment-related commands", + ['pv'] = { + name = "Python virtual environment-related commands", c = { CreateVenv, "Create virtual environment" }, l = { SelectVenv, "Load virtual environment" }, o = { @@ -97,7 +95,6 @@ do end, "Choose (override) new virtual environment", }, - }, }, }, { prefix = vim.api.nvim_get_var("maplocalleader") }) end From aa3d08745aefa9d467871cea611b8ee4d75a16dd Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 5 Apr 2024 23:52:43 +0100 Subject: [PATCH 26/42] Everything except the actual mail config --- flake.nix | 3 +++ home-manager/home.nix | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/flake.nix b/flake.nix index a57e1e1..f2ecee5 100644 --- a/flake.nix +++ b/flake.nix @@ -73,6 +73,7 @@ nixpkgs = pkgs; username = "patrick"; dotnet = pkgs.dotnet-sdk_8; + mbsync = import ./mbsync.nix {inherit pkgs;}; }; in [ ./home-manager/capybara-config.nix @@ -96,6 +97,7 @@ nixpkgs = pkgs; username = "patrick"; dotnet = pkgs.dotnet-sdk_8; + mbsync = import ./mbsync.nix {inherit pkgs;}; }; in [ ./home-manager/earthworm-config.nix @@ -123,6 +125,7 @@ username = "patrick"; dotnet = pkgs.dotnet-sdk_8; whisper = whisper.packages.${system}; + mbsync = import ./mbsync.nix {inherit pkgs;}; }; in [ ./darwin-configuration.nix diff --git a/home-manager/home.nix b/home-manager/home.nix index 86c2e3a..83aef43 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -1,6 +1,7 @@ { nixpkgs, username, + mbsync, dotnet, ... }: { @@ -271,6 +272,7 @@ vimAlias = true; vimdiffAlias = true; withPython3 = true; + withRuby = true; 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; @@ -295,6 +297,7 @@ }; home.packages = [ + nixpkgs.notmuch nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs nixpkgs.nodePackages_latest.bash-language-server nixpkgs.nodePackages_latest.vscode-json-languageserver @@ -355,6 +358,20 @@ (nixpkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) ]; + programs.mbsync = { + extraConfig = '' + CopyArrivalDate yes + ''; + package = mbsync; + }; + programs.neomutt = { + extraConfig = '' + set use_threads=threads sort=last-date sort_aux=date + ''; + sidebar.enable = true; + vimKeys = true; + }; + home.file.".mailcap".source = ./mail/mailcap; home.file.".ideavimrc".source = ./ideavimrc; home.file.".config/yt-dlp/config".source = ./youtube-dl.conf; From d3ec6b02c3faa327e104459b481243f8be95c53d Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 6 Apr 2024 00:25:15 +0100 Subject: [PATCH 27/42] Ripgrep config from store (#46) --- home-manager/home.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/home-manager/home.nix b/home-manager/home.nix index 83aef43..a2bf4e1 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -57,7 +57,7 @@ grep = "${nixpkgs.ripgrep}/bin/rg"; }; sessionVariables = { - RIPGREP_CONFIG_PATH = "/Users/${username}/.config/ripgrep/config"; + RIPGREP_CONFIG_PATH = ./ripgrep.conf; }; initExtra = builtins.readFile ./.zshrc; }; @@ -375,6 +375,8 @@ home.file.".mailcap".source = ./mail/mailcap; home.file.".ideavimrc".source = ./ideavimrc; home.file.".config/yt-dlp/config".source = ./youtube-dl.conf; + # Not actually used, but if I ever need to debug it'll be easier + # if I can see what the current state of the world is by looking in .config home.file.".config/ripgrep/config".source = ./ripgrep.conf; programs.emacs = { From a210ee430139305547bae34e53ff487ebb7ca730 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 6 Apr 2024 12:24:52 +0100 Subject: [PATCH 28/42] Add mail config (#47) --- darwin-configuration.nix | 33 +++++++++ flake.nix | 3 + home-manager/ascii | 2 + home-manager/home.nix | 155 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 home-manager/ascii diff --git a/darwin-configuration.nix b/darwin-configuration.nix index c7403e3..c80a23b 100644 --- a/darwin-configuration.nix +++ b/darwin-configuration.nix @@ -1,5 +1,6 @@ {pkgs, ...}: let python = import ./python.nix {inherit pkgs;}; + mbsync = import ./mbsync.nix {inherit pkgs;}; in { nix.useDaemon = true; @@ -26,6 +27,38 @@ in { # $ 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; diff --git a/flake.nix b/flake.nix index f2ecee5..73cc4e3 100644 --- a/flake.nix +++ b/flake.nix @@ -74,6 +74,7 @@ username = "patrick"; dotnet = pkgs.dotnet-sdk_8; mbsync = import ./mbsync.nix {inherit pkgs;}; + secretsPath = "/home/patrick/.secrets/"; }; in [ ./home-manager/capybara-config.nix @@ -98,6 +99,7 @@ username = "patrick"; dotnet = pkgs.dotnet-sdk_8; mbsync = import ./mbsync.nix {inherit pkgs;}; + secretsPath = "/home/patrick/.secrets/"; }; in [ ./home-manager/earthworm-config.nix @@ -126,6 +128,7 @@ dotnet = pkgs.dotnet-sdk_8; whisper = whisper.packages.${system}; mbsync = import ./mbsync.nix {inherit pkgs;}; + secretsPath = "/Users/patrick/.secrets/"; }; in [ ./darwin-configuration.nix diff --git a/home-manager/ascii b/home-manager/ascii new file mode 100644 index 0000000..244b281 --- /dev/null +++ b/home-manager/ascii @@ -0,0 +1,2 @@ + +  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ diff --git a/home-manager/home.nix b/home-manager/home.nix index a2bf4e1..7b9af13 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -3,8 +3,51 @@ username, mbsync, dotnet, + secretsPath, ... -}: { +}: let + deobfuscate = str: let + lib = nixpkgs.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 ./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 { # Let Home Manager install and manage itself. programs.home-manager.enable = true; @@ -358,13 +401,121 @@ (nixpkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) ]; + 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 = ''${nixpkgs.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; + }; + programs.mbsync = { + enable = true; extraConfig = '' CopyArrivalDate yes ''; package = mbsync; }; programs.neomutt = { + enable = true; extraConfig = '' set use_threads=threads sort=last-date sort_aux=date ''; @@ -372,6 +523,8 @@ vimKeys = true; }; + programs.notmuch.enable = true; + home.file.".mailcap".source = ./mail/mailcap; home.file.".ideavimrc".source = ./ideavimrc; home.file.".config/yt-dlp/config".source = ./youtube-dl.conf; From fd71527762dfac602126e370369f1f6015a4d7b0 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 6 Apr 2024 12:30:05 +0100 Subject: [PATCH 29/42] Delete Python workaround (#48) --- darwin-configuration.nix | 3 +-- python.nix | 14 -------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 python.nix diff --git a/darwin-configuration.nix b/darwin-configuration.nix index c80a23b..567e50c 100644 --- a/darwin-configuration.nix +++ b/darwin-configuration.nix @@ -1,5 +1,4 @@ {pkgs, ...}: let - python = import ./python.nix {inherit pkgs;}; mbsync = import ./mbsync.nix {inherit pkgs;}; in { nix.useDaemon = true; @@ -12,7 +11,7 @@ in { pkgs.rustup pkgs.libiconv pkgs.clang - python + pkgs.python3 ]; users.users.patrick = { diff --git a/python.nix b/python.nix deleted file mode 100644 index b08f40c..0000000 --- a/python.nix +++ /dev/null @@ -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 From ccaa90d392bd971d621f57f6aa597f92da646a80 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 6 Apr 2024 14:29:21 +0100 Subject: [PATCH 30/42] Delete dead file (#49) --- server-home.nix | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 server-home.nix diff --git a/server-home.nix b/server-home.nix deleted file mode 100644 index f2e98ee..0000000 --- a/server-home.nix +++ /dev/null @@ -1,2 +0,0 @@ -{nixpkgs, ...}: { -} From 3208bf16c52997befc2482a3419e6b55bd11a51d Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:33:50 +0100 Subject: [PATCH 31/42] Split into modules (#50) --- flake.lock | 56 ++-- home-manager/home.nix | 275 +----------------- home-manager/modules/agda.nix | 7 + home-manager/modules/alacritty.nix | 17 ++ home-manager/modules/direnv.nix | 10 + home-manager/modules/emacs.nix | 14 + home-manager/modules/mail.nix | 179 ++++++++++++ home-manager/{ => modules/mail}/ascii | 0 home-manager/{ => modules}/mail/mailcap | 0 .../{ => modules}/mail/mutt-oauth2.py | 0 home-manager/modules/ripgrep.nix | 7 + .../{ => modules/ripgrep}/ripgrep.conf | 0 home-manager/modules/rust.nix | 10 + .../{ => modules/rust}/cargo-config.toml | 0 home-manager/modules/tmux.nix | 18 ++ home-manager/modules/zsh.nix | 23 ++ home-manager/{.zshrc => modules/zsh/zshrc} | 0 17 files changed, 329 insertions(+), 287 deletions(-) create mode 100644 home-manager/modules/agda.nix create mode 100644 home-manager/modules/alacritty.nix create mode 100644 home-manager/modules/direnv.nix create mode 100644 home-manager/modules/emacs.nix create mode 100644 home-manager/modules/mail.nix rename home-manager/{ => modules/mail}/ascii (100%) rename home-manager/{ => modules}/mail/mailcap (100%) rename home-manager/{ => modules}/mail/mutt-oauth2.py (100%) create mode 100644 home-manager/modules/ripgrep.nix rename home-manager/{ => modules/ripgrep}/ripgrep.conf (100%) create mode 100644 home-manager/modules/rust.nix rename home-manager/{ => modules/rust}/cargo-config.toml (100%) create mode 100644 home-manager/modules/tmux.nix create mode 100644 home-manager/modules/zsh.nix rename home-manager/{.zshrc => modules/zsh/zshrc} (100%) diff --git a/flake.lock b/flake.lock index 0e11556..44d4a9c 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1711325419, - "narHash": "sha256-dKZUWMB4py9rhefu1lsrCrwksK4WX/dtW8Ma807KyPA=", + "lastModified": 1712279577, + "narHash": "sha256-Bwn4rmQi2L2iX6g3ycQMA4baE3zgPHAO0xPBpr2T4/k=", "owner": "tpwrules", "repo": "nixos-apple-silicon", - "rev": "93e85575f63b32b9996676513d95288fc1c87ca9", + "rev": "d47afc3f0f8b3078c818da8609c41340af61a2ec", "type": "github" }, "original": { @@ -50,11 +50,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1711789603, - "narHash": "sha256-5c8prZYLBFgMDoBrBTMuAu6F33HHF9kfK+i4d39gUDA=", + "lastModified": 1712941527, + "narHash": "sha256-wD9XQFGW0qzRW1YHj6oklCHzgKNxjwS0tZ/hFGgiHX4=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "f8ad90d467e2a48cf91aa0b3b75ac7929dc07425", + "rev": "9f4406718ada7af83892e17355ef7fd202c20897", "type": "github" }, "original": { @@ -102,11 +102,11 @@ ] }, "locked": { - "lastModified": 1709336216, - "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", "type": "github" }, "original": { @@ -219,11 +219,11 @@ ] }, "locked": { - "lastModified": 1711625603, - "narHash": "sha256-W+9dfqA9bqUIBV5u7jaIARAzMe3kTq/Hp2SpSVXKRQw=", + "lastModified": 1712759992, + "narHash": "sha256-2APpO3ZW4idlgtlb8hB04u/rmIcKA8O7pYqxF66xbNY=", "owner": "nix-community", "repo": "home-manager", - "rev": "c0ef0dab55611c676ad7539bf4e41b3ec6fa87d2", + "rev": "31357486b0ef6f4e161e002b6893eeb4fafc3ca9", "type": "github" }, "original": { @@ -254,11 +254,11 @@ }, "locked": { "dir": "contrib", - "lastModified": 1711756269, - "narHash": "sha256-3e0j/bvXCI1eOxt6x/ENGkhlDRD3nxLhDU0Q8JViiw0=", + "lastModified": 1712877603, + "narHash": "sha256-8JesAgnsv1bD+xHNoqefz0Gv243wSiCKnzh4rhZLopU=", "owner": "neovim", "repo": "neovim", - "rev": "2424c3e6967ef953222cf48564629ffe5812d415", + "rev": "18ee9f9e7dbbc9709ee9c1572870b4ad31443569", "type": "github" }, "original": { @@ -279,11 +279,11 @@ ] }, "locked": { - "lastModified": 1711757045, - "narHash": "sha256-+85QI5/ABsk3ObUS2/uPkShQQuHbaq4bgzVBE5zAY94=", + "lastModified": 1712880226, + "narHash": "sha256-2CGLzsFft8zF/gEY4qDN0uAjRCWUqvNJ9yV118NlzTg=", "owner": "nix-community", "repo": "neovim-nightly-overlay", - "rev": "8c9dcf6816db1b976046e491dfb61eef8c15a202", + "rev": "58d367a1924bf0d02bcc5bd2c5af8ac97f178381", "type": "github" }, "original": { @@ -294,27 +294,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711163522, - "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", + "lastModified": 1712163089, + "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", + "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", + "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1711668574, - "narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=", + "lastModified": 1712741485, + "narHash": "sha256-bCs0+MSTra80oXAsnM6Oq62WsirOIaijQ/BbUY59tR4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659", + "rev": "b2cf36f43f9ef2ded5711b30b1f393ac423d8f72", "type": "github" }, "original": { @@ -326,11 +326,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1711715736, - "narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=", + "lastModified": 1712849433, + "narHash": "sha256-flQtf/ZPJgkLY/So3Fd+dGilw2DKIsiwgMEn7BbBHL0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "807c549feabce7eddbf259dbdcec9e0600a0660d", + "rev": "f173d0881eff3b21ebb29a2ef8bedbc106c86ea5", "type": "github" }, "original": { diff --git a/home-manager/home.nix b/home-manager/home.nix index 7b9af13..8f6c8d3 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -5,49 +5,7 @@ dotnet, secretsPath, ... -}: let - deobfuscate = str: let - lib = nixpkgs.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 ./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 { +}: { # Let Home Manager install and manage itself. programs.home-manager.enable = true; @@ -67,47 +25,24 @@ in { 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; - autosuggestion.enable = 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.libcxx}/lib"; - RUST_BACKTRACE = "full"; - }; - shellAliases = { - vim = "nvim"; - view = "vim -R"; - grep = "${nixpkgs.ripgrep}/bin/rg"; - }; - sessionVariables = { - RIPGREP_CONFIG_PATH = ./ripgrep.conf; - }; - 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 = { @@ -322,25 +257,7 @@ in { package = nixpkgs.neovim-nightly; }; - programs.direnv = { - enable = true; - enableZshIntegration = true; - nix-direnv.enable = true; - }; - - programs.alacritty = { - enable = true; - settings = { - font = { - normal = { - family = "FiraCode Nerd Font Mono"; - }; - }; - }; - }; - home.packages = [ - nixpkgs.notmuch nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs nixpkgs.nodePackages_latest.bash-language-server nixpkgs.nodePackages_latest.vscode-json-languageserver @@ -353,8 +270,6 @@ in { nixpkgs.nil nixpkgs.fsautocomplete nixpkgs.keepassxc - nixpkgs.rust-analyzer - nixpkgs.tmux nixpkgs.wget nixpkgs.yt-dlp nixpkgs.cmake @@ -365,32 +280,20 @@ in { 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.grpc-tools - nixpkgs.element-desktop - nixpkgs.ihp-new - nixpkgs.direnv nixpkgs.lnav nixpkgs.age nixpkgs.nodejs nixpkgs.nodePackages.pyright nixpkgs.sqlitebrowser - nixpkgs.typst - nixpkgs.poetry nixpkgs.woodpecker-agent - nixpkgs.alacritty nixpkgs.lynx nixpkgs.alejandra nixpkgs.ffmpeg @@ -398,154 +301,8 @@ in { nixpkgs.pandoc nixpkgs.fd nixpkgs.sumneko-lua-language-server - (nixpkgs.nerdfonts.override {fonts = ["FiraCode" "DroidSansMono"];}) ]; - 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 = ''${nixpkgs.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; - }; - - 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.file.".ideavimrc".source = ./ideavimrc; home.file.".config/yt-dlp/config".source = ./youtube-dl.conf; - # Not actually used, but if I ever need to debug it'll be easier - # if I can see what the current state of the world is by looking in .config - home.file.".config/ripgrep/config".source = ./ripgrep.conf; - - programs.emacs = { - enable = true; - package = nixpkgs.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) - ;; Allow hash to be entered - (global set-key (kbd "M-3") '(lambda () (interactive) (insert "#"))) - ''; - }; - - home.file.".cargo/config.toml".source = ./cargo-config.toml; } diff --git a/home-manager/modules/agda.nix b/home-manager/modules/agda.nix new file mode 100644 index 0000000..df3c831 --- /dev/null +++ b/home-manager/modules/agda.nix @@ -0,0 +1,7 @@ +{pkgs, ...}: { + imports = [./emacs.nix]; + + home.packages = [ + pkgs.agda + ]; +} diff --git a/home-manager/modules/alacritty.nix b/home-manager/modules/alacritty.nix new file mode 100644 index 0000000..dda085f --- /dev/null +++ b/home-manager/modules/alacritty.nix @@ -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"];}) + ]; +} diff --git a/home-manager/modules/direnv.nix b/home-manager/modules/direnv.nix new file mode 100644 index 0000000..978d0fa --- /dev/null +++ b/home-manager/modules/direnv.nix @@ -0,0 +1,10 @@ +{pkgs, ...}: { + home.packages = [ + pkgs.direnv + ]; + programs.direnv = { + enable = true; + enableZshIntegration = true; + nix-direnv.enable = true; + }; +} diff --git a/home-manager/modules/emacs.nix b/home-manager/modules/emacs.nix new file mode 100644 index 0000000..6995dc9 --- /dev/null +++ b/home-manager/modules/emacs.nix @@ -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) + ''; + }; +} diff --git a/home-manager/modules/mail.nix b/home-manager/modules/mail.nix new file mode 100644 index 0000000..1d3ff2e --- /dev/null +++ b/home-manager/modules/mail.nix @@ -0,0 +1,179 @@ +{ + pkgs, + mbsync, + secretsPath, + ... +}: let + deobfuscate = str: let + lib = pkgs.lib; + base64Table = + builtins.listToAttrs + (lib.imap0 (i: c: lib.nameValuePair c i) + (lib.stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")); + + # Generated using python3: + # print(''.join([ chr(n) for n in range(1, 256) ]), file=open('ascii', 'w')) + ascii = builtins.readFile ./mail/ascii; + + # List of base-64 numbers + numbers64 = map (c: base64Table.${c}) (lib.lists.reverseList (lib.stringToCharacters str)); + + # List of base-256 numbers + numbers256 = lib.concatLists (lib.genList ( + i: let + v = + lib.foldl' + (acc: el: acc * 64 + el) + 0 + (lib.sublist (i * 4) 4 numbers64); + in [ + (lib.mod (v / 256 / 256) 256) + (lib.mod (v / 256) 256) + (lib.mod v 256) + ] + ) (lib.length numbers64 / 4)); + in + # Converts base-256 numbers to ascii + lib.concatMapStrings ( + n: + # Can't represent the null byte in Nix.. + let + result = lib.substring (n - 1) 1 ascii; + in + if result == " " + then "" + else result + ) + numbers256; +in { + accounts.email.accounts."Gmail" = let + address = (deobfuscate "AFTN0cWdh12c") + "gmail.com"; + in { + notmuch.enable = true; + neomutt = { + enable = true; + }; + address = address; + flavor = "gmail.com"; + mbsync = { + enable = true; + create = "maildir"; + extraConfig.account = { + AuthMechs = "XOAUTH2"; + }; + }; + userName = address; + # This is accompanied by a developer application at Google: + # https://console.cloud.google.com/apis/credentials + # Create an OAuth 2.0 Client ID with type `Desktop`. + # The Google application needs the https://mail.google.com scope; mine has + # an authorized domain `google.com` but I don't know if that's required. + # Enter the client ID and client secret into a two-line text file + # named gmail-client-app.txt immediately next to the intended destination + # secret file (the arg to mutt-oauth2.py in the invocation): + # so here it would be /path/to/gmail-client-app.txt . + # Run `./mail/mutt-oauth2.py /path/to/secret --authorize --verbose` once manually, + # and that will populate /path/to/secret. + # I've left it unencrypted here; the original uses GPG to store it encrypted at rest. + passwordCommand = ''${pkgs.python3}/bin/python ${./mail/mutt-oauth2.py} ${secretsPath}/gmail.txt''; + realName = "Patrick Stevens"; + }; + + accounts.email.accounts."BTInternet" = let + address = (deobfuscate "z5WZ2VGdz5yajlmc0FGc") + "@btinternet.com"; + in { + notmuch.enable = true; + neomutt = { + enable = true; + }; + address = address; + imap = { + host = "mail.btinternet.com"; + port = 993; + tls = { + enable = true; + useStartTls = false; + }; + }; + mbsync = { + enable = true; + create = "maildir"; + }; + realName = "Patrick Stevens"; + passwordCommand = "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; + }; + + 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 + ]; +} diff --git a/home-manager/ascii b/home-manager/modules/mail/ascii similarity index 100% rename from home-manager/ascii rename to home-manager/modules/mail/ascii diff --git a/home-manager/mail/mailcap b/home-manager/modules/mail/mailcap similarity index 100% rename from home-manager/mail/mailcap rename to home-manager/modules/mail/mailcap diff --git a/home-manager/mail/mutt-oauth2.py b/home-manager/modules/mail/mutt-oauth2.py similarity index 100% rename from home-manager/mail/mutt-oauth2.py rename to home-manager/modules/mail/mutt-oauth2.py diff --git a/home-manager/modules/ripgrep.nix b/home-manager/modules/ripgrep.nix new file mode 100644 index 0000000..e5c6d9f --- /dev/null +++ b/home-manager/modules/ripgrep.nix @@ -0,0 +1,7 @@ +{pkgs, ...}: { + home.packages = [ + pkgs.ripgrep + ]; + + home.file.".config/ripgrep/config".source = ./ripgrep/ripgrep.conf; +} diff --git a/home-manager/ripgrep.conf b/home-manager/modules/ripgrep/ripgrep.conf similarity index 100% rename from home-manager/ripgrep.conf rename to home-manager/modules/ripgrep/ripgrep.conf diff --git a/home-manager/modules/rust.nix b/home-manager/modules/rust.nix new file mode 100644 index 0000000..49294b1 --- /dev/null +++ b/home-manager/modules/rust.nix @@ -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 + ]; +} diff --git a/home-manager/cargo-config.toml b/home-manager/modules/rust/cargo-config.toml similarity index 100% rename from home-manager/cargo-config.toml rename to home-manager/modules/rust/cargo-config.toml diff --git a/home-manager/modules/tmux.nix b/home-manager/modules/tmux.nix new file mode 100644 index 0000000..13c430e --- /dev/null +++ b/home-manager/modules/tmux.nix @@ -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' + ''; + }; +} diff --git a/home-manager/modules/zsh.nix b/home-manager/modules/zsh.nix new file mode 100644 index 0000000..60a1430 --- /dev/null +++ b/home-manager/modules/zsh.nix @@ -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; +} diff --git a/home-manager/.zshrc b/home-manager/modules/zsh/zshrc similarity index 100% rename from home-manager/.zshrc rename to home-manager/modules/zsh/zshrc From e7f68f24a35dcc78fd39e7409e7aa5c198f1ee38 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 15 Apr 2024 21:11:22 +0100 Subject: [PATCH 32/42] Add gnupg (#51) --- darwin-configuration.nix | 1 - home-manager/home.nix | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/darwin-configuration.nix b/darwin-configuration.nix index 567e50c..c4fc37b 100644 --- a/darwin-configuration.nix +++ b/darwin-configuration.nix @@ -62,7 +62,6 @@ in { 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; diff --git a/home-manager/home.nix b/home-manager/home.nix index 8f6c8d3..9e57c39 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -292,7 +292,6 @@ nixpkgs.age nixpkgs.nodejs nixpkgs.nodePackages.pyright - nixpkgs.sqlitebrowser nixpkgs.woodpecker-agent nixpkgs.lynx nixpkgs.alejandra @@ -301,6 +300,7 @@ nixpkgs.pandoc nixpkgs.fd nixpkgs.sumneko-lua-language-server + nixpkgs.gnupg ]; home.file.".ideavimrc".source = ./ideavimrc; From 02ceae3e22ca046f106205f857aa155ba63661ed Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 15 Apr 2024 21:42:29 +0100 Subject: [PATCH 33/42] Start signing Git commits (#52) --- darwin-configuration.nix | 1 + home-manager/home.nix | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/darwin-configuration.nix b/darwin-configuration.nix index c4fc37b..ecf2c82 100644 --- a/darwin-configuration.nix +++ b/darwin-configuration.nix @@ -21,6 +21,7 @@ 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 diff --git a/home-manager/home.nix b/home-manager/home.nix index 9e57c39..3862bb2 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -49,13 +49,16 @@ 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"; }; From b361bbcbcb71e99191d38702668f6cebd3aeb471 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:48:11 +0100 Subject: [PATCH 34/42] Syncthing (#53) --- home-manager/home.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/home-manager/home.nix b/home-manager/home.nix index 3862bb2..ab1de40 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -120,6 +120,10 @@ }; }; + services.syncthing = { + enable = true; + }; + programs.neovim = let pynvimpp = nixpkgs.python3.pkgs.buildPythonPackage { pname = "pynvim-pp"; @@ -261,6 +265,7 @@ }; home.packages = [ + nixpkgs.syncthing nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs nixpkgs.nodePackages_latest.bash-language-server nixpkgs.nodePackages_latest.vscode-json-languageserver From ad6a4548c6267c7a47dbd0d8ab8ac55ec53264f5 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:18:22 +0100 Subject: [PATCH 35/42] Capybara Steam and syncthing (#54) --- home-manager/capybara-config.nix | 13 +++++++++++++ home-manager/modules/zsh/zshrc | 1 + 2 files changed, 14 insertions(+) diff --git a/home-manager/capybara-config.nix b/home-manager/capybara-config.nix index e38f93c..ba8e8c4 100644 --- a/home-manager/capybara-config.nix +++ b/home-manager/capybara-config.nix @@ -3,6 +3,7 @@ config, ... }: { + nixpkgs.config.allowUnfree = true; imports = [ ../hardware/capybara.nix ]; @@ -30,12 +31,19 @@ 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 = '' @@ -54,4 +62,9 @@ keep-outputs = true keep-derivations = true ''; + + programs.steam = { + enable = true; + remotePlay.openFirewall = true; + }; } diff --git a/home-manager/modules/zsh/zshrc b/home-manager/modules/zsh/zshrc index a89f648..ec173d4 100644 --- a/home-manager/modules/zsh/zshrc +++ b/home-manager/modules/zsh/zshrc @@ -27,5 +27,6 @@ export WORDCHARS='' autoload edit-command-line zle -N edit-command-line bindkey '^X^E' edit-command-line +bindkey -e PATH="$PATH:$HOME/.cargo/bin" From 5536c974930db284e1ef1933f2436e0da118f50b Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sun, 28 Apr 2024 22:40:55 +0100 Subject: [PATCH 36/42] Qualify path to cat (#55) --- home-manager/modules/mail.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/home-manager/modules/mail.nix b/home-manager/modules/mail.nix index 1d3ff2e..2a9ca8e 100644 --- a/home-manager/modules/mail.nix +++ b/home-manager/modules/mail.nix @@ -100,7 +100,7 @@ in { create = "maildir"; }; realName = "Patrick Stevens"; - passwordCommand = "cat ${secretsPath}/btinternet.txt"; + passwordCommand = "${pkgs.coreutils}/bin/cat ${secretsPath}/btinternet.txt"; smtp = { host = "mail.btinternet.com"; port = 465; @@ -143,7 +143,7 @@ in { passwordCommand = # I store the ProtonMail Bridge password here. # Extracting it from a keychain would be better. - "cat ${secretsPath}/proton.txt"; + "${pkgs.coreutils}/bin/cat ${secretsPath}/proton.txt"; smtp = { host = "127.0.0.1"; port = 1025; # 8126; if using hydroxide From cde0abe5d9d22e6814211f349ba4beca05e8bb5b Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sun, 28 Apr 2024 22:42:54 +0100 Subject: [PATCH 37/42] Add calendar backup job (#56) --- darwin-configuration.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/darwin-configuration.nix b/darwin-configuration.nix index ecf2c82..6099d95 100644 --- a/darwin-configuration.nix +++ b/darwin-configuration.nix @@ -57,6 +57,16 @@ in { RunAtLoad = true; }; }; + + backup-calendar = { + command = ''${pkgs.bash}/bin/bash -c "mkdir -p /Users/patrick/Backups && if [ ! -d /Users/patrick/Backups/radicale ] ; then ${pkgs.git}/bin/git clone root@patrickstevens.co.uk:/preserve/radicale/data/.git /Users/patrick/Backups/radicale >/tmp/radicale.out.log 2>/tmp/radicale.err.log; fi && ${pkgs.git}/bin/git --git-dir /Users/patrick/Backups/radicale/.git pull 2>>/tmp/radicale.err.log"''; + serviceConfig = { + KeepAlive = false; + UserName = "patrick"; + StartInterval = 3600; + RunAtLoad = true; + }; + }; }; # Auto upgrade nix package and the daemon service. From 8752e0a72072565493951189c45b988c1053a8fe Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 4 May 2024 00:10:38 +0100 Subject: [PATCH 38/42] Update Ionide (#57) --- flake.lock | 80 ++++++++++++++++---------------- home-manager/nvim/ionide-vim.lua | 25 ++-------- 2 files changed, 44 insertions(+), 61 deletions(-) diff --git a/flake.lock b/flake.lock index 44d4a9c..2e9fec2 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1712279577, - "narHash": "sha256-Bwn4rmQi2L2iX6g3ycQMA4baE3zgPHAO0xPBpr2T4/k=", + "lastModified": 1714264490, + "narHash": "sha256-5hKsAO7ZLwOLCXeI5+jDmDr7t4zBezk0xidVWghoBB0=", "owner": "tpwrules", "repo": "nixos-apple-silicon", - "rev": "d47afc3f0f8b3078c818da8609c41340af61a2ec", + "rev": "4fac534b775aa0c40611257fa19ab8ab3243f4dc", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1711763326, - "narHash": "sha256-sXcesZWKXFlEQ8oyGHnfk4xc9f2Ip0X/+YZOq3sKviI=", + "lastModified": 1713946171, + "narHash": "sha256-lc75rgRQLdp4Dzogv5cfqOg6qYc5Rp83oedF2t0kDp8=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "36524adc31566655f2f4d55ad6b875fb5c1a4083", + "rev": "230a197063de9287128e2c68a7a4b0cd7d0b50a7", "type": "github" }, "original": { @@ -50,11 +50,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1712941527, - "narHash": "sha256-wD9XQFGW0qzRW1YHj6oklCHzgKNxjwS0tZ/hFGgiHX4=", + "lastModified": 1714755945, + "narHash": "sha256-N44WGoXubIaUwrlc7N2XIkS8pzxprWfyWHWBI1en1kM=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "9f4406718ada7af83892e17355ef7fd202c20897", + "rev": "d72860d651e27709884a8a2ef85f1e99e5eae21c", "type": "github" }, "original": { @@ -102,11 +102,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1714641030, + "narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e", "type": "github" }, "original": { @@ -124,11 +124,11 @@ ] }, "locked": { - "lastModified": 1709336216, - "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", "type": "github" }, "original": { @@ -159,11 +159,11 @@ "systems": "systems_2" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -199,11 +199,11 @@ ] }, "locked": { - "lastModified": 1710478346, - "narHash": "sha256-Xjf8BdnQG0tLhPMlqQdwCIjOp7Teox0DP3N/jjyiGM4=", + "lastModified": 1713898448, + "narHash": "sha256-6q6ojsp/Z9P2goqnxyfCSzFOD92T3Uobmj8oVAicUOs=", "owner": "hercules-ci", "repo": "hercules-ci-effects", - "rev": "64e7763d72c1e4c1e5e6472640615b6ae2d40fbf", + "rev": "c0302ec12d569532a6b6bd218f698bc402e93adc", "type": "github" }, "original": { @@ -219,11 +219,11 @@ ] }, "locked": { - "lastModified": 1712759992, - "narHash": "sha256-2APpO3ZW4idlgtlb8hB04u/rmIcKA8O7pYqxF66xbNY=", + "lastModified": 1714679908, + "narHash": "sha256-KzcXzDvDJjX34en8f3Zimm396x6idbt+cu4tWDVS2FI=", "owner": "nix-community", "repo": "home-manager", - "rev": "31357486b0ef6f4e161e002b6893eeb4fafc3ca9", + "rev": "9036fe9ef8e15a819fa76f47a8b1f287903fb848", "type": "github" }, "original": { @@ -254,11 +254,11 @@ }, "locked": { "dir": "contrib", - "lastModified": 1712877603, - "narHash": "sha256-8JesAgnsv1bD+xHNoqefz0Gv243wSiCKnzh4rhZLopU=", + "lastModified": 1714683427, + "narHash": "sha256-SMfFU+VsRTZLVIkGpf67oOTZ29gWmFvxF0nGO6CRx/4=", "owner": "neovim", "repo": "neovim", - "rev": "18ee9f9e7dbbc9709ee9c1572870b4ad31443569", + "rev": "01e4a70d668d54a7cefa3ff53ec97e39df516265", "type": "github" }, "original": { @@ -279,11 +279,11 @@ ] }, "locked": { - "lastModified": 1712880226, - "narHash": "sha256-2CGLzsFft8zF/gEY4qDN0uAjRCWUqvNJ9yV118NlzTg=", + "lastModified": 1714694802, + "narHash": "sha256-b0+Zrd2PDgRIEeeXbivzw3kcSaXCZItOvgOgdfRsyOo=", "owner": "nix-community", "repo": "neovim-nightly-overlay", - "rev": "58d367a1924bf0d02bcc5bd2c5af8ac97f178381", + "rev": "9b2c33c7fa0287db93868d955e7b3d0da3837a57", "type": "github" }, "original": { @@ -294,27 +294,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1712163089, - "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", + "lastModified": 1714076141, + "narHash": "sha256-Drmja/f5MRHZCskS6mvzFqxEaZMeciScCTFxWVLqWEY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", + "rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", + "rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1712741485, - "narHash": "sha256-bCs0+MSTra80oXAsnM6Oq62WsirOIaijQ/BbUY59tR4=", + "lastModified": 1714531828, + "narHash": "sha256-ILsf3bdY/hNNI/Hu5bSt2/KbmHaAVhBbNUOdGztTHEg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b2cf36f43f9ef2ded5711b30b1f393ac423d8f72", + "rev": "0638fe2715d998fa81d173aad264eb671ce2ebc1", "type": "github" }, "original": { @@ -326,11 +326,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1712849433, - "narHash": "sha256-flQtf/ZPJgkLY/So3Fd+dGilw2DKIsiwgMEn7BbBHL0=", + "lastModified": 1714656196, + "narHash": "sha256-kjQkA98lMcsom6Gbhw8SYzmwrSo+2nruiTcTZp5jK7o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f173d0881eff3b21ebb29a2ef8bedbc106c86ea5", + "rev": "94035b482d181af0a0f8f77823a790b256b7c3cc", "type": "github" }, "original": { diff --git a/home-manager/nvim/ionide-vim.lua b/home-manager/nvim/ionide-vim.lua index 1e33b51..539db58 100644 --- a/home-manager/nvim/ionide-vim.lua +++ b/home-manager/nvim/ionide-vim.lua @@ -2,23 +2,6 @@ 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) @@ -76,7 +59,7 @@ local function BuildFSharpProjects(projects) end if not projects then - projects = captureLoadedProjects() + projects = vim.fn['fsharp#getLoadedProjects']() end if projects then local total_projects = 0 @@ -107,7 +90,7 @@ vim.api.nvim_create_user_command("BuildFSharpProject", function(opts) .new({}, { prompt_title = "Projects", finder = finders.new_table({ - results = captureLoadedProjects(), + results = vim.fn['fsharp#getLoadedProjects'](), }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_buf, _) @@ -173,7 +156,7 @@ vim.api.nvim_create_user_command("RunFSharpProject", function(opts) .new({}, { prompt_title = "Projects", finder = finders.new_table({ - results = captureLoadedProjects(), + results = vim.fn['fsharp#getLoadedProjects'](), }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_buf, _) @@ -202,7 +185,7 @@ vim.api.nvim_create_user_command("PublishFSharpProject", function(opts) .new({}, { prompt_title = "Projects", finder = finders.new_table({ - results = captureLoadedProjects(), + results = vim.fn['fsharp#getLoadedProjects'](), }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_buf, _) From 1fc72d0288a8641205fa1e6036ef2c04816223b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 08:24:50 +0100 Subject: [PATCH 39/42] Bump cachix/install-nix-action from 26 to 27 (#58) Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 26 to 27. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v26...V27) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 48483bf..4378424 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -13,7 +13,7 @@ jobs: - name: "Checkout" uses: "actions/checkout@v4" - name: "Install Nix" - uses: "cachix/install-nix-action@v26" + uses: "cachix/install-nix-action@V27" with: { "extra_nix_config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" } - name: "Check flake" run: "nix flake check --all-systems" From 29d80dec16a419271168544f71a80ccfc0c7789b Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 27 May 2024 17:55:10 +0100 Subject: [PATCH 40/42] Neovim non nightly (#59) --- flake.lock | 208 ++++--------------------------------- flake.nix | 7 +- home-manager/home.nix | 33 ++---- home-manager/nvim/init.lua | 1 - 4 files changed, 31 insertions(+), 218 deletions(-) diff --git a/flake.lock b/flake.lock index 2e9fec2..7b5b96a 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1714264490, - "narHash": "sha256-5hKsAO7ZLwOLCXeI5+jDmDr7t4zBezk0xidVWghoBB0=", + "lastModified": 1716699938, + "narHash": "sha256-AzTYm22tTDJy0tSqMDf95rmCxxoGTPClu0paGPeh5a0=", "owner": "tpwrules", "repo": "nixos-apple-silicon", - "rev": "4fac534b775aa0c40611257fa19ab8ab3243f4dc", + "rev": "842306255856d7f5677c113e699101eb253e2e3f", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1713946171, - "narHash": "sha256-lc75rgRQLdp4Dzogv5cfqOg6qYc5Rp83oedF2t0kDp8=", + "lastModified": 1716511055, + "narHash": "sha256-5Fe/DGgvMhPEMl9VdVxv3zvwRcwNDmW5eRJ0gk72w7U=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "230a197063de9287128e2c68a7a4b0cd7d0b50a7", + "rev": "0bea8222f6e83247dd13b055d83e64bce02ee532", "type": "github" }, "original": { @@ -50,11 +50,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1714755945, - "narHash": "sha256-N44WGoXubIaUwrlc7N2XIkS8pzxprWfyWHWBI1en1kM=", + "lastModified": 1716800073, + "narHash": "sha256-ZznQFA/Mjomt1phpfwVFtp3F2b6yvlyslmjJ6bgBB5w=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "d72860d651e27709884a8a2ef85f1e99e5eae21c", + "rev": "165e4bc50493e402cf296c413edb479b04aeb339", "type": "github" }, "original": { @@ -78,64 +78,6 @@ "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": 1714641030, - "narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e", - "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": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", - "type": "github" - }, - "original": { - "id": "flake-parts", - "type": "indirect" - } - }, "flake-utils": { "inputs": { "systems": "systems" @@ -158,24 +100,6 @@ "inputs": { "systems": "systems_2" }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_3": { - "inputs": { - "systems": "systems_3" - }, "locked": { "lastModified": 1701680307, "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", @@ -190,28 +114,6 @@ "type": "github" } }, - "hercules-ci-effects": { - "inputs": { - "flake-parts": "flake-parts_2", - "nixpkgs": [ - "neovim-nightly", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1713898448, - "narHash": "sha256-6q6ojsp/Z9P2goqnxyfCSzFOD92T3Uobmj8oVAicUOs=", - "owner": "hercules-ci", - "repo": "hercules-ci-effects", - "rev": "c0302ec12d569532a6b6bd218f698bc402e93adc", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "hercules-ci-effects", - "type": "github" - } - }, "home-manager": { "inputs": { "nixpkgs": [ @@ -219,11 +121,11 @@ ] }, "locked": { - "lastModified": 1714679908, - "narHash": "sha256-KzcXzDvDJjX34en8f3Zimm396x6idbt+cu4tWDVS2FI=", + "lastModified": 1716736760, + "narHash": "sha256-h3RmnNknKYtVA+EvUSra6QAwfZjC2q1G8YA7W0gat8Y=", "owner": "nix-community", "repo": "home-manager", - "rev": "9036fe9ef8e15a819fa76f47a8b1f287903fb848", + "rev": "5d151429e1e79107acf6d06dcc5ace4e642ec239", "type": "github" }, "original": { @@ -244,77 +146,29 @@ "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": 1714683427, - "narHash": "sha256-SMfFU+VsRTZLVIkGpf67oOTZ29gWmFvxF0nGO6CRx/4=", - "owner": "neovim", - "repo": "neovim", - "rev": "01e4a70d668d54a7cefa3ff53ec97e39df516265", - "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": 1714694802, - "narHash": "sha256-b0+Zrd2PDgRIEeeXbivzw3kcSaXCZItOvgOgdfRsyOo=", - "owner": "nix-community", - "repo": "neovim-nightly-overlay", - "rev": "9b2c33c7fa0287db93868d955e7b3d0da3837a57", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "neovim-nightly-overlay", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1714076141, - "narHash": "sha256-Drmja/f5MRHZCskS6mvzFqxEaZMeciScCTFxWVLqWEY=", + "lastModified": 1716293225, + "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", "owner": "nixos", "repo": "nixpkgs", - "rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856", + "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856", + "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1714531828, - "narHash": "sha256-ILsf3bdY/hNNI/Hu5bSt2/KbmHaAVhBbNUOdGztTHEg=", + "lastModified": 1716633019, + "narHash": "sha256-xim1b5/HZYbWaZKyI7cn9TJCM6ewNVZnesRr00mXeS4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0638fe2715d998fa81d173aad264eb671ce2ebc1", + "rev": "9d29cd266cebf80234c98dd0b87256b6be0af44e", "type": "github" }, "original": { @@ -326,11 +180,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1714656196, - "narHash": "sha256-kjQkA98lMcsom6Gbhw8SYzmwrSo+2nruiTcTZp5jK7o=", + "lastModified": 1716715802, + "narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "94035b482d181af0a0f8f77823a790b256b7c3cc", + "rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f", "type": "github" }, "original": { @@ -361,7 +215,6 @@ "darwin": "darwin", "emacs": "emacs", "home-manager": "home-manager", - "neovim-nightly": "neovim-nightly", "nixpkgs": "nixpkgs_2", "whisper": "whisper" } @@ -412,24 +265,9 @@ "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_3", + "flake-utils": "flake-utils_2", "model": "model", "nixpkgs": "nixpkgs_3" }, diff --git a/flake.nix b/flake.nix index 73cc4e3..8270525 100644 --- a/flake.nix +++ b/flake.nix @@ -24,14 +24,9 @@ whisper = { url = "github:Smaug123/whisper.cpp/nix"; }; - neovim-nightly = { - url = "github:nix-community/neovim-nightly-overlay"; - inputs.nixpkgs.follows = "nixpkgs"; - }; }; outputs = { - neovim-nightly, darwin, emacs, nixpkgs, @@ -46,7 +41,7 @@ }; systems = ["aarch64-darwin" "aarch64-linux" "x86_64-linux"]; in let - overlays = [emacs.overlay neovim-nightly.overlay]; + overlays = [emacs.overlay]; recursiveMerge = attrList: let f = attrPath: builtins.zipAttrsWith (n: values: diff --git a/home-manager/home.nix b/home-manager/home.nix index ab1de40..f177cd8 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -125,29 +125,6 @@ }; 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 - pynvimpp - ps.pyyaml - ps.std2 - ]); debugPyEnv = nixpkgs.python3.withPackages (ps: [ps.debugpy]); in { enable = true; @@ -257,11 +234,15 @@ vimAlias = true; vimdiffAlias = true; withPython3 = true; + extraPython3Packages = ps: [ + ps.pynvim + ps.pynvim-pp + ps.pyyaml + ps.std2 + ]; withRuby = true; - extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.replaceStrings ["%PYTHONENV%"] ["${pythonEnv}"] (builtins.readFile ./nvim/init.lua) + "\n" + builtins.readFile ./nvim/python.lua; - - package = nixpkgs.neovim-nightly; + extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.readFile ./nvim/init.lua + "\n" + builtins.readFile ./nvim/python.lua; }; home.packages = [ diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index 56a5d06..de68b2b 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -1,4 +1,3 @@ -vim.g.python3_host_prog = "%PYTHONENV%/bin/python" vim.opt.mouse = "" vim.opt.history = 500 vim.opt.background = "dark" From 162b58abc41b75089d92ed7fbd3a2d61b64f3708 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 27 May 2024 18:03:47 +0100 Subject: [PATCH 41/42] Difftastic as Git diff tool (#60) --- home-manager/home.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/home-manager/home.nix b/home-manager/home.nix index f177cd8..148613e 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -54,7 +54,7 @@ co = "checkout"; st = "status"; }; - delta = {enable = true;}; + difftastic.enable = true; extraConfig = { commit.gpgsign = true; gpg.program = "${nixpkgs.gnupg}/bin/gpg"; @@ -246,6 +246,7 @@ }; home.packages = [ + nixpkgs.difftastic nixpkgs.syncthing nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs nixpkgs.nodePackages_latest.bash-language-server From 9649e2e37b9392065733f3aeed3008b675ece8db Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 31 May 2024 21:33:33 +0100 Subject: [PATCH 42/42] Add .NET project filetypes in nvim (#61) --- home-manager/nvim/init.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index de68b2b..71b70d8 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -287,3 +287,10 @@ whichkey.register({ "Find files by name", }, }, { prefix = vim.api.nvim_get_var("mapleader") }) + +vim.api.nvim_create_autocmd({"BufRead","BufNewFile"}, { + pattern = {"Directory.Build.props", "*.fsproj", "*.csproj"}, + callback = function() + vim.bo.filetype = "xml" + end, +})