From 660e09ad0123e5c31bfca8ce0e7d1df835786232 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sun, 28 May 2006 16:38:32 +0200 Subject: [PATCH] Add support for setting color options in the ~/.tig user configuration file Now you can put stuff like this in ~/.tig: # Diff colors color diff-header yellow default color diff-index blue default color diff-chunk magenta default # UI colors color title-blur white blue color title-focus white blue bold --- Makefile | 2 +- tig.c | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 299 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index fb0e31e..1d8e180 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ tig: tig.c tig.1.txt: tig.c sed -n '/\/\*\*/,/\*\*\//p' < $< | \ - sed 's/.*\*\*\///' | \ + sed 's/.*\*\*\/.*//' | \ sed '/^[^*]*\*\*/d' | \ sed 's/\*\///;s/^[^*]*\* *//' > $@ diff --git a/tig.c b/tig.c index c06a0e1..06dd14b 100644 --- a/tig.c +++ b/tig.c @@ -149,6 +149,29 @@ struct ref { static struct ref **get_refs(char *id); +struct int_map { + const char *name; + int namelen; + int value; +}; + +static int +set_from_int_map(struct int_map *map, size_t map_size, + int *value, const char *name, int namelen) +{ + + int i; + + for (i = 0; i < map_size; i++) + if (namelen == map[i].namelen && + !strncasecmp(name, map[i].name, namelen)) { + *value = map[i].value; + return OK; + } + + return ERR; +} + /* * String helpers @@ -536,42 +559,185 @@ parse_options(int argc, char *argv[]) "" -/* - * Line-oriented content detection. - */ +/** + * FILES + * ----- + * '~/.tig':: + * User configuration file. See "<>" + * section for examples. + * + * '.git/config':: + * Repository config file. Read on startup with the help of + * git-repo-config(1). + **/ +/** + * [[config-options]] + * User Configuration file + * ----------------------- + * You can permanently set an option by putting it in the `~/.tig` file. + * The file consists of a series of 'commands'. Each + * line of the file may contain only one command. + * + * The hash mark ('#'), or semi-colon (';') is used as a 'comment' character. + * All text after the comment character to the end of the line is ignored. + * You can use comments to annotate your initialization file. + * + * Configuration Commands + * ~~~~~~~~~~~~~~~~~~~~~~ + * + * -- + * + * *color* object fgcolor bgcolor [attributes]:: + * + * If your terminal supports color, these commands can be used to assign + * foreground/backgound combinations to certain objects. Optionally, an + * attribute can be given as the last parameter. + * + * Valid objects are described in the "<>" section + * below. Note, object names are case-insensitive, and you may use '-', '_', + * and '.' interchangeably. So "Diff-Header", "DIFF_HEADER", and "diff.header" + * are the same. + * + * Valid colors include: 'white', 'black', 'green', 'magenta', 'blue', 'cyan', + * 'yellow', 'red', 'default'. Use 'default' to refer to the default terminal + * colors. + **/ + +static struct int_map color_map[] = { +#define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name } + COLOR_MAP(DEFAULT), + COLOR_MAP(BLACK), + COLOR_MAP(BLUE), + COLOR_MAP(CYAN), + COLOR_MAP(GREEN), + COLOR_MAP(MAGENTA), + COLOR_MAP(RED), + COLOR_MAP(WHITE), + COLOR_MAP(YELLOW), +}; + +/** + * Valid attributes include: 'normal', 'blink', 'bold', 'dim', 'reverse', 'standout', + * and 'underline'. Note, not all attributes may be supported by the terminal. + **/ + +static struct int_map attr_map[] = { +#define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name } + ATTR_MAP(NORMAL), + ATTR_MAP(BLINK), + ATTR_MAP(BOLD), + ATTR_MAP(DIM), + ATTR_MAP(REVERSE), + ATTR_MAP(STANDOUT), + ATTR_MAP(UNDERLINE), +}; + +/** + * Some example color options: + * + * -------------------------------------------------------------------------- + * # Diff colors + * color diff-header yellow default + * color diff-index blue default + * color diff-chunk magenta default + * # UI colors + * color title-blur white blue + * color title-focus white blue bold + * -------------------------------------------------------------------------- + * + * -- + **/ + +/** + * [[color-objs]] + * Color objects + * ~~~~~~~~~~~~~ + * + * -- + **/ #define LINE_INFO \ -/* Line type String to match Foreground Background Attributes - * --------- --------------- ---------- ---------- ---------- */ \ -/* Diff markup */ \ -LINE(DIFF, "diff --git ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -LINE(DIFF_INDEX, "index ", COLOR_BLUE, COLOR_DEFAULT, 0), \ +/** + * Diff markup:: + * + * Objects concerning diff start, chunks and lines added and deleted. + * + * 'diff-header', 'diff-chunk', 'diff-add', 'diff-del' + **/ \ +LINE(DIFF_HEADER, "diff --git ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(DIFF_CHUNK, "@@", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(DIFF_ADD, "+", COLOR_GREEN, COLOR_DEFAULT, 0), \ LINE(DIFF_DEL, "-", COLOR_RED, COLOR_DEFAULT, 0), \ -LINE(DIFF_OLDMODE, "old file mode ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -LINE(DIFF_NEWMODE, "new file mode ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -LINE(DIFF_COPY, "copy ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -LINE(DIFF_RENAME, "rename ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -LINE(DIFF_SIM, "similarity ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -LINE(DIFF_DISSIM, "dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -/* Pretty print commit header */ \ +/** + * Enhanced git diff markup:: + * + * Extra diff information emitted by the git diff machinery, such as mode + * changes, rename detection, and similarity. + * + * 'diff-oldmode', 'diff-newmode', 'diff-copy-from', 'diff-copy-to', + * 'diff-rename-from', 'diff-rename-to', 'diff-similarity' 'diff-dissimilarity' + * 'diff-tree', 'diff-index' + **/ \ +LINE(DIFF_INDEX, "index ", COLOR_BLUE, COLOR_DEFAULT, 0), \ +LINE(DIFF_OLDMODE, "old file mode ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_NEWMODE, "new file mode ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_COPY_FROM, "copy from", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_COPY_TO, "copy to", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_RENAME_FROM, "rename from", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_RENAME_TO, "rename to", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_SIMILARITY, "similarity ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(DIFF_TREE, "diff-tree ", COLOR_BLUE, COLOR_DEFAULT, 0), \ +/** + * Pretty print commit headers:: + * + * Commit diffs and the revision logs are usually formatted using pretty + * printed headers , unless `--pretty=raw` was given. This includes lines, + * such as merge info, commit ID, and author and comitter date. + * + * 'pp-author' 'pp-commit' 'pp-merge' 'pp-date' 'pp-adate' 'pp-cdate' + **/ \ LINE(PP_AUTHOR, "Author: ", COLOR_CYAN, COLOR_DEFAULT, 0), \ LINE(PP_COMMIT, "Commit: ", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(PP_MERGE, "Merge: ", COLOR_BLUE, COLOR_DEFAULT, 0), \ LINE(PP_DATE, "Date: ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(PP_ADATE, "AuthorDate: ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(PP_CDATE, "CommitDate: ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -/* Raw commit header */ \ +/** + * Raw commit header:: + * + * Usually shown when `--pretty=raw` is given, however 'commit' is pretty + * much omnipresent. + * + * 'commit' 'parent' 'tree' 'author' 'committer' + **/ \ LINE(COMMIT, "commit ", COLOR_GREEN, COLOR_DEFAULT, 0), \ LINE(PARENT, "parent ", COLOR_BLUE, COLOR_DEFAULT, 0), \ LINE(TREE, "tree ", COLOR_BLUE, COLOR_DEFAULT, 0), \ LINE(AUTHOR, "author ", COLOR_CYAN, COLOR_DEFAULT, 0), \ LINE(COMMITTER, "committer ", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ -/* Misc */ \ -LINE(DIFF_TREE, "diff-tree ", COLOR_BLUE, COLOR_DEFAULT, 0), \ +/** + * Commit message:: + * + * For now only `Signed-off-by lines` are colorized. + * + * 'signoff' + **/ \ LINE(SIGNOFF, " Signed-off-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ -/* UI colors */ \ +/** + * UI colors:: + * + * Colors for text not matching any of the above: 'default' + * + * Status window colors: 'status' + * + * Title window colors: 'title-blur' 'title-focus' + * + * Cursor line colors: 'cursor' + * + * Main view specific: 'main-date' 'main-author' 'main-commit' 'main-delim' + * 'main-tag' 'main-ref' + **/ \ LINE(DEFAULT, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \ LINE(CURSOR, "", COLOR_WHITE, COLOR_GREEN, A_BOLD), \ LINE(STATUS, "", COLOR_GREEN, COLOR_DEFAULT, 0), \ @@ -582,7 +748,15 @@ LINE(MAIN_AUTHOR, "", COLOR_GREEN, COLOR_DEFAULT, 0), \ LINE(MAIN_COMMIT, "", COLOR_DEFAULT, COLOR_DEFAULT, 0), \ LINE(MAIN_DELIM, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(MAIN_TAG, "", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \ -LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, A_BOLD), +LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, A_BOLD), \ +/** + * -- + **/ + + +/* + * Line-oriented content detection. + */ enum line_type { #define LINE(type, line, fg, bg, attr) \ @@ -592,6 +766,8 @@ enum line_type { }; struct line_info { + const char *name; /* Option name. */ + int namelen; /* Size of option name. */ const char *line; /* The start of line to match. */ int linelen; /* Size of string to match. */ int fg, bg, attr; /* Color and text attributes for the lines. */ @@ -599,7 +775,7 @@ struct line_info { static struct line_info line_info[] = { #define LINE(type, line, fg, bg, attr) \ - { (line), STRING_SIZE(line), (fg), (bg), (attr) } + { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) } LINE_INFO #undef LINE }; @@ -626,6 +802,28 @@ get_line_attr(enum line_type type) return COLOR_PAIR(type) | line_info[type].attr; } +static struct line_info * +get_line_info(char *name, int namelen) +{ + enum line_type type; + int i; + + /* Diff-Header -> DIFF_HEADER */ + for (i = 0; i < namelen; i++) { + if (name[i] == '-') + name[i] = '_'; + else if (name[i] == '.') + name[i] = '_'; + } + + for (type = 0; type < ARRAY_SIZE(line_info); type++) + if (namelen == line_info[type].namelen && + !strncasecmp(line_info[type].name, name, namelen)) + return &line_info[type]; + + return NULL; +} + static void init_colors(void) { @@ -655,6 +853,81 @@ struct line { }; +#define set_color(color, name, namelen) \ + set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, namelen) + +#define set_attribute(attr, name, namelen) \ + set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, namelen) + +static int +read_option(char *opt, int optlen, char *value, int valuelen) +{ + optlen = strcspn(opt, "#;"); + if (optlen == 0) + /* The whole line is a comment. */ + return OK; + + else if (opt[optlen] != 0) + /* Part of the option name is a comment, so the value part + * should be ignored. */ + valuelen = 0; + else + /* Else look for comment endings in the value. */ + valuelen = strcspn(value, "#;"); + + opt[optlen] = value[valuelen] = 0; + + /* Reads: "color" object fgcolor bgcolor [attr] */ + if (!strcmp(opt, "color")) { + struct line_info *info; + + value = chomp_string(value); + valuelen = strcspn(value, " \t"); + info = get_line_info(value, valuelen); + if (!info) + return ERR; + + value = chomp_string(value + valuelen); + valuelen = strcspn(value, " \t"); + if (set_color(&info->fg, value, valuelen) == ERR) + return ERR; + + value = chomp_string(value + valuelen); + valuelen = strcspn(value, " \t"); + if (set_color(&info->bg, value, valuelen) == ERR) + return ERR; + + value = chomp_string(value + valuelen); + if (*value && + set_attribute(&info->attr, value, strlen(value)) == ERR) + return ERR; + + return OK; + } + + return ERR; +} + +static int +load_options(void) +{ + char *home = getenv("HOME"); + char buf[1024]; + FILE *file; + + if (!home || + snprintf(buf, sizeof(buf), "%s/.tig", home) >= sizeof(buf)) + return ERR; + + /* It's ok that the file doesn't exist. */ + file = fopen(buf, "r"); + if (!file) + return OK; + + return read_properties(file, " \t", read_option); +} + + /** * The viewer * ---------- @@ -2510,7 +2783,10 @@ main(int argc, char *argv[]) signal(SIGINT, quit); - /* Load the repo config file first so options can be overwritten from + if (load_options() == ERR) + die("Failed to load user config."); + + /* Load the repo config file so options can be overwritten from * the command line. */ if (load_repo_config() == ERR) die("Failed to load repo config."); -- 2.30.2