diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4378424..4fc3e2e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -13,14 +13,16 @@ jobs: - name: "Checkout" uses: "actions/checkout@v4" - name: "Install Nix" - uses: "cachix/install-nix-action@V27" + uses: "cachix/install-nix-action@v30" with: { "extra_nix_config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" } - name: "Check flake" run: "nix flake check --all-systems" all-required-checks-complete: + if: ${{ always() }} runs-on: "ubuntu-latest" steps: - - run: "echo \"All required checks complete.\"" - needs: - - "flake-check" + - uses: Smaug123/all-required-checks-complete-action@05b40a8c47ef0b175ea326e9abb09802cb67b44e + with: + needs-context: ${{ toJSON(needs) }} + needs: [ "flake-check" ] diff --git a/darwin-configuration.nix b/darwin-configuration.nix index 6099d95..4668fe6 100644 --- a/darwin-configuration.nix +++ b/darwin-configuration.nix @@ -53,13 +53,14 @@ in { serviceConfig = { KeepAlive = false; UserName = "patrick"; - StartInterval = 60; + # Refresh token is 60min long, so do this more often than that! + StartInterval = 30; RunAtLoad = true; }; }; backup-calendar = { - command = ''${pkgs.bash}/bin/bash -c "mkdir -p /Users/patrick/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"''; + command = ''${pkgs.bash}/bin/bash -c "mkdir -p '/Users/patrick/Library/Application Support/RadicaleBackups' && if [ ! -d '/Users/patrick/Library/Application Support/RadicaleBackups/.git' ] ; then ${pkgs.git}/bin/git clone root@patrickstevens.co.uk:/preserve/radicale/data/.git '/Users/patrick/Library/Application Support/RadicaleBackups' >/tmp/radicale.out.log 2>/tmp/radicale.err.log; fi && ${pkgs.git}/bin/git --git-dir '/Users/patrick/Library/Application Support/RadicaleBackups/.git' --work-tree '/Users/patrick/Library/Application Support/RadicaleBackups/' pull 2>>/tmp/radicale.err.log"''; serviceConfig = { KeepAlive = false; UserName = "patrick"; @@ -67,6 +68,46 @@ in { RunAtLoad = true; }; }; + + sync-nixpkgs = { + command = ''${pkgs.bash}/bin/bash -c "if [ -d /Users/patrick/Documents/GitHub/nixpkgs ] ; then ${pkgs.git}/bin/git --git-dir /Users/patrick/Documents/GitHub/nixpkgs/.git --work-tree '/Users/patrick/Documents/GitHub/nixpkgs/' fetch origin ; fi"''; + serviceConfig = { + KeepAlive = false; + UserName = "patrick"; + StartInterval = 36000; + RunAtLoad = true; + }; + }; + + sync-dotnet-api-docs = { + command = ''${pkgs.bash}/bin/bash -c "if [ -d /Users/patrick/Documents/GitHub/dotnet-api-docs ] ; then ${pkgs.git}/bin/git --git-dir /Users/patrick/Documents/GitHub/dotnet-api-docs/.git --work-tree '/Users/patrick/Documents/GitHub/dotnet-api-docs' fetch origin ; fi"''; + serviceConfig = { + KeepAlive = false; + UserName = "patrick"; + StartInterval = 36000; + RunAtLoad = true; + }; + }; + + sync-dotnet-docs = { + command = ''${pkgs.bash}/bin/bash -c "if [ -d /Users/patrick/Documents/GitHub/dotnet-docs ] ; then ${pkgs.git}/bin/git --git-dir /Users/patrick/Documents/GitHub/dotnet-docs/.git --work-tree '/Users/patrick/Documents/GitHub/dotnet-docs' fetch origin ; fi"''; + serviceConfig = { + KeepAlive = false; + UserName = "patrick"; + StartInterval = 36000; + RunAtLoad = true; + }; + }; + + nix-store-optimise = { + command = ''${pkgs.nix}/bin/nix store optimise''; + serviceConfig = { + KeepAlive = false; + UserName = "patrick"; + StartInterval = 72000; + RunAtLoad = true; + }; + }; }; # Auto upgrade nix package and the daemon service. @@ -77,8 +118,9 @@ in { # Sandbox causes failure: https://github.com/NixOS/nix/issues/4119 nix.settings.sandbox = false; + # Optimising store leads to transient build failures https://github.com/NixOS/nix/issues/7273 nix.extraOptions = '' - auto-optimise-store = true + auto-optimise-store = false experimental-features = nix-command flakes extra-experimental-features = ca-derivations max-jobs = auto # Allow building multiple derivations in parallel diff --git a/flake.lock b/flake.lock index 7b5b96a..acb18dc 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1716699938, - "narHash": "sha256-AzTYm22tTDJy0tSqMDf95rmCxxoGTPClu0paGPeh5a0=", + "lastModified": 1725418254, + "narHash": "sha256-2zPzPP9Eu5NxgJxTVcuCCX5xh7CWy7rYaLHfaAZS6H8=", "owner": "tpwrules", "repo": "nixos-apple-silicon", - "rev": "842306255856d7f5677c113e699101eb253e2e3f", + "rev": "c5f944f49a052232015bb3c03524b69e3fdd2aa4", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1716511055, - "narHash": "sha256-5Fe/DGgvMhPEMl9VdVxv3zvwRcwNDmW5eRJ0gk72w7U=", + "lastModified": 1725628909, + "narHash": "sha256-xI0OSqPHcs/c/utJsU0Zvcp1VhejMI9mgwr68uHHlPs=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "0bea8222f6e83247dd13b055d83e64bce02ee532", + "rev": "76559183801030451e200c90a1627c1d82bb4910", "type": "github" }, "original": { @@ -50,11 +50,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1716800073, - "narHash": "sha256-ZznQFA/Mjomt1phpfwVFtp3F2b6yvlyslmjJ6bgBB5w=", + "lastModified": 1725786744, + "narHash": "sha256-Rqq4YMe9PZL9lT5gz9nJTYq1U6KZvl485dmk7NwubDc=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "165e4bc50493e402cf296c413edb479b04aeb339", + "rev": "6ab2b1aa86b3cde735a33d3670b5d2e2f288c4da", "type": "github" }, "original": { @@ -121,11 +121,11 @@ ] }, "locked": { - "lastModified": 1716736760, - "narHash": "sha256-h3RmnNknKYtVA+EvUSra6QAwfZjC2q1G8YA7W0gat8Y=", + "lastModified": 1725781935, + "narHash": "sha256-o6LRtdpgBTzev9n243Ktu3rn0/qsv0frFyJwU6vJsdE=", "owner": "nix-community", "repo": "home-manager", - "rev": "5d151429e1e79107acf6d06dcc5ace4e642ec239", + "rev": "ec4c6928bbacc89cf10e9c959a7a47cbaad95344", "type": "github" }, "original": { @@ -148,43 +148,43 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716293225, - "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", + "lastModified": 1725103162, + "narHash": "sha256-Ym04C5+qovuQDYL/rKWSR+WESseQBbNAe5DsXNx5trY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", + "rev": "12228ff1752d7b7624a54e9c1af4b222b3c1073b", "type": "github" }, "original": { "owner": "nixos", + "ref": "nixos-unstable", "repo": "nixpkgs", - "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1716633019, - "narHash": "sha256-xim1b5/HZYbWaZKyI7cn9TJCM6ewNVZnesRr00mXeS4=", + "lastModified": 1725407940, + "narHash": "sha256-tiN5Rlg/jiY0tyky+soJZoRzLKbPyIdlQ77xVgREDNM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9d29cd266cebf80234c98dd0b87256b6be0af44e", + "rev": "6f6c45b5134a8ee2e465164811e451dcb5ad86e3", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1716715802, - "narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=", + "lastModified": 1725534445, + "narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f", + "rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8270525..9e4d91d 100644 --- a/flake.nix +++ b/flake.nix @@ -63,6 +63,10 @@ in nixpkgs.lib.nixosSystem { inherit system; + specialArgs = { + username = "patrick"; + dotnet = pkgs.dotnet-sdk_8; + }; modules = let args = { nixpkgs = pkgs; @@ -70,6 +74,7 @@ dotnet = pkgs.dotnet-sdk_8; mbsync = import ./mbsync.nix {inherit pkgs;}; secretsPath = "/home/patrick/.secrets/"; + machinename = "capybara"; }; in [ ./home-manager/capybara-config.nix @@ -95,6 +100,7 @@ dotnet = pkgs.dotnet-sdk_8; mbsync = import ./mbsync.nix {inherit pkgs;}; secretsPath = "/home/patrick/.secrets/"; + machinename = "earthworm"; }; in [ ./home-manager/earthworm-config.nix @@ -124,6 +130,7 @@ whisper = whisper.packages.${system}; mbsync = import ./mbsync.nix {inherit pkgs;}; secretsPath = "/Users/patrick/.secrets/"; + machinename = "darwin"; }; in [ ./darwin-configuration.nix diff --git a/hardware/capybara.nix b/hardware/capybara.nix index 77491b5..1439291 100644 --- a/hardware/capybara.nix +++ b/hardware/capybara.nix @@ -12,9 +12,9 @@ (modulesPath + "/installer/scan/not-detected.nix") ]; - boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod"]; + boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod"]; boot.initrd.kernelModules = []; - boot.kernelModules = ["kvm-intel"]; + boot.kernelModules = ["kvm-amd"]; boot.extraModulePackages = []; fileSystems."/" = { @@ -23,12 +23,21 @@ }; fileSystems."/boot" = { - device = "/dev/nvme0n1p2"; + device = "/dev/disk/by-uuid/9248-31C6"; fsType = "vfat"; + options = ["fmask=0022" "dmask=0022"]; }; swapDevices = []; - powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eno1.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp9s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; } diff --git a/home-manager/capybara-config.nix b/home-manager/capybara-config.nix index ba8e8c4..8fa1f47 100644 --- a/home-manager/capybara-config.nix +++ b/home-manager/capybara-config.nix @@ -1,6 +1,8 @@ { pkgs, config, + username, + dotnet, ... }: { nixpkgs.config.allowUnfree = true; @@ -8,6 +10,23 @@ ../hardware/capybara.nix ]; + hardware.graphics = { + enable = true; + }; + + services.xserver.videoDrivers = ["nvidia"]; + + hardware.nvidia = { + modesetting.enable = true; + powerManagement.enable = false; + + # I don't have a Turing GPU + powerManagement.finegrained = false; + + open = false; + nvidiaSettings = true; + }; + boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; boot.loader.grub.useOSProber = true; @@ -38,16 +57,16 @@ }; environment.systemPackages = [ + pkgs.git pkgs.vim pkgs.wget pkgs.tmux pkgs.home-manager pkgs.firefox - pkgs.steam-run ]; environment.loginShellInit = '' - [[ "$(tty)" == /dev/tty1 ]] && sway + [[ "$(tty)" == /dev/tty1 ]] && sway --unsupported-gpu ''; services.openssh.enable = true; diff --git a/home-manager/home.nix b/home-manager/home.nix index ff73851..054b91c 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -1,5 +1,6 @@ { nixpkgs, + machinename, username, mbsync, dotnet, @@ -58,7 +59,12 @@ extraConfig = { commit.gpgsign = true; gpg.program = "${nixpkgs.gnupg}/bin/gpg"; - user.signingkey = "7C97D679CF3BC4F9"; + user.signingkey = + if machinename == "darwin" || machinename == "earthworm" + then "7C97D679CF3BC4F9" + else if machinename == "capybara" + then "AE90453E879DBCFA" + else throw "unrecognised machine name!"; core = { autocrlf = "input"; }; @@ -129,6 +135,15 @@ in { enable = true; plugins = [ + { + plugin = nixpkgs.vimPlugins.nvim-web-devicons; + } + { + plugin = nixpkgs.vimPlugins.mini-nvim; + } + { + plugin = nixpkgs.vimPlugins.satellite-nvim; + } { plugin = nixpkgs.vimPlugins.nvim-lightbulb; type = "lua"; @@ -235,6 +250,7 @@ vimdiffAlias = true; withPython3 = true; extraPython3Packages = ps: [ + ps.pip ps.pynvim ps.pynvim-pp ps.pyyaml @@ -242,56 +258,78 @@ ]; withRuby = true; - extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + builtins.readFile ./nvim/dotnet.lua + "\n" + builtins.readFile ./nvim/init.lua + "\n" + builtins.readFile ./nvim/python.lua; + extraLuaConfig = builtins.readFile ./nvim/build-utils.lua + "\n" + (builtins.replaceStrings ["_CURL_"] ["${nixpkgs.curl}/bin/curl"] (builtins.readFile ./nvim/dotnet.lua)) + "\n" + builtins.readFile ./nvim/init.lua + "\n" + builtins.readFile ./nvim/python.lua; }; - home.packages = [ - nixpkgs.difftastic - nixpkgs.syncthing - nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs - nixpkgs.nodePackages_latest.bash-language-server - nixpkgs.nodePackages_latest.vscode-json-languageserver - nixpkgs.nodePackages_latest.vscode-langservers-extracted - nixpkgs.hadolint - nixpkgs.ltex-ls - nixpkgs.yaml-language-server - nixpkgs.csharp-ls - nixpkgs.netcoredbg - nixpkgs.nil - nixpkgs.fsautocomplete - nixpkgs.keepassxc - nixpkgs.wget - nixpkgs.yt-dlp - nixpkgs.cmake - nixpkgs.gnumake - nixpkgs.gcc - nixpkgs.lldb - nixpkgs.hledger - nixpkgs.hledger-web - dotnet - nixpkgs.jitsi-meet - nixpkgs.elan - nixpkgs.coreutils-prefixed - nixpkgs.shellcheck - nixpkgs.universal-ctags - nixpkgs.asciinema - nixpkgs.git-lfs - nixpkgs.imagemagick - nixpkgs.nixpkgs-fmt - nixpkgs.lnav - nixpkgs.age - nixpkgs.nodejs - nixpkgs.nodePackages.pyright - nixpkgs.woodpecker-agent - nixpkgs.lynx - nixpkgs.alejandra - nixpkgs.ffmpeg - nixpkgs.bat - nixpkgs.pandoc - nixpkgs.fd - nixpkgs.sumneko-lua-language-server - nixpkgs.gnupg - ]; + home.packages = + [ + nixpkgs.jq + nixpkgs.difftastic + nixpkgs.syncthing + nixpkgs.nodePackages_latest.dockerfile-language-server-nodejs + nixpkgs.nodePackages_latest.bash-language-server + nixpkgs.nodePackages_latest.vscode-json-languageserver + nixpkgs.nodePackages_latest.vscode-langservers-extracted + nixpkgs.hadolint + nixpkgs.yaml-language-server + nixpkgs.csharp-ls + nixpkgs.netcoredbg + nixpkgs.nil + nixpkgs.fsautocomplete + nixpkgs.keepassxc + nixpkgs.wget + nixpkgs.yt-dlp + nixpkgs.cmake + nixpkgs.gnumake + nixpkgs.gcc + nixpkgs.lldb + nixpkgs.hledger + nixpkgs.hledger-web + dotnet + nixpkgs.elan + nixpkgs.coreutils-prefixed + nixpkgs.shellcheck + nixpkgs.universal-ctags + nixpkgs.asciinema + nixpkgs.git-lfs + nixpkgs.imagemagick + nixpkgs.nixpkgs-fmt + nixpkgs.lnav + nixpkgs.age + nixpkgs.nodejs + nixpkgs.pyright + nixpkgs.woodpecker-agent + nixpkgs.lynx + nixpkgs.alejandra + nixpkgs.ffmpeg + nixpkgs.bat + nixpkgs.pandoc + nixpkgs.fd + nixpkgs.sumneko-lua-language-server + nixpkgs.gnupg + nixpkgs.gh + nixpkgs.clang-tools + nixpkgs.deno + nixpkgs.yazi + ] + ++ ( + if nixpkgs.stdenv.isLinux + then [ + nixpkgs.protonmail-bridge + nixpkgs.pinentry + nixpkgs.signal-desktop + ] + else [] + ) + ++ ( + if machinename == "capybara" + then [ + nixpkgs.steam-run + nixpkgs.discord + nixpkgs.anki-bin + ] + else [] + ); home.file.".ideavimrc".source = ./ideavimrc; home.file.".config/yt-dlp/config".source = ./youtube-dl.conf; diff --git a/home-manager/linux.nix b/home-manager/linux.nix index 60c1e1f..536b7c5 100644 --- a/home-manager/linux.nix +++ b/home-manager/linux.nix @@ -4,7 +4,7 @@ dotnet, ... }: { - home.packages = [nixpkgs.firefox-wayland nixpkgs.wl-clipboard]; + home.packages = [nixpkgs.firefox-wayland nixpkgs.wl-clipboard nixpkgs.jetbrains.rider]; nixpkgs.config.firefox = { speechSynthesisSupport = true; }; @@ -22,6 +22,11 @@ ''; }; + services.gpg-agent = { + enable = nixpkgs.stdenv.isLinux; + pinentryPackage = nixpkgs.pinentry-qt; + }; + services.swayidle = {enable = true;}; services.cbatticon = { lowLevelPercent = 20; diff --git a/home-manager/modules/mail.nix b/home-manager/modules/mail.nix index 2a9ca8e..5e07a14 100644 --- a/home-manager/modules/mail.nix +++ b/home-manager/modules/mail.nix @@ -75,7 +75,7 @@ in { # 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''; + passwordCommand = ''${pkgs.python3}/bin/python ${./mail/mutt-oauth2.py} ${secretsPath}/gmail.txt 2>/tmp/gmail-passcmd.2.txt''; realName = "Patrick Stevens"; }; @@ -149,7 +149,10 @@ in { port = 1025; # 8126; if using hydroxide tls = {enable = false;}; }; - userName = address; + userName = + if pkgs.stdenv.isLinux + then builtins.head (pkgs.lib.strings.splitString "@" address) + else address; }; programs.mbsync = { diff --git a/home-manager/nvim/dotnet.lua b/home-manager/nvim/dotnet.lua index eb117b2..1fe48bb 100644 --- a/home-manager/nvim/dotnet.lua +++ b/home-manager/nvim/dotnet.lua @@ -73,13 +73,14 @@ function RegisterSolution(sln_path) end local whichkey = require("which-key") - whichkey.register({ - s = { - name = ".NET solution", - b = { BuildDotNetSolution, "Build .NET solution" }, - t = { TestDotNetSolution, "Test .NET solution" }, + whichkey.add({ + { + "s", + desc = ".NET solution", }, - }, { prefix = vim.api.nvim_get_var("maplocalleader"), buffer = vim.api.nvim_get_current_buf() }) + { "sb", BuildDotNetSolution, desc = "Build .NET solution" }, + { "st", TestDotNetSolution, desc = "Test .NET solution" }, + }, { buffer = vim.api.nvim_get_current_buf() }) end local function find_nearest_slns() @@ -153,8 +154,612 @@ vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, { }) vim.api.nvim_create_autocmd("FileType", { - pattern = { "fsharp", "cs" }, + pattern = { "fsharp", "cs", "fsharp_project" }, callback = function() FindAndRegisterSolution(false) end, }) + +-- For what I'm sure are reasons, Lua appears to have nothing in its standard library +---@generic K +---@generic V1 +---@generic V2 +---@param tbl table +---@param f fun(V1): V2 +---@return table +local function map(tbl, f) + local t = {} + for k, v in pairs(tbl) do + t[k] = f(v) + end + return t +end + +---@generic K +---@generic V +---@param tbl table +---@param f fun(V1): nil +local function iter(tbl, f) + for _, v in pairs(tbl) do + f(v) + end +end + +---@generic K +---@generic V +---@param tbl table +---@param predicate fun(V): boolean +---@return boolean, V +local function find(tbl, predicate) + for _, v in pairs(tbl) do + if predicate(v) then + return true, v + end + end + return false, nil +end + +---@class (exact) NuGetVersion +---@field major number +---@field minor number +---@field patch number +---@field suffix? string +local NuGetVersion = {} + +---@param v NuGetVersion +---@nodiscard +---@return string +local function nuGetVersionToString(v) + local s = tostring(v.major) .. "." .. tostring(v.minor) .. "." .. tostring(v.patch) + if v.suffix then + return s .. v.suffix + else + return s + end +end + +local function get_all_variables() + local variables = {} + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + for _, line in ipairs(lines) do + local var_name, var_value = line:match("<(%w+)>([^<]+)") + if var_name and var_value then + variables[var_name] = var_value + end + end + return variables +end + +local function resolve_variable(version, variables) + if version:match("^%$%((.+)%)$") then + local var_name = version:match("^%$%((.+)%)$") + return variables[var_name] or nil + end + return nil +end + +---@param v string +---@nodiscard +---@return NuGetVersion +local function parse_version(v) + local variables = get_all_variables() + local major, minor, patch, pre = v:match("(%d+)%.(%d+)%.(%d+)(.*)$") + if major == nil then + local resolved = resolve_variable(v, variables) + if resolved ~= nil then + return parse_version(resolved) + end + end + -- TODO: why does this type-check if you remove the field names? + return { + major = tonumber(major) or 0, + minor = tonumber(minor) or 0, + patch = tonumber(patch) or 0, + suffix = pre or nil, + } +end + +---@param a NuGetVersion +---@param b NuGetVersion +---@nodiscard +---@return boolean +local function compare_versions(a, b) + if a.major ~= b.major then + return a.major < b.major + elseif a.minor ~= b.minor then + return a.minor < b.minor + elseif a.patch ~= b.patch then + return a.patch < b.patch + elseif a.suffix and not b.suffix then + return false + elseif not a.suffix and b.suffix then + return true + else + return a.suffix < b.suffix + end +end + +---@param url string +---@nodiscard +local function curl_sync(url) + local command = string.format("_CURL_ --silent --compressed --fail '%s'", url) + local response = vim.fn.system(command) + if vim.v.shell_error ~= 0 then + print("Failed to fetch " .. url) + return nil + end + local success, decoded = pcall(vim.fn.json_decode, response) + if not success then + print("Failed to decode JSON from curl at " .. url) + return nil + end + return decoded +end + +---@param url string +---@param callback fun(table): nil +---@return nil +local function curl(url, callback) + local stdout = vim.uv.new_pipe(false) + local stdout_text = "" + local handle + handle, _ = vim.uv.spawn( + "_CURL_", + { args = { "--silent", "--compressed", "--fail", url }, stdio = { nil, stdout, nil } }, + vim.schedule_wrap(function(code, _) + stdout:read_stop() + stdout:close() + if handle and not handle:is_closing() then + handle:close() + end + if code ~= 0 then + print("Failed to fetch " .. url) + end + local success, decoded = pcall(vim.fn.json_decode, stdout_text) + if not success then + print("Failed to decode JSON from curl at " .. url .. "\n" .. stdout_text) + end + callback(decoded) + end) + ) + vim.uv.read_start(stdout, function(err, data) + assert(not err, err) + if data then + stdout_text = stdout_text .. data + end + end) +end + +local _nugetIndex +local _packageBaseAddress + +---@param callback fun(): nil +local function populate_nuget_api(callback) + if _nugetIndex ~= nil then + callback() + end + local url = string.format("https://api.nuget.org/v3/index.json") + local function handle(decoded) + local default_nuget_reg = "https://api.nuget.org/v3/registration5-semver1/" + local default_base_address = "https://api.nuget.org/v3-flatcontainer/" + + if not decoded then + print("Failed to fetch NuGet index; falling back to default") + _nugetIndex = default_nuget_reg + _packageBaseAddress = default_base_address + else + local resources = decoded["resources"] + if resources == nil then + print("Failed to parse: " .. decoded .. tostring(decoded)) + for k, v in pairs(decoded) do + print(k .. ": " .. tostring(v)) + end + callback() + return + end + + local resourceSuccess, regUrl = find(resources, function(o) + return o["@type"] == "RegistrationsBaseUrl/3.6.0" + end) + if not resourceSuccess then + print("Failed to find endpoint in NuGet index; falling back to default") + _nugetIndex = default_nuget_reg + else + _nugetIndex = regUrl["@id"] + end + + local baseAddrSuccess, baseAddrUrl = find(resources, function(o) + return o["@type"] == "PackageBaseAddress/3.0.0" + end) + if not baseAddrSuccess then + print("Failed to find endpoint in NuGet index; falling back to default") + _packageBaseAddress = default_base_address + else + _packageBaseAddress = baseAddrUrl["@id"] + end + end + callback() + end + curl(url, handle) +end + +---@return nil +local function populate_nuget_api_sync() + if _nugetIndex ~= nil then + return + end + local url = string.format("https://api.nuget.org/v3/index.json") + local decoded = curl_sync(url) + local default_nuget_reg = "https://api.nuget.org/v3/registration5-semver1/" + local default_base_address = "https://api.nuget.org/v3-flatcontainer/" + + if not decoded then + print("Failed to fetch NuGet index; falling back to default") + _nugetIndex = default_nuget_reg + _packageBaseAddress = default_base_address + else + local resources = decoded["resources"] + + local resourceSuccess, regUrl = find(resources, function(o) + return o["@type"] == "RegistrationsBaseUrl/3.6.0" + end) + if not resourceSuccess then + print("Failed to find endpoint in NuGet index; falling back to default") + _nugetIndex = default_nuget_reg + else + _nugetIndex = regUrl["@id"] + end + + local baseAddrSuccess, baseAddrUrl = find(resources, function(o) + return o["@type"] == "PackageBaseAddress/3.0.0" + end) + if not baseAddrSuccess then + print("Failed to find endpoint in NuGet index; falling back to default") + _packageBaseAddress = default_base_address + else + _packageBaseAddress = baseAddrUrl["@id"] + end + end +end + +---@return nil +---@param callback fun(nugetIndex: string): nil +local function get_nuget_index(callback) + populate_nuget_api(function() + callback(_nugetIndex) + end) +end + +---@return nil +---@param callback fun(packageBaseIndex: string): nil +local function get_package_base_addr(callback) + populate_nuget_api(function() + callback(_packageBaseAddress) + end) +end + +---@return string +local function get_package_base_addr_sync() + populate_nuget_api_sync() + return _packageBaseAddress +end + +local _package_versions_cache = {} + +---@param package_name string +---@return NuGetVersion[] +local function get_package_versions_sync(package_name) + if _package_versions_cache[package_name] ~= nil then + return _package_versions_cache[package_name] + end + local base = get_package_base_addr_sync() + + local url = base .. string.format("%s/index.json", package_name:lower()) + local decoded = curl_sync(url) + if not decoded then + print("Failed to fetch package versions") + return {} + end + + local versions = map(decoded.versions, parse_version) + table.sort(versions, function(a, b) + return compare_versions(b, a) + end) + _package_versions_cache[package_name] = versions + return versions +end + +---@param package_name string +---@param callback fun(v: NuGetVersion[]): nil +---@return nil +local function get_package_versions(package_name, callback) + if _package_versions_cache[package_name] ~= nil then + callback(_package_versions_cache[package_name]) + end + + local function handle(base) + local url = base .. string.format("%s/index.json", package_name:lower()) + local function handle2(decoded) + if not decoded then + print("Failed to fetch package versions") + callback({}) + end + + local versions = map(decoded.versions, parse_version) + table.sort(versions, function(a, b) + return compare_versions(b, a) + end) + _package_versions_cache[package_name] = versions + callback(versions) + end + curl(url, handle2) + end + get_package_base_addr(handle) +end + +---@param version NuGetVersion +---@return nil +local function update_package_version(version) + local line = vim.api.nvim_get_current_line() + local new_line = line:gsub('Version="[^"]+"', string.format('Version="%s"', nuGetVersionToString(version))) + vim.api.nvim_set_current_line(new_line) +end + +-- A map from package to { packageWeDependOn: version }. +--- @type table> +local _package_dependency_cache = {} +---@param package_name string +---@param version NuGetVersion +---@param callback fun(result: table): nil +---@return nil +local function get_package_dependencies(package_name, version, callback) + local key = package_name .. "@" .. nuGetVersionToString(version) + local cache_hit = _package_dependency_cache[key] + if cache_hit ~= nil then + callback(cache_hit) + return + end + + local function handle1(index) + local url = index .. string.format("%s/%s.json", package_name:lower(), nuGetVersionToString(version):lower()) + + local function handle(response) + if not response then + print( + "Failed to get dependencies of " + .. package_name + .. " at version " + .. version + .. " : unsuccessful response to " + .. url + ) + return + end + + local entry_url = response["catalogEntry"] + local function handle2(catalog_entry) + if not catalog_entry then + print( + "Failed to get dependencies of " + .. package_name + .. " at version " + .. version + .. " : unsuccessful response to " + .. entry_url + ) + return + end + + local result = {} + if catalog_entry["dependencyGroups"] then + iter(catalog_entry["dependencyGroups"], function(grp) + if grp["dependencies"] then + for _, dep in pairs(grp["dependencies"]) do + result[dep["id"]] = dep["range"] + end + end + end) + end + + _package_dependency_cache[key] = result + + callback(result) + end + curl(entry_url, handle2) + end + + curl(url, handle) + end + + get_nuget_index(handle1) +end + +---@return table +---@nodiscard +local function get_all_package_references() + local packages = {} + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + + for _, line in ipairs(lines) do + local package_name = line:match('PackageReference Include="([^"]+)"') + or line:match('PackageReference Update="([^"]+)"') + local version = line:match('Version="([^"]+)"') + + if package_name and version then + if not packages[package_name] then + packages[package_name] = {} + end + table.insert(packages[package_name], parse_version(version)) + end + end + + return packages +end + +function ClearNuGetDependencyCache() + for k, _ in pairs(_package_dependency_cache) do + _package_dependency_cache[k] = nil + end +end +vim.api.nvim_create_user_command("ClearNuGetDependencyCache", ClearNuGetDependencyCache, {}) + +function PrintNuGetDependencyCache() + for k, v in pairs(_package_dependency_cache) do + print(k .. ":") + for dep, ver in pairs(v) do + print(" " .. dep .. ": " .. ver) + end + end +end +vim.api.nvim_create_user_command("PrintNuGetDependencyCache", PrintNuGetDependencyCache, {}) + +local function prefetch_dependencies() + local packages = get_all_package_references() + + local function process_package(package_name, versions, callback) + local count = #versions + for _, version in ipairs(versions) do + vim.schedule(function() + get_package_dependencies(package_name, version, function(_) + count = count - 1 + if count == 0 then + callback() + end + end) + end) + end + end + + local total_packages = 0 + for _ in pairs(packages) do + total_packages = total_packages + 1 + end + + local processed_packages = 0 + for package_name, versions in pairs(packages) do + process_package(package_name, versions, function() + local function handle(package_versions) + if package_versions then + process_package(package_name, package_versions, function() + processed_packages = processed_packages + 1 + if processed_packages == total_packages then + print("All dependencies prefetched") + end + end) + else + processed_packages = processed_packages + 1 + if processed_packages == total_packages then + print("All dependencies prefetched") + end + end + end + get_package_versions(package_name, handle) + end) + end +end + +---@param v1 NuGetVersion +---@param v2 NuGetVersion +---@return boolean +---@nodiscard +local function nuget_versions_equal(v1, v2) + return v1.major == v2.major and v1.minor == v2.minor and v1.patch == v2.patch and v1.suffix == v2.suffix +end + +vim.api.nvim_create_autocmd("FileType", { + pattern = { "fsharp_project", "csharp_project", "xml" }, + callback = function() + function UpdateNuGetVersion() + local line = vim.api.nvim_get_current_line() + local package_name = line:match('PackageReference Include="([^"]+)"') + or line:match('PackageReference Update="([^"]+)"') + + if not package_name then + print("No package reference found on the current line") + return + end + + local current_version = parse_version(line:match('Version="([^"]+)"')) + if not current_version then + print("oh no!") + end + + local package_versions = get_package_versions_sync(package_name) + + if #package_versions == 0 then + print("No versions found for the package") + return + end + + local pickers = require("telescope.pickers") + local finders = require("telescope.finders") + local previewers = require("telescope.previewers") + + pickers + .new({}, { + prompt_title = string.format("Select version for %s", package_name), + finder = finders.new_table({ + results = package_versions, + entry_maker = function(entry) + local val = nuGetVersionToString(entry) + local display_value = val + if current_version and nuget_versions_equal(entry, current_version) then + display_value = "[CURRENT] " .. val + end + return { + value = entry, + display = display_value, + ordinal = entry, + } + end, + }), + previewer = previewers.new_buffer_previewer({ + define_preview = function(self, entry, _) + local bufnr = self.state.bufnr + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "Loading..." }) + get_package_dependencies(package_name, entry.value, function(package_dependencies) + if not package_dependencies then + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "No dependencies found" }) + return + end + + local display = {} + table.insert( + display, + "Dependencies for " + .. package_name + .. " at version " + .. nuGetVersionToString(entry.value) + .. ":" + ) + for dep, range in pairs(package_dependencies) do + table.insert(display, dep .. ": " .. range) + end + local ok, err = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, display) + if not ok then + -- If we can't set lines, the window's probably gone. Ignore. + return + end + end) + end, + }), + attach_mappings = function(_, mapping) + mapping("i", "", function(prompt_bufnr) + local selection = require("telescope.actions.state").get_selected_entry() + require("telescope.actions").close(prompt_bufnr) + update_package_version(selection.value) + end) + return true + end, + }) + :find() + end + local whichkey = require("which-key") + whichkey.add({ + { "n", desc = "NuGet" }, + { "nu", UpdateNuGetVersion, desc = "Upgrade NuGet versions" }, + }, { buffer = vim.api.nvim_get_current_buf() }) + + vim.schedule(prefetch_dependencies) + end, +}) diff --git a/home-manager/nvim/init.lua b/home-manager/nvim/init.lua index 71b70d8..d68942b 100644 --- a/home-manager/nvim/init.lua +++ b/home-manager/nvim/init.lua @@ -169,8 +169,6 @@ function DisplayAllMappingsWithTelescope() 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 @@ -224,73 +222,69 @@ function ToggleSpell() vim.cmd("setlocal spell!") end -whichkey.register({ - [vim.api.nvim_get_var("maplocalleader")] = { - DisplayAllMappingsWithTelescope, - "View all mappings", +whichkey.add({ + { + "", + function() + require("which-key").show({ global = false }) + end, + desc = "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 = { + { "mp", MarkdownPreview, desc = "Preview Markdown in Lynx" }, + { "md", RemoveCarriageReturn, desc = "Delete carriage returns from file" }, + { "j", FormatJson, desc = "Auto-format JSON" }, +}) +whichkey.add({ + { + "g", function() require("telescope.builtin").grep_string() end, - "Find instances of text under cursor", + desc = "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", - }, + { "h", desc = "Find historical..." }, + { + "hf", + function() + require("telescope.builtin").oldfiles() + end, + desc = "List previously open files", }, - m = { + { + "hc", + function() + require("telescope.builtin").command_history() + end, + desc = "List previously run commands", + }, + { + "hs", + function() + require("telescope.builtin").search_history() + end, + desc = "List previously run searches", + }, + { + "m", function() require("telescope.builtin").marks() end, - "List marks", + desc = "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")] = { + { "cd", ChangeToCurrentDirectory, desc = "Switch CWD to the directory of the open buffer" }, + { "ss", ToggleSpell, desc = "Toggle spell-checker on or off" }, + { + "", function() require("telescope.builtin").find_files() end, - "Find files by name", + desc = "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, +}) + +vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, { + pattern = { "Directory.Build.props", "*.fsproj", "*.csproj" }, + callback = function() + vim.bo.filetype = "xml" + end, }) diff --git a/home-manager/nvim/ionide-vim.lua b/home-manager/nvim/ionide-vim.lua index 539db58..ede1d84 100644 --- a/home-manager/nvim/ionide-vim.lua +++ b/home-manager/nvim/ionide-vim.lua @@ -59,7 +59,7 @@ local function BuildFSharpProjects(projects) end if not projects then - projects = vim.fn['fsharp#getLoadedProjects']() + projects = vim.fn["fsharp#getLoadedProjects"]() end if projects then local total_projects = 0 @@ -90,7 +90,7 @@ vim.api.nvim_create_user_command("BuildFSharpProject", function(opts) .new({}, { prompt_title = "Projects", finder = finders.new_table({ - results = vim.fn['fsharp#getLoadedProjects'](), + results = vim.fn["fsharp#getLoadedProjects"](), }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_buf, _) @@ -156,7 +156,7 @@ vim.api.nvim_create_user_command("RunFSharpProject", function(opts) .new({}, { prompt_title = "Projects", finder = finders.new_table({ - results = vim.fn['fsharp#getLoadedProjects'](), + results = vim.fn["fsharp#getLoadedProjects"](), }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_buf, _) @@ -185,7 +185,7 @@ vim.api.nvim_create_user_command("PublishFSharpProject", function(opts) .new({}, { prompt_title = "Projects", finder = finders.new_table({ - results = vim.fn['fsharp#getLoadedProjects'](), + results = vim.fn["fsharp#getLoadedProjects"](), }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_buf, _) @@ -206,28 +206,19 @@ vim.api.nvim_create_autocmd("FileType", { callback = function() local status, whichkey = pcall(require, "which-key") 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)" }, - r = { - name = "Run F# project", - d = { ":RunFSharpProject Debug", "Run F# project in debug configuration" }, - r = { ":RunFSharpProject Release", "Run F# project in release configuration" }, - }, - p = { - ":PublishFSharpProject", - "Publish F# project", - }, - b = { - "Build F# project", - a = { BuildFSharpProjects, "Build all projects" }, - s = { ":BuildFSharpProject", "Build specified project" }, - }, - }, - }, { prefix = vim.api.nvim_get_var("maplocalleader"), buffer = vim.api.nvim_get_current_buf() }) + whichkey.add({ + { "f", desc = "F#" }, + { "ft", ":call fsharp#showTooltip()", desc = "Show F# Tooltip" }, + { "fsi", ":call fsharp#toggleFsi()", desc = "Toggle FSI (F# Interactive)" }, + { "fsl", ":call fsharp#sendLineToFsi()", desc = "Send line to FSI (F# Interactive)" }, + { "fr", desc = "Run F# project" }, + { "frd", ":RunFSharpProject Debug", desc = "Run F# project in debug configuration" }, + { "frr", ":RunFSharpProject Release", desc = "Run F# project in release configuration" }, + { "fp", ":PublishFSharpProject", desc = "Publish F# project" }, + { "fb", desc = "Build F# project" }, + { "fba", BuildFSharpProjects, desc = "Build all projects" }, + { "fbs", ":BuildFSharpProject", desc = "Build specified project" }, + }, { 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 }) diff --git a/home-manager/nvim/lean.lua b/home-manager/nvim/lean.lua index 667e9ba..b74c961 100644 --- a/home-manager/nvim/lean.lua +++ b/home-manager/nvim/lean.lua @@ -2,16 +2,16 @@ 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", - }, +require("which-key").add({ + { "l", desc = "Lean" }, + { "li", "LeanInfoviewToggle", desc = "Toggle Lean info view" }, + { "lp", "LeanInfoviewPinTogglePause", desc = "Pause Lean info view" }, + { "ls", "LeanSorryFill", desc = "Fill open goals with sorry" }, + { "lw", "LeanInfoviewEnableWidgets", desc = "Enable Lean widgets" }, + { "lW", "LeanInfoviewDisableWidgets", desc = "Disable Lean widgets" }, + { + "l?", + "LeanAbbreviationsReverseLookup", + desc = "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 9fac0f0..701290f 100644 --- a/home-manager/nvim/lspconfig.lua +++ b/home-manager/nvim/lspconfig.lua @@ -13,6 +13,8 @@ local schemas = { ["https://json.schemastore.org/dotnet-tools.json"] = "dotnet-tools.json", } +require("lspconfig")["clangd"].setup({}) + require("lspconfig")["yamlls"].setup({ settings = { yaml = { @@ -41,15 +43,18 @@ require("lspconfig")["jsonls"].setup({ }, }) +require("lspconfig")["denols"].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) + if not client.workspace_folders then + return + end local path = client.workspace_folders[1].name if vim.uv.fs_stat(path .. "/.luarc.json") or vim.loop.fs_stat(path .. "/.luarc.jsonc") then return @@ -116,15 +121,13 @@ end do local whichkey_status, whichkey = pcall(require, "which-key") if whichkey_status then - whichkey.register({ - l = { - name = "loclist-related commands", - p = { vim.diagnostic.goto_prev, "Go to previous entry in loclist" }, - n = { vim.diagnostic.goto_next, "Go to next entry in loclist" }, - l = { ToggleLocList, "Toggle loclist" }, - f = { vim.diagnostic.open_float, "Open current loclist entry in floating window" }, - }, - }, { prefix = vim.api.nvim_get_var("mapleader") }) + whichkey.add({ + { "l", desc = "loclist-related commands" }, + { "lp", vim.diagnostic.goto_prev, desc = "Go to previous entry in loclist" }, + { "ln", vim.diagnostic.goto_next, desc = "Go to next entry in loclist" }, + { "ll", ToggleLocList, desc = "Toggle loclist" }, + { "lf", vim.diagnostic.open_float, desc = "Open current loclist entry in floating window" }, + }) else vim.keymap.set("n", "lp", vim.diagnostic.goto_prev) vim.keymap.set("n", "ln", vim.diagnostic.goto_next) @@ -144,48 +147,44 @@ vim.api.nvim_create_autocmd("LspAttach", { -- Buffer local mappings. -- See `:help vim.lsp.*` for documentation on any of the below functions - whichkey.register({ - g = { - name = "Go-to related commands", - D = { vim.lsp.buf.declaration, "Go to declaration" }, - d = { vim.lsp.buf.definition, "Go to definition" }, - i = { vim.lsp.buf.implementation, "Go to implementation" }, - r = { - function() - require("telescope.builtin").lsp_references() - end, - "Find references", - }, + whichkey.add({ + { "g", desc = "Go-to related commands" }, + { "gD", vim.lsp.buf.declaration, desc = "Go to declaration" }, + { "gd", vim.lsp.buf.definition, desc = "Go to definition" }, + { "gi", vim.lsp.buf.implementation, desc = "Go to implementation" }, + { + "gr", + function() + require("telescope.builtin").lsp_references() + end, + desc = "Find references", }, - K = { vim.lsp.buf.hover, "Display information about symbol under cursor" }, + { "gK", vim.lsp.buf.hover, desc = "Display information about symbol under cursor" }, }) - whichkey.register({ - [""] = { vim.lsp.buf.signature_help, "Display signature information about symbol under cursor" }, + whichkey.add({ + { "", vim.lsp.buf.signature_help, desc = "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", - }, + whichkey.add({ + { "w", desc = "Workspace-related commands" }, + { "wa", vim.lsp.buf.add_workspace_folder, desc = "Add a path to the workspace folders list" }, + { "wr", vim.lsp.buf.add_workspace_folder, desc = "Remove a path from the workspace folders list" }, + { + "wl", + function() + print(vim.inspect(vim.lsp.buf.list_workspace_folders())) + end, + desc = "Show the workspace folders list", }, - f = { + { + "f", function() vim.lsp.buf.format({ async = true }) end, - "Autoformat", + desc = "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") }) + { "ca", vim.lsp.buf.code_action, desc = "Select a code action" }, + { "rn", vim.lsp.buf.rename, desc = "Rename variable" }, + { "D", vim.lsp.buf.type_definition, desc = "Go to type definition" }, + }) end, }) diff --git a/home-manager/nvim/nvim-dap-python.lua b/home-manager/nvim/nvim-dap-python.lua index 059ab3e..898dce1 100644 --- a/home-manager/nvim/nvim-dap-python.lua +++ b/home-manager/nvim/nvim-dap-python.lua @@ -2,14 +2,10 @@ require("dap-python").setup("%PYTHONENV%/bin/python") do local whichkey = require("which-key") - whichkey.register({ - ['pd'] = { - "Debugger-related commands", - t = { - "Tests", - f = { require("dap-python").test_class, "Run Python tests in the current file" }, - c = { require("dap-python").test_method, "Run the Python test under the cursor" }, - }, - }, - }, { prefix = vim.api.nvim_get_var("maplocalleader") }) + whichkey.add({ + { "pd", desc = "Debugger-related commands" }, + { "pdt", desc = "Tests" }, + { "pdtf", require("dap-python").test_class, desc = "Run Python tests in the current file" }, + { "pdtc", require("dap-python").test_method, desc = "Run the Python test under the cursor" }, + }) end diff --git a/home-manager/nvim/nvim-dap.lua b/home-manager/nvim/nvim-dap.lua index 49da93d..630ac31 100644 --- a/home-manager/nvim/nvim-dap.lua +++ b/home-manager/nvim/nvim-dap.lua @@ -30,37 +30,36 @@ dap.configurations.cs = { do local whichkey = require("which-key") - whichkey.register({ - d = { - name = "Debugger-related commands", - o = { dap.step_over, "Step over" }, - i = { dap.step_into, "Step into" }, - c = { dap.continue, "Continue" }, - C = { dap.run_last, "Run with last debug configuration" }, - b = { dap.toggle_breakpoint, "Toggle breakpoint" }, - r = { dap.repl.open, "Open debug repl" }, - v = { - name = "Commands to view debugger state", - v = { - function() - dap_ui.hover() - end, - "View value of expression under cursor", - }, - s = { - function() - dap_ui.sidebar(dap_ui.scopes).open() - end, - "View values of all variables in all scopes", - }, - f = { - function() - dap_ui.sidebar(dap_ui.frames).open() - end, - "View stack frames", - }, - }, - t = { dap.terminate, "Terminate/stop/end debug session" }, + whichkey.add({ + { "d", desc = "Debugger-related commands" }, + { "do", dap.step_over, desc = "Step over" }, + { "di", dap.step_into, desc = "Step into" }, + { "dc", dap.continue, desc = "Continue" }, + { "dC", dap.run_last, desc = "Run with last debug configuration" }, + { "db", dap.toggle_breakpoint, desc = "Toggle breakpoint" }, + { "dr", dap.repl.open, desc = "Open debug repl" }, + { "dv", desc = "Commands to view debugger state" }, + { + "dvv", + function() + dap_ui.hover() + end, + desc = "View value of expression under cursor", }, - }, { prefix = vim.api.nvim_get_var("maplocalleader") }) + { + "dvs", + function() + dap_ui.sidebar(dap_ui.scopes).open() + end, + desc = "View values of all variables in all scopes", + }, + { + "dvf", + function() + dap_ui.sidebar(dap_ui.frames).open() + end, + desc = "View stack frames", + }, + { "dt", dap.terminate, desc = "Terminate/stop/end debug session" }, + }) end diff --git a/home-manager/nvim/python.lua b/home-manager/nvim/python.lua index 5476f60..b38fbf9 100644 --- a/home-manager/nvim/python.lua +++ b/home-manager/nvim/python.lua @@ -55,12 +55,10 @@ end do local whichkey = require("which-key") - whichkey.register({ - ['pt'] = { - "Run Python tests", - f = { RunPythonTestsInFile, "Run Python tests in the current file" }, - a = { RunAllPythonTests, "Run all Python tests" }, - c = { RunPythonTestAtCursor, "Run the Python test under the cursor" }, - }, - }, { prefix = vim.api.nvim_get_var("maplocalleader") }) + whichkey.add({ + { "pt", desc = "Run Python tests" }, + { "ptf", RunPythonTestsInFile, desc = "Run Python tests in the current file" }, + { "pta", RunAllPythonTests, desc = "Run all Python tests" }, + { "ptc", RunPythonTestAtCursor, desc = "Run the Python test under the cursor" }, + }) end diff --git a/home-manager/nvim/tokyonight.lua b/home-manager/nvim/tokyonight.lua index 229ff32..761a794 100644 --- a/home-manager/nvim/tokyonight.lua +++ b/home-manager/nvim/tokyonight.lua @@ -1,5 +1,8 @@ require("tokyonight").setup({ style = "night", + on_colors = function(colors) + colors.border = "#565f89" + end, }) vim.cmd([[colorscheme tokyonight]]) diff --git a/home-manager/nvim/venv-selector.lua b/home-manager/nvim/venv-selector.lua index 37ed4cc..d1f76e4 100644 --- a/home-manager/nvim/venv-selector.lua +++ b/home-manager/nvim/venv-selector.lua @@ -84,17 +84,16 @@ end do local whichkey = require("which-key") - whichkey.register({ - ['pv'] = { - name = "Python virtual environment-related commands", - c = { CreateVenv, "Create virtual environment" }, - l = { SelectVenv, "Load virtual environment" }, - o = { - function() - vim.cmd("VenvSelect") - end, - "Choose (override) new virtual environment", - }, + whichkey.add({ + { "pv", desc = "Python virtual environment-related commands" }, + { "pvc", CreateVenv, desc = "Create virtual environment" }, + { "pvl", SelectVenv, desc = "Load virtual environment" }, + { + "pvo", + function() + vim.cmd("VenvSelect") + end, + desc = "Choose (override) new virtual environment", }, - }, { prefix = vim.api.nvim_get_var("maplocalleader") }) + }) end diff --git a/home-manager/nvim/which-key.lua b/home-manager/nvim/which-key.lua index c94e855..77e55e7 100644 --- a/home-manager/nvim/which-key.lua +++ b/home-manager/nvim/which-key.lua @@ -18,67 +18,19 @@ require("which-key").setup({ 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 = {