From fb4a1cdd4e54d4a5fa4d9eddae052cb60844c104 Mon Sep 17 00:00:00 2001 From: Asqiir Date: Fri, 29 Mar 2024 22:44:39 +0100 Subject: [PATCH] Add colortheme configuration to kate (#95) --- modules/apps/default.nix | 2 +- modules/apps/kate.nix | 101 ----------- modules/apps/kate/check-theme-name-free.sh | 45 +++++ modules/apps/kate/default.nix | 192 +++++++++++++++++++++ 4 files changed, 238 insertions(+), 102 deletions(-) delete mode 100644 modules/apps/kate.nix create mode 100755 modules/apps/kate/check-theme-name-free.sh create mode 100644 modules/apps/kate/default.nix diff --git a/modules/apps/default.nix b/modules/apps/default.nix index 96833b65..d7cc36e3 100644 --- a/modules/apps/default.nix +++ b/modules/apps/default.nix @@ -3,6 +3,6 @@ { imports = [ ./konsole.nix - ./kate.nix + ./kate ]; } diff --git a/modules/apps/kate.nix b/modules/apps/kate.nix deleted file mode 100644 index 9c2314d1..00000000 --- a/modules/apps/kate.nix +++ /dev/null @@ -1,101 +0,0 @@ -{ config, lib, ... }: - - - -let - cfg = config.programs.kate; - # compute kate's magic TabHandlingMode - # 0 is not tab & not undoByShiftTab - # 1 is tab & undoByShiftTab - # 2 is not tab & undoByShiftTab - tabHandlingMode = indentSettings: - if (!indentSettings.undoByShiftTab && !indentSettings.tabFromEverywhere) then 0 else - ( - if (indentSettings.undoByShiftTab && indentSettings.tabFromEverywhere) then 1 else - 2 - ); -in -{ - options.programs.kate = { - enable = lib.mkEnableOption '' - Enable configuration management for kate. - ''; - editor = { - tabWidth = lib.mkOption { - description = "The width of a single tab (''\t) sign (in number of spaces)."; - default = 4; - type = lib.types.int; - }; - - indent.showLines = lib.mkOption { - description = "Whether to show the vertical lines that mark each indentation level."; - default = true; - type = lib.types.bool; - }; - - indent.width = lib.mkOption { - description = "The width of each indent level (in number of spaces)."; - default = cfg.editor.tabWidth; - type = lib.types.int; - }; - - indent.autodetect = lib.mkOption { - description = "Whether kate should try to detect indentation for each given file and not impose default indentation settings."; - default = true; - type = lib.types.bool; - }; - - indent.keepExtraSpaces = lib.mkOption { - description = "Whether additional spaces that do not match the indent should be kept when adding/removing indentation level. If these are kept (option to true) then indenting 1 space further (with a default of 4 spaces) will be set to 5 spaces."; - default = false; - type = lib.types.bool; - }; - - indent.replaceWithSpaces = lib.mkOption { - description = "Whether all indentation should be automatically converted to spaces."; - default = false; - type = lib.types.bool; - }; - - indent.backspaceDecreaseIndent = lib.mkOption { - description = "Whether the backspace key in the indentation should decrease indentation by a full level always."; - default = true; - type = lib.types.bool; - }; - - indent.tabFromEverywhere = lib.mkOption { - description = "Whether the tabulator key increases intendation independent from the current cursor position."; - default = false; - type = lib.types.bool; - }; - - indent.undoByShiftTab = lib.mkOption { - description = "Whether to unindent the current line by one level with the shortcut Shift+Tab"; - default = true; - type = lib.types.bool; - }; - }; - }; - - config.assertions = [ - { - assertion = cfg.editor.indent.undoByShiftTab || (!cfg.editor.indent.tabFromEverywhere); - message = "Kate does not support both 'undoByShiftTab' to be disabled and 'tabFromEverywhere' to be enabled at the same time."; - } - ]; - - config.programs.plasma.configFile."katerc" = lib.mkIf cfg.enable { - "KTextEditor Document" = { - "Auto Detect Indent" = cfg.editor.indent.autodetect; - "Indentation Width" = cfg.editor.indent.width; - "Tab Handling" = (tabHandlingMode cfg.editor.indent); - "Tab Width" = cfg.editor.tabWidth; - "Keep Extra Spaces" = cfg.editor.indent.keepExtraSpaces; - "ReplaceTabsDyn" = cfg.editor.indent.replaceWithSpaces; - }; - - "KTextEditor Renderer" = { - "Show Indentation Lines" = cfg.editor.indent.showLines; - }; - }; -} diff --git a/modules/apps/kate/check-theme-name-free.sh b/modules/apps/kate/check-theme-name-free.sh new file mode 100755 index 00000000..996f2ea6 --- /dev/null +++ b/modules/apps/kate/check-theme-name-free.sh @@ -0,0 +1,45 @@ +# there could be a bash shebang to ${pkgs.bash}/bin/bash here + +# https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux/5947802#5947802 +RED='\033[0;31m' + +# https://stackoverflow.com/questions/2924697/how-does-one-output-bold-text-in-bash/2924755#2924755 +BOLD=$(tput bold) +NORMAL=$(tput sgr0) + + +# # ===================================== +# # CHECK THE NUMBER OF ARGS +# # +# # https://www.baeldung.com/linux/bash-check-script-arguments + +if [[ "$#" -ne 1 ]]; then + # https://stackoverflow.com/questions/3005963/how-can-i-have-a-newline-in-a-string-in-sh/3182519#3182519 + >&2 printf "%sIncorrect number of arguments.%s Expected one: The name of the theme that should not already be in use" "${RED}${BOLD}" "${NORMAL}${RED}" + exit 1 +fi + +THEMENAME=$1 + + +# ===================================== +# GO THROUGH THE THEMES +# +# reference the XDG dir as proposed in https://github.com/nix-community/home-manager/pull/4594#issuecomment-1774024207 + +THEME_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/org.kde.syntax-highlighting/themes/" + +# TODO and symlinks! (or: skip over dirs) +find "${THEME_DIR}" \( -type f -o -type l \) | while read -r themefile; do + THIS_THEMENAME=$(jq -r .metadata.name "${themefile}") + + if [[ "${THIS_THEMENAME}" == "${themefile}" ]]; then + # make sure to not look at symbolic links to the nix store + # https://stackoverflow.com/questions/17918367/linux-shell-verify-whether-a-file-exists-and-is-a-soft-link/17918442#17918442 + # https://stackoverflow.com/questions/2172352/in-bash-how-can-i-check-if-a-string-begins-with-some-value/2172367#2172367 + if [[ ! ( -L "${themefile}" && $(readlink -f "${themefile}") == /nix/store/* ) ]]; then + >&2 printf "%s In %s there is already a theme with the name %s (%s).%s You could rename the theme given in config.programs.kate.editor.theme.src by changing the value for metadata.name inside the theme." "${RED}${BOLD}" "${THEME_DIR}" "${THEMENAME}" "${themefile}" "${NORMAL}${RED}" + exit 1 # even on dryrun + fi + fi +done diff --git a/modules/apps/kate/default.nix b/modules/apps/kate/default.nix new file mode 100644 index 00000000..510453d3 --- /dev/null +++ b/modules/apps/kate/default.nix @@ -0,0 +1,192 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.kate; + + # compute kate's magic TabHandlingMode + # 0 is not tab & not undoByShiftTab + # 1 is tab & undoByShiftTab + # 2 is not tab & undoByShiftTab + tabHandlingMode = indentSettings: + if (!indentSettings.undoByShiftTab && !indentSettings.tabFromEverywhere) then 0 else + ( + if (indentSettings.undoByShiftTab && indentSettings.tabFromEverywhere) then 1 else + 2 + ); + + checkThemeNameScript = pkgs.writeShellApplication { + name = "checkThemeName"; + runtimeInputs = with pkgs; [ jq ]; + text = builtins.readFile ./check-theme-name-free.sh; + }; + + checkThemeName = name: + '' + ${checkThemeNameScript}/bin/checkThemeName ${name} + ''; + + script = pkgs.writeScript "kate-check" (checkThemeName cfg.editor.theme.name); +in +{ + options.programs.kate = { + enable = lib.mkEnableOption '' + Enable configuration management for kate. + ''; + + package = lib.mkPackageOption pkgs "kate" { + default = [ "kate" ]; + example = "pkgs.libsForQt5.kate"; + extraDescription = '' + Which kate package to install. Use `pkgs.libsForQt5.kate` in Plasma5 and + `pkgs.kdePackages.kate` in Plasma6. Use `null` if home-manager should not install kate + (use this if you want to manage the settings of this user of a system-wide kate + installation). + ''; + }; + + # ================================== + # INDENTATION + editor = { + tabWidth = lib.mkOption { + description = "The width of a single tab (''\t) sign (in number of spaces)."; + default = 4; + type = lib.types.int; + }; + + indent.showLines = lib.mkOption { + description = "Whether to show the vertical lines that mark each indentation level."; + default = true; + type = lib.types.bool; + }; + + indent.width = lib.mkOption { + description = "The width of each indent level (in number of spaces)."; + default = cfg.editor.tabWidth; + type = lib.types.int; + }; + + indent.autodetect = lib.mkOption { + description = '' + Whether kate should try to detect indentation for each given file and not impose default indentation settings. + ''; + default = true; + type = lib.types.bool; + }; + + indent.keepExtraSpaces = lib.mkOption { + description = "Whether additional spaces that do not match the indent should be kept when adding/removing indentation level. If these are kept (option to true) then indenting 1 space further (with a default of 4 spaces) will be set to 5 spaces."; + default = false; + type = lib.types.bool; + }; + + indent.replaceWithSpaces = lib.mkOption { + description = "Whether all indentation should be automatically converted to spaces."; + default = false; + type = lib.types.bool; + }; + + indent.backspaceDecreaseIndent = lib.mkOption { + description = "Whether the backspace key in the indentation should decrease indentation by a full level always."; + default = true; + type = lib.types.bool; + }; + + indent.tabFromEverywhere = lib.mkOption { + description = "Whether the tabulator key increases intendation independent from the current cursor position."; + default = false; + type = lib.types.bool; + }; + + indent.undoByShiftTab = lib.mkOption { + description = "Whether to unindent the current line by one level with the shortcut Shift+Tab"; + default = true; + type = lib.types.bool; + }; + }; + }; + + config.assertions = [ + { + assertion = cfg.editor.indent.undoByShiftTab || (!cfg.editor.indent.tabFromEverywhere); + message = "Kate does not support both 'undoByShiftTab' to be disabled and 'tabFromEverywhere' to be enabled at the same time."; + } + ]; + + config.programs.plasma.configFile."katerc" = lib.mkIf cfg.enable { + "KTextEditor Document" = { + "Auto Detect Indent".value = cfg.editor.indent.autodetect; + "Indentation Width".value = cfg.editor.indent.width; + "Tab Handling".value = (tabHandlingMode cfg.editor.indent); + "Tab Width".value = cfg.editor.tabWidth; + "Keep Extra Spaces".value = cfg.editor.indent.keepExtraSpaces; + "ReplaceTabsDyn".value = cfg.editor.indent.replaceWithSpaces; + }; + + "KTextEditor Renderer" = { + "Show Indentation Lines".value = cfg.editor.indent.showLines; + + + # COLORTHEME (cannot define this below) + # Do pick the theme if the user chose one, + # Do not touch the theme settings otherwise + "Auto Color Theme Selection".value = lib.mkIf (cfg.editor.theme.name != "") false; + "Color Theme".value = lib.mkIf (cfg.editor.theme.name != "") cfg.editor.theme.name; + }; + }; + + + # ================================== + # COLORTHEME + + options.programs.kate.editor.theme = { + src = lib.mkOption { + description = '' + The path of a theme file for the KDE editor (not the window color scheme). + Obtain a custom one by using the GUI settings in kate. If you want to use a system-wide + editor color scheme set this path to null. If you set the metadata.name entry in the file + to a value that matches the name of a system-wide color scheme undesired behaviour may + occur. The activation will fail if a theme with the filename `.theme` + already exists.''; + type = lib.types.nullOr lib.types.path; + default = null; + }; + + name = lib.mkOption { + description = '' + The name of the theme in use. May be a system theme. + If a theme file was submitted this setting will be set automatically. + ''; + type = lib.types.str; + default = ""; + }; + }; + + config.programs.kate.editor.theme = { + # kate's naming scheme is ${themename}.theme + # which is why we use the same naming scheme here + name = lib.mkIf (cfg.enable && null != cfg.editor.theme.src) (lib.mkForce (builtins.fromJSON (builtins.readFile cfg.editor.theme.src))."metadata"."name"); + }; + + # This won't override existing files since the home-manager activation fails in that case + config.xdg.dataFile."${cfg.editor.theme.name}.theme" = lib.mkIf (cfg.enable && null != cfg.editor.theme.src) + { + source = cfg.editor.theme.src; + target = "org.kde.syntax-highlighting/themes/${cfg.editor.theme.name}.theme"; + }; + + config = { + home.packages = lib.mkIf (cfg.enable && cfg.package != null) [ cfg.package ]; + + # In case of using a custom theme, check that there is no name collision + home.activation.checkKateTheme = lib.mkIf (cfg.enable && cfg.editor.theme.src != null) (lib.hm.dag.entryBefore [ "writeBoundary" ] + # No `$DRY_RUN_CMD`, since even a dryrun should fail if checks fail + '' + ${script} + ''); + + # In case of using a system theme, there should be a check that there exists such a theme + # but I could not figure out where to find them + # That's why there is no check for now + # See also [the original PR](https://github.com/pjones/plasma-manager/pull/95#issue-2206192839) + }; +}