{ config, pkgs, lib, ... }: with lib; let cfg = config.programs.my-kakoune.tree-sitter; languageModule = types.submodule { options = { grammar.src = mkOption { type = types.package; description = "The repo to be used"; }; grammar.path = mkOption { type = types.str; default = "src"; }; grammar.compile = { command = mkOption { type = types.str; default = "${pkgs.gcc}/bin/gcc"; }; args = mkOption { type = types.listOf types.str; default = [ "-c" "-fpic" "../parser.c" "../scanner.c" "-I" ".." ]; }; flags = mkOption { type = types.listOf types.str; default = [ "-O3" ]; }; }; grammar.link = { command = mkOption { type = types.str; default = "${pkgs.gcc}/bin/gcc"; }; args = mkOption { type = types.listOf types.str; default = [ "-shared" "-fpic" "parser.o" "scanner.o" ]; }; flags = mkOption { type = types.listOf types.str; default = [ "-O3" ]; }; }; queries.src = mkOption { type = types.package; description = "The repo to be used"; }; queries.path = mkOption { type = types.nullOr types.str; default = null; }; }; }; in { options.programs.my-kakoune.tree-sitter = { enable = mkOption { type = types.bool; default = true; description = "Enable kak-tree-sitter"; }; package = mkPackageOption pkgs "kak-tree-sitter" { }; features = { highlighting = mkOption { type = types.bool; description = "Enable highlighting"; default = true; }; text_objects = mkOption { type = types.bool; description = "Enable text objects"; default = true; }; }; highlighterGroups = mkOption { type = types.attrsOf types.str; default = { attribute = "@attribute"; comment = "@comment"; conceal = "%opt{mauve}+i"; constant = "%opt{peach}"; constant_builtin_boolean = "%opt{sky}"; constant_character = "%opt{yellow}"; constant_macro = "%opt{mauve}"; constant_numeric = "%opt{peach}"; constructor = "%opt{sapphire}"; diff_plus = "%opt{green}"; diff_minus = "%opt{red}"; diff_delta = "%opt{blue}"; diff_delta_moved = "%opt{mauve}"; error = "%opt{red}+b"; function = "@function"; function_builtin = "@builtin"; function_macro = "+i@ts_function"; hint = "%opt{blue}+b"; info = "%opt{green}+b"; keyword = "keyword"; keyword_conditional = "+i@ts_keyword"; keyword_control_conditional = "+i@ts_keyword"; keyword_control_directive = "+i@ts_keyword"; keyword_control_import = "+i@ts_keyword"; keyword_directive = "+i@ts_keyword"; label = "%opt{sapphire}+i"; markup_bold = "%opt{peach}+b"; markup_heading = "%opt{red}"; markup_heading_1 = "%opt{red}"; markup_heading_2 = "%opt{mauve}"; markup_heading_3 = "%opt{green}"; markup_heading_4 = "%opt{yellow}"; markup_heading_5 = "%opt{pink}"; markup_heading_6 = "%opt{teal}"; markup_heading_marker = "%opt{peach}+b"; markup_italic = "%opt{pink}+i"; markup_list_checked = "%opt{green}"; markup_list_numbered = "%opt{blue}+i"; markup_list_unchecked = "%opt{teal}"; markup_list_unnumbered = "%opt{mauve}"; markup_link_label = "%opt{blue}"; markup_link_url = "%opt{teal}+u"; markup_link_uri = "%opt{teal}+u"; markup_link_text = "%opt{blue}"; markup_quote = "%opt{crust}"; markup_raw = "%opt{sky}"; markup_raw_block = "%opt{sky}"; markup_raw_inline = "%opt{green}"; markup_strikethrough = "%opt{crust}+s"; namespace = "@module"; operator = "@operator"; property = "%opt{sky}"; punctuation = "%opt{overlay2}"; punctuation_special = "%opt{sky}"; special = "%opt{blue}"; spell = "%opt{mauve}"; string = "%opt{green}"; string_regex = "%opt{peach}"; string_regexp = "%opt{peach}"; string_escape = "%opt{mauve}"; string_special = "%opt{blue}"; string_special_path = "%opt{green}"; string_special_symbol = "%opt{mauve}"; string_symbol = "%opt{red}"; tag = "%opt{teal}"; tag_error = "%opt{red}"; text_title = "%opt{mauve}"; type = "@type"; type_enum_variant = "+i@ts_type"; variable = "@variable"; variable_builtin = "@builtin"; variable_other_member = "%opt{teal}"; variable_parameter = "+i@variable"; warning = "%opt{peach}+b"; }; }; extraHighlighterGroups = mkOption { type = types.attrsOf types.str; default = { }; description = "Highlighter groups to add to the `highlighterGroups`. Maps from group names to face names."; }; aliases = mkOption { type = types.attrsOf types.str; default = { comment_block = "comment"; comment_line = "comment"; constant_character_escape = "constant_character"; constant_numeric_float = "constant_numeric"; constant_numeric_integer = "constant_numeric"; function_method = "function"; function_special = "function"; keyword_control = "keyword"; keyword_control_repeat = "keyword"; keyword_control_return = "keyword"; keyword_control_except = "keyword"; keyword_control_exception = "keyword"; keyword_function = "keyword"; keyword_operator = "keyword"; keyword_special = "keyword"; keyword_storage = "keyword"; keyword_storage_modifier = "keyword"; keyword_storage_modifier_mut = "keyword"; keyword_storage_modifier_ref = "keyword"; keyword_storage_type = "keyword"; punctuation_bracket = "punctuation"; punctuation_delimiter = "punctuation"; text = "string"; type_builtin = "type"; }; description = "Highlighter groups to be aliased by other groups"; }; extraAliases = mkOption { type = types.attrsOf types.str; default = { }; description = "Extra highlighter groups to be aliased by other groups"; }; languages = mkOption { type = types.attrsOf languageModule; default = { }; }; }; config = let allGroups = attrsets.recursiveUpdate cfg.highlighterGroups cfg.extraHighlighterGroups; aliases = attrsets.recursiveUpdate cfg.aliases cfg.extraAliases; toTs = name: "ts_${strings.concatStringsSep "_" (strings.splitString "." name)}"; toScm = name: strings.concatStringsSep "." (strings.splitString "_" name); definedFaces = attrsets.mapAttrs' (name: value: { inherit value; name = toTs name; }) allGroups; aliasFaces = attrsets.mapAttrs' (name: value: { name = toTs name; value = "@${toTs value}"; }) aliases; faces = attrsets.recursiveUpdate definedFaces aliasFaces; toml = pkgs.formats.toml { }; srcName = src: lib.removePrefix "/nix/store/" src.outPath; mkGitRepo = src: pkgs.runCommandLocal "${src.name}-git" { } '' cp -r --no-preserve=all ${src} $out cd $out if ! test -d $out/.git; then ${lib.getExe pkgs.git} init -b ${srcName src} ${lib.getExe pkgs.git} config user.email "a@b.com" ${lib.getExe pkgs.git} config user.name "a" ${lib.getExe pkgs.git} add . ${lib.getExe pkgs.git} commit -m "Just making a git commit" fi ''; toLanguageConf = name: lang: with lang; { grammar = { inherit (grammar) path; source.git.url = "${mkGitRepo grammar.src}"; source.git.pin = "${srcName grammar.src}"; compile = grammar.compile.command; compile_args = grammar.compile.args; compile_flags = grammar.compile.flags; link = grammar.link.command; link_args = grammar.link.args ++ [ "-o" "${name}.so" ]; link_flags = grammar.link.flags; }; queries = { source.git.url = "${mkGitRepo queries.src}"; source.git.pin = "${srcName queries.src}"; path = if queries.path == null then "runtime/queries/${name}" else queries.path; }; }; in mkIf cfg.enable { assertions = with lib.asserts; ([ ] ++ attrsets.mapAttrsToList (name: _: { assertion = (! (builtins.hasAttr name allGroups)); message = "${name} was both defined and aliased"; }) aliases ); home.packages = [ cfg.package ]; xdg.configFile."kak-tree-sitter/config.toml" = { source = toml.generate "config.toml" { highlight.groups = builtins.map toScm (builtins.attrNames allGroups ++ builtins.attrNames aliases); features = cfg.features; language = builtins.mapAttrs toLanguageConf cfg.languages; }; onChange = '' export PATH=$PATH:${lib.getBin pkgs.gcc} ${cfg.package}/bin/ktsctl sync -a ''; }; programs.my-kakoune.extraFaces = faces; }; }