{lib, config, ...}: let cfg = config.helixApp; # helix theming docs: https://docs.helix-editor.com/themes.html # Check current stylix theme: ~/.config/helix/themes/stylix.toml # And computed minimal: ~/.config/helix/themes/minimal.toml # Minimal syntax highlighting theme using base16 colors # Philosophy: "If everything is highlighted, nothing is" # Highlight comments, keywords, strings, and constants, all else uses style, not color minimalTheme = { # Inherit Stylix's base theme (UI, palette, etc.) inherits = "stylix"; # Comments "comment" = { fg = "base0A"; modifiers = ["italic"]; }; # Keywords "keyword" = { fg = "base0E"; modifiers = ["italic"]; }; "keyword.control" = { fg = "base0E"; modifiers = ["italic"]; }; "keyword.directive" = { fg = "base0E"; modifiers = ["italic"]; }; "keyword.function" = { fg = "base0E"; modifiers = ["italic"]; }; "keyword.operator" = { fg = "base0E"; modifiers = ["italic"]; }; "keyword.return" = { fg = "base0E"; modifiers = ["italic"]; }; "keyword.storage" = { fg = "base0E"; modifiers = ["italic"]; }; # Strings "string" = "base0B"; "string.regexp" = "base0B"; "string.special" = "base0B"; # Constants/Numbers "constant" = "base08"; "constant.numeric" = "base08"; "constant.builtin" = "base08"; "constant.character" = "base08"; "constant.character.escape" = "base08"; # === SUBTLE HIGHLIGHTING === # Types "type" = { fg = "base0F"; modifiers = ["bold"]; }; "type.builtin" = { fg = "base0F"; modifiers = ["bold"]; }; "type.enum" = { fg = "base0F"; modifiers = ["bold"]; }; "type.enum.variant" = { fg = "base0F"; modifiers = ["bold"]; }; # Definitions - bold+italic, same color as text (landmarks) "function.definition" = { fg = "base07"; modifiers = ["bold" "italic"]; }; "type.definition" = { fg = "base07"; modifiers = ["bold" "italic"]; }; # === PLAIN TEXT (majority of code) === # Functions - plain text "function" = "base07"; "function.builtin" = "base07"; "function.method" = "base07"; "function.macro" = "base07"; # Variables - plain text "variable" = "base05"; "variable.builtin" = "base07"; "variable.parameter" = "base05"; "variable.other" = "base05"; "variable.other.member" = "base05"; # Other constructs - plain text "constructor" = "base05"; "attribute" = "base05"; "label" = "base05"; "namespace" = "base05"; "tag" = "base05"; "special" = "base05"; # Punctuation - plain text, dim "punctuation" = { fg = "base05"; modifiers = ["dim"]; }; "punctuation.bracket" = { fg = "base05"; modifiers = ["dim"]; }; "punctuation.delimiter" = { fg = "base05"; modifiers = ["dim"]; }; "punctuation.special" = { fg = "base05"; modifiers = ["dim"]; }; "operator" = { fg = "base05"; modifiers = ["dim"]; }; # === MARKUP (documents) === "markup.heading" = { fg = "base0E"; modifiers = ["bold"]; }; "markup.heading.1" = { fg = "base0E"; modifiers = ["bold"]; underline.style = "double_line";}; "markup.heading.2" = { fg = "base0B"; modifiers = ["bold"]; underline.style = "line";}; "markup.heading.3" = { fg = "base08"; modifiers = ["bold"]; underline.style = "line";}; "markup.heading.4" = { fg = "base0C"; modifiers = ["bold"]; }; "markup.heading.5" = { fg = "base0D"; modifiers = ["bold"]; }; "markup.heading.6" = { fg = "base0F"; modifiers = ["bold"]; }; "markup.list" = "base0E"; "markup.list.checked" = { modifiers = ["crossed_out"]; }; "markup.bold" = { modifiers = ["bold"]; }; "markup.italic" = { modifiers = ["italic"]; }; "markup.strikethrough" = { modifiers = ["crossed_out"]; }; "markup.link.url" = { fg = "base09"; underline.style = "line"; }; "markup.link.text" = "base0E"; "markup.quote" = "base0B"; "markup.raw" = "base06"; # "markup.raw.inline" = "base06"; # "markup.raw.block" = { fg = "base06"; bg = "";}; # === UI Tweaks === "ui.text.directory" = { fg = "base06"; modifiers = ["italic"]; }; # directory in popups "ui.text.focus" = { fg = "base07"; modifiers = ["bold"]; }; # selected line in pickers }; in { options.helixApp = { enable = lib.mkEnableOption "enable helix editor"; themeOverrides = lib.mkOption { type = lib.types.attrsOf lib.types.anything; default = {}; description = '' Override helix theme scopes. These are merged with the minimal base theme. Uses base16 color names (base00-base0F) for Stylix integration. Example: Make types plain text instead of subtle: themeOverrides = { "type" = "base05"; }; ''; example = { "type" = "base05"; "comment" = { fg = "base03"; modifiers = ["italic"]; }; }; }; }; config = lib.mkIf cfg.enable { # Let Stylix generate its base theme (for UI elements, palette) stylix.targets.helix.enable = true; programs.helix = { enable = true; # Use our minimal theme instead of Stylix's default settings.theme = lib.mkForce "minimal"; # Define minimal theme that inherits Stylix's palette but overrides syntax highlighting themes.minimal = minimalTheme // cfg.themeOverrides; settings = { keys.normal = { # Navigation (Colemak-DH) n = "move_char_left"; i = "move_visual_line_down"; e = "move_visual_line_up"; o = "move_char_right"; "S-tab" = "jump_backward"; I = [ "page_cursor_half_down" "align_view_center" ]; E = [ "page_cursor_half_up" "align_view_center" ]; # Modes h = "insert_mode"; H = "insert_at_line_start"; l = "open_below"; L = "open_above"; # Search k = "search_next"; K = "search_prev"; # Selection j = "join_selections"; J = "yank_joined"; C-s = "split_selection_on_newline"; C-minus = "merge_selections"; C-_ = "merge_consecutive_selections"; "C-;" = "flip_selections"; "C-:" = "ensure_selections_forward"; "C-," = "remove_primary_selection"; C-c = "change_selection_noyank"; C-d = "delete_selection_noyank"; "C-(" = "rotate_selection_contents_backward"; "C-)" = "rotate_selection_contents_forward"; C-x = "shrink_to_line_bounds"; C-J = "join_selections_space"; C-K = "remove_selections"; C-e = "expand_selection"; C-i = "shrink_selection"; C-n = "select_prev_sibling"; C-o = "select_next_sibling"; # Misc "C-/" = "toggle_comments"; "@" = ":append-output git config get user.email"; space = { B = ":sh git log -n 5 --format='format:%%h (%%an: %%ar) %%s' --no-patch -L%{cursor_line},+1:%{buffer_name}"; x = ":write-buffer-close"; X = ":write-quit-all"; o = ":config-open"; h = "hover"; k = "select_references_to_symbol_under_cursor"; }; g = { "/" = "goto_next_buffer"; h = "goto_previous_buffer"; n = [ "collapse_selection" "extend_to_line_start" ]; o = [ "collapse_selection" "extend_to_line_end" ]; e = "move_line_up"; i = "move_line_down"; l = "goto_last_line"; p = "no_op"; k = "no_op"; j = "no_op"; }; m.m = [ "select_mode" "match_brackets" "normal_mode" ]; "C-w" = { h = "hsplit"; C-h = "hsplit"; n = "jump_view_left"; C-n = "jump_view_left"; i = "jump_view_down"; I = "swap_view_down"; C-i = "jump_view_down"; e = "jump_view_up"; E = "swap_view_up"; C-e = "jump_view_up"; o = "jump_view_right"; O = "swap_view_right"; C-o = "jump_view_right"; # Remove old s = "no_op"; C-s = "no_op"; H = "no_op"; j = "no_op"; J = "no_op"; C-j = "no_op"; k = "no_op"; K = "no_op"; C-k = "no_op"; l = "no_op"; L = "no_op"; C-l = "no_op"; }; }; keys.select = { n = "extend_char_left"; i = "extend_line_down"; e = "extend_line_up"; o = "extend_char_right"; I = [ "page_cursor_half_down" "align_view_center" ]; E = [ "page_cursor_half_up" "align_view_center" ]; g = { "/" = "goto_next_buffer"; h = "goto_previous_buffer"; n = "goto_line_start"; o = "goto_line_end"; e = "move_line_up"; i = "move_line_down"; l = "goto_last_line"; p = "no_op"; k = "no_op"; j = "no_op"; }; }; editor = { auto-format = true; auto-save = true; bufferline = "always"; color-modes = true; completion-timeout = 5; cursorcolumn = true; cursorline = true; indent-heuristic = "tree-sitter"; line-number = "relative"; rulers = [ 120 ]; text-width = 120; end-of-line-diagnostics = "hint"; cursor-shape = { insert = "bar"; normal = "block"; select = "underline"; }; file-picker.hidden = false; indent-guides.render = true; inline-diagnostics = { cursor-line = "warning"; other-lines = "disable"; prefix-len = 5; max-diagnostics = 1; max-wrap = 30; }; lsp = { display-messages = true; display-inlay-hints = true; }; soft-wrap = { enable = true; max-wrap = 30; }; statusline = { left = [ "mode" "file-modification-indicator" "spinner" "version-control" ]; center = [ "file-name" ]; right = [ "diagnostics" "selections" "register" "position" "file-encoding" ]; mode.normal = "Normal"; mode.insert = "Insert"; mode.select = "Select"; }; whitespace.render = { space = "all"; tab = "all"; tabpad = "all"; newline = "none"; nbsp = "none"; }; whitespace.characters = { space = " "; tab = "⇀"; tabpad = " "; }; }; }; languages = { language = [ { name = "go"; debugger = { name = "go"; transport = "tcp"; command = "dlv"; args = [ "connect" ]; port-arg = "127.0.0.1:2345"; templates = [{ name = "connect"; request = "launch"; completion = []; args = {}; }]; }; } { name = "markdown"; language-servers = [ "marksman" ]; } { name = "dart"; formatter = { command = "dart"; args = [ "format" "-l" "120" ]; }; language-servers = [ "dart" ]; } { name = "nix"; language-servers = [ "nil" ]; } { name = "zig"; language-servers = [ "zls" ]; debugger = { name = "codelldb-dap"; transport = "tcp"; command = "codelldb"; args = []; port-arg = "--port {}"; templates = [ { name = "launch"; request = "launch"; completion = [{ name = "binary"; completion = "filename"; }]; args = { console = "internalConsole"; program = "{0}"; }; } { name = "attach"; request = "attach"; completion = [ "pid" ]; args = { console = "internalConsole"; pid = "{0}"; }; } { name = "gdbserver attach"; request = "attach"; completion = [ { name = "lldb connect url"; default = "connect://localhost:3333"; } { name = "file"; completion = "filename"; } "pid" ]; args = { console = "internalConsole"; attachCommands = [ "platform select remote-gdb-server" "platform connect {0}" "file {1}" "attach {2}" ]; }; } ]; }; } { name = "cyano"; scope = "source.cyo"; file-types = [ "cyo" ]; language-servers = [ "ltex-ls" ]; } ]; language-server.ltex-ls = { command = "ltex-ls"; }; }; }; }; }