let
  disableNano = {
    programs.nano.enable = false;
  };

  helixBase =
    {
      inputs,
      lib,
      pkgs,
      config,
      ...
    }:
    let
      inherit (lib.attrsets) mapAttrs' nameValuePair;
      inherit (lib.trivial) const;
      inherit (lib.attrsets)
        genAttrs
        optionalAttrs
        attrValues
        mapAttrs
        ;
      inherit (lib.lists) singleton;
      inherit (builtins) elem;
      inherit (config) theme;

      toml = pkgs.formats.toml { };

      yaziPickerScript =
        pkgs.writeShellScript "yazi-picker.sh" # bash
          ''
            #!/usr/bin/env bash

            paths=$(yazi "$2" --chooser-file=/dev/stdout | while read -r; do printf "%q " "$REPLY"; done)

            if [[ -n "$paths" ]]; then
            	zellij action toggle-floating-panes
            	zellij action write 27 # send <Escape> key
            	zellij action write-chars ":$1 $paths"
            	zellij action write 13 # send <Enter> key
            else
            	zellij action toggle-floating-panes
            fi
          '';

      mkFgStyle =
        colors: color:
        genAttrs colors (const {
          fg = color;
        });

      mkBgStyle =
        colors: color:
        genAttrs colors (const {
          bg = color;
        });

      mkThemes =
        themes:
        mapAttrs' (
          name: value:
          nameValuePair "helix/themes/${name}.toml" {
            source = toml.generate "helix-theme-${name}.toml" value;
          }
        ) themes;

      settings = {
        theme = if theme.colorScheme == "pywal" then "base16_custom" else theme.helix;

        editor = {
          bufferline = "multiple";
          completion-timeout = 5;
          completion-replace = true;
          color-modes = true;
          cursorline = true;
          file-picker.hidden = false;
          idle-timeout = 0;
          shell = [
            "nu"
            "--commands"
          ];
          trim-trailing-whitespace = true;
          true-color = true;
          lsp.display-inlay-hints = true;
          inline-diagnostics.cursor-line = "hint";
          end-of-line-diagnostics = "hint";
          jump-label-alphabet = "jfkdls;aurieowpqnvmcxz";
          cursor-shape = {
            normal = "block";
            select = "underline";
            insert = "bar";
          };
          indent-guides = {
            character = "▏";
            render = true;
          };
          whitespace = {
            characters.tab = "→";
            render.tab = "all";
          };

          auto-pairs = {
            "\"" = "\"";
            "'" = "'";
            "`" = "`";
            "(" = ")";
            "{" = "}";
            "[" = "]";
            "<" = ">";
          };

          # Nightly options:
          word-completion.trigger-length = 3;
          rainbow-brackets = false;
        };

        keys = {
          select = {
            "A-h" = "jump_view_left";
            "A-j" = "jump_view_down";
            "A-k" = "jump_view_up";
            "A-l" = "jump_view_right";

            "C-j" = "@X*dp";
            "C-k" = "@X*dkP";

            "ret" = "goto_word";

            "q" = "record_macro";
            "@" = "replay_macro";

            "C-X" = "select_line_above";

            "C-y" =
              ":sh zellij run -n Yazi -c -f -x 10%% -y 10%% --width 80%% --height 80%% -- ${yaziPickerScript} open ...%{buffer_name}";

            "C-a" = "@*%s<ret>";

            "D" = "extend_to_line_end";
          };

          normal = {
            # Hack to have a custom popup helper with a title.
            "A-h" = "jump_view_left";
            "A-j" = "jump_view_down";
            "A-k" = "jump_view_up";
            "A-l" = "jump_view_right";

            # Move lines up and down. Use `z` register to avoid clobbering system or primary clipboard.
            "C-j" = "@X\"zd\"zp";
            "C-k" = "@X\"zdk\"zP";

            "ret" = "goto_word";

            "q" = "record_macro";
            "@" = "replay_macro";

            "C-X" = "select_line_above";

            "C-y" =
              ":sh zellij run -n Yazi -c -f -x 10%% -y 10%% --width 80%% --height 80%% -- ${yaziPickerScript} open ...%{buffer_name}";

            "C-a" = "@*%s<ret>";

            "D" = "extend_to_line_end";

            "'" = {
              _ = "@ File Management\n";

              t = "@:sh touch <C-r>%";
              r = "@:sh rm <C-r>%";
              k = "@:sh mkdir <C-r>%";
              m = "@:sh mv <C-r>% <C-r>%";
              c = "@:sh cp <C-r>% <C-r>%";
            };

            # Like `ci<char>` in vim.
            m = {
              "(" = "@lf)hmi)";
              ")" = "@lf)hmi)";
              "{" = "@lf}hmi}";
              "}" = "@lf}hmi}";
              "[" = "@lf]hmi]";
              "]" = "@lf]hmi]";
              "'" = "@lf'lmi'";
              "\"" = "@lf\"lmi\"";
            };
          };
        };
      };

      # Pywal output doesn't have gradients like base16 needs.
      # So it's necessary to override a few colours.
      # Also added overrides for diagnostic colours which don't work well from Pywal.
      themes = {
        base16_custom = {
          inherits = "base16_default";
        }
        # Comments.
        // mkFgStyle [ "comment" ] (if theme.isDark then "#909090" else "#C0C0C0")
        # Selections.
        // mkBgStyle [ "ui.selection" "ui.selection.primary" ] (
          if theme.isDark then "#707070" else "#E0E0E0"
        )
        # Cursorline and popups.
        // mkBgStyle [ "ui.cursorline.primary" "ui.cursorline.secondary" "ui.popup" "ui.popup.info" ] (
          if theme.isDark then "#404040" else "#F0F0F0"
        )
        # Info.
        //
          mkFgStyle
            [ "hint" "info" "diagnostic" "diagnostic.hint" "diagnostic.info" "diagnostic.unnecessary" ]
            (
              if theme.isDark then "#2B83A6" else "#3A8C9A" # Muted teal.
            )
        # Warnings.
        // mkFgStyle [ "warning" "diagnostic.warning" "diagnostic.deprecated" ] (
          if theme.isDark then "#B58900" else "#9D8740" # Muted yellow.
        )
        # Errors.
        // mkFgStyle [ "error" "diagnostic.error" ] (
          if theme.isDark then "#9D0006" else "#8F3F71" # Muted red/brown.
        );
      };

      languages = {
        language =
          let
            denoFmtArgs = [
              "fmt"
              "--use-tabs"
              "--no-semicolons"
              "--indent-width"
              "4"
              "--unstable-component"
            ];

            denoJsTsLanguages = [
              "javascript"
              "jsx"
              "typescript"
              "tsx"
            ];

            languageConfig =
              name: extension:
              {
                inherit name;
                auto-format = true;
                formatter.command = "deno";
                formatter.args = denoFmtArgs ++ [
                  "--ext"
                  extension
                  "-"
                ];
              }
              // optionalAttrs (elem name denoJsTsLanguages) {
                language-servers = [
                  "deno"
                  "typos"
                ];
              };

            denoFmtLanguages =
              {
                astro = "astro";
                css = "css";
                html = "html";
                javascript = "js";
                json = "json";
                jsonc = "jsonc";
                jsx = "jsx";
                markdown = "md";
                scss = "scss";
                svelte = "svelte";
                tsx = "tsx";
                typescript = "ts";
                vue = "vue";
                yaml = "yaml";
              }
              |> mapAttrs languageConfig
              |> attrValues;

            baseLanguages = [
              {
                name = "rust";
                auto-format = true;
                language-servers = [
                  {
                    name = "rust-analyzer";
                    except-features = singleton "inlay-hints";
                  }
                  "typos"
                ];
                indent = {
                  tab-width = 3;
                  unit = "   ";
                };
              }
              {
                name = "nix";
                auto-format = true;
                formatter.command = "nixfmt";
                language-servers = [
                  "nixd"
                  "typos"
                ];
              }
              {
                name = "toml";
                auto-format = true;
                formatter.command = "taplo";
                formatter.args = [
                  "fmt"
                  "--option"
                  "align_entries=true"
                  "--option"
                  "column_width=100"
                  "--option"
                  "compact_arrays=false"
                  "--option"
                  "reorder_inline_tables=true"
                  "--option"
                  "reorder_keys=true"
                  "-"
                ];
                language-servers = [
                  "taplo"
                  "typos"
                ];
              }
              {
                name = "markdown";
                auto-format = true;
                language-servers = [
                  "marksman"
                  "typos"
                ];
              }
              {
                name = "just";
                auto-format = true;
                formatter.command = "just-formatter";
                language-servers = [
                  "just-lsp"
                  "typos"
                ];
              }
              {
                name = "nu";
                auto-format = false;
                # formatter.command = "nufmt"; # Not good enough yet.
                # formatter.args = [
                #   "--config"
                #   "/home/jam/.config/nufmt/config.nuon"
                #   "--stdin"
                # ];
                formatter.command = "topiary";
                formatter.args = [
                  "format"
                  "--language"
                  "nu"
                ];
                language-servers = [
                  "nu-lsp"
                  # "nu-lint" # Waiting for <https://codeberg.org/wvhulle/nu-lint/pulls/96>
                  "typos"
                ];
                indent = {
                  tab-width = 3;
                  unit = "   ";
                };
              }
              # I can't get this working right now.
              # {
              #   name               = "rust";
              #   debugger.name      = "lldb-dap";
              #   debugger.transport = "stdio";
              #   debugger.command   = "lldb-dap";
              #   debugger.templates = [{
              #     name         = "binary";
              #     request      = "launch";
              #     args.program = "{0}";
              #     completion   = [{
              #       name       = "binary";
              #       completion = "filename";
              #     }];
              #   }];
              # }
            ];
          in
          denoFmtLanguages ++ baseLanguages;

        language-servers = {
          nixd = {
            command = "nixd";
            args = singleton "--inlay-hints";
            config.nixd = {
              nixpkgs.expr = ''import (builtins.getFlake "/home/jam/nixos").inputs.os { }'';
              options = {
                current-host.expr = ''(builtins.getFlake "/home/jam/nixos").nixosConfigurations.${config.networking.hostName}.options'';
                flake-parts.expr = ''(builtins.getFlake "/home/jam/nixos").debug.options'';
                flake-parts2.expr = ''(builtins.getFlake "/home/jam/nixos").currentSystem.options'';
              };
            };
          };

          typos.command = "typos-lsp";

          deno = {
            command = "deno";
            args = singleton "lsp";

            config.javascript = {
              enable = true;
              lint = true;
              unstable = true;

              suggest.imports.hosts."https://deno.land" = true;

              inlayHints.enumMemberValues.enabled = true;
              inlayHints.functionLikeReturnTypes.enabled = true;
              inlayHints.parameterNames.enabled = "all";
              inlayHints.parameterTypes.enabled = true;
              inlayHints.propertyDeclarationTypes.enabled = true;
              inlayHints.variableTypes.enabled = true;
            };
          };

          rust-analyzer = {
            except-features = singleton "inlay-hints";

            config = {
              cargo.features = "all";
              check.command = "clippy";
              completion.callable.snippets = "add_parentheses";
            };
          };
        };
      };
    in
    {
      hjem.extraModules = singleton {
        packages = singleton inputs.helix.packages.${pkgs.stdenv.hostPlatform.system}.helix; # `.helix` follows the master branch.

        xdg.config.files = {
          "helix/config.toml".source = toml.generate "helix-config.toml" settings;

          "helix/languages.toml".source = toml.generate "helix-languages.toml" languages;

          "nufmt/config.nuon".text = # nuon
            ''
              {
                indent: 3
                line_length: 100
                margin: 1
              }
            '';
        }
        // mkThemes themes;
      };
    };

  helixExtra =
    {
      inputs,
      pkgs,
      lib,
      ...
    }:
    let
      inherit (lib.lists) singleton;
    in
    {
      hjem.extraModules = singleton {
        packages = [
          # Rust
          # rust-analyzer is in modules/common/rust.nix
          pkgs.lldb

          # Assembler
          pkgs.asm-lsp

          # TypeScript etc.
          pkgs.deno

          # Nix
          pkgs.nixd
          pkgs.nixfmt

          # YAML
          pkgs.yaml-language-server

          # JSON
          pkgs.vscode-json-languageserver

          # TOML
          pkgs.taplo

          # Svelte
          pkgs.svelte-language-server

          # SQL
          pkgs.sqruff

          # Markdown
          pkgs.marksman

          # Just
          pkgs.just-lsp

          # Haskell
          pkgs.fourmolu
          pkgs.haskell-language-server

          # Nushell
          pkgs.topiary
          inputs.nu-lint.packages.${pkgs.stdenv.hostPlatform.system}.default
          # (inputs.nufmt.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs {
          #   # Fix various random build errors.
          #   doCheck = false;
          #   patches = [ ];
          # })

          # Typos
          pkgs.typos-lsp
        ];
      };
    };
in
{
  flake-file.inputs = {
    helix = {
      url = "github:helix-editor/helix";

      inputs.nixpkgs.follows = "os";
    };

    nu-lint = {
      url = "git+https://codeberg.org/wvhulle/nu-lint";

      inputs.nixpkgs.follows = "os";
    };

    nufmt = {
      url = "github:nushell/nufmt";

      inputs.nixpkgs.follows = "os";
    };
  };

  flake.modules.nixos.helix = helixBase;
  flake.modules.darwin.helix = helixBase;

  flake.modules.nixos.helix-extra = helixExtra;
  flake.modules.darwin.helix-extra = helixExtra;

  flake.modules.nixos.disable-nano = disableNano;
  flake.modules.darwin.disable-nano = disableNano;
}