Skip to content

Commit d803c7b

Browse files
committed
Allow use of regular expressions for coloring
When defining colors, strings of the form "/.../" will be interpreted as regular expressions. E.g. color "/([^:]+):([0-9]+):(|([0-9]+):)[ \t]+(note|warning|error): (.*)(\[-W([a-zA-Z0-8_-]+)\]|)/" yellow default bold Closes #1249 Suggested-by: Sebastian Gniazdowski <[email protected]>
1 parent abe103d commit d803c7b

File tree

4 files changed

+59
-10
lines changed

4 files changed

+59
-10
lines changed

doc/tigrc.5.adoc

+4-1
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,8 @@ color diff-chunk magenta default
978978
color "Reported-by:" green default
979979
# View-specific color
980980
color tree.date black cyan bold
981+
# Custom color
982+
color "/(note|warning|error):/" yellow default bold
981983
--------------------------------------------------------------------------
982984
983985
Or in the Git configuration files:
@@ -998,7 +1000,8 @@ Area names::
9981000
9991001
Can be either a built-in area name or a custom quoted string. The
10001002
latter allows custom color rules to be added for lines matching a
1001-
quoted string.
1003+
quoted string. Strings of the form "/.../" are interpreted as
1004+
regular expressions.
10021005
Valid built-in area names are described below. Note, all names are
10031006
case-insensitive, and you may use '-', and '_' interchangeably,
10041007
e.g. "Diff-Header" and "DIFF_HEADER" are the same.

include/tig/line.h

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ struct line_rule {
115115
int namelen; /* Size of option name. */
116116
const char *line; /* The start of line to match. */
117117
int linelen; /* Size of string to match. */
118+
regex_t *regex; /* Pre-compiled regexp. */
118119
struct line_info info; /* List of line info matching this rule. */
119120
};
120121

src/line.c

+15-7
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ get_line_type(const char *line)
3535
for (type = 0; type < line_rules; type++) {
3636
struct line_rule *rule = &line_rule[type];
3737

38+
if (rule->regex && !regexec(rule->regex, line, 0, NULL, 0))
39+
return type;
40+
3841
/* Case insensitive search matches Signed-off-by lines better. */
3942
if (rule->linelen && linelen >= rule->linelen &&
4043
!strncasecmp(rule->line, line, rule->linelen))
@@ -89,7 +92,7 @@ get_line_info(const char *prefix, enum line_type type)
8992
}
9093

9194
static struct line_info *
92-
init_line_info(const char *prefix, const char *name, size_t namelen, const char *line, size_t linelen)
95+
init_line_info(const char *prefix, const char *name, size_t namelen, const char *line, size_t linelen, regex_t *regex)
9396
{
9497
struct line_rule *rule;
9598

@@ -101,6 +104,7 @@ init_line_info(const char *prefix, const char *name, size_t namelen, const char
101104
rule->namelen = namelen;
102105
rule->line = line;
103106
rule->linelen = linelen;
107+
rule->regex = regex;
104108

105109
rule->info.prefix = prefix;
106110
rule->info.fg = COLOR_DEFAULT;
@@ -110,7 +114,7 @@ init_line_info(const char *prefix, const char *name, size_t namelen, const char
110114
}
111115

112116
#define INIT_BUILTIN_LINE_INFO(type, line) \
113-
init_line_info(NULL, #type, STRING_SIZE(#type), (line), STRING_SIZE(line))
117+
init_line_info(NULL, #type, STRING_SIZE(#type), (line), STRING_SIZE(line), NULL)
114118

115119
static struct line_rule *
116120
find_line_rule(struct line_rule *query)
@@ -145,11 +149,15 @@ add_line_rule(const char *prefix, struct line_rule *query)
145149
if (query->name)
146150
return NULL;
147151

148-
/* Quoted line. */
149-
query->line = strndup(query->line, query->linelen);
150-
if (!query->line)
151-
return NULL;
152-
return init_line_info(prefix, "", 0, query->line, query->linelen);
152+
return init_line_info(prefix, "", 0, query->line, query->linelen, query->regex);
153+
}
154+
155+
/* When a rule already exists and we are just adding view-specific
156+
* colors, query->line and query->regex can be freed. */
157+
free((void *) query->line);
158+
if (query->regex) {
159+
regfree(query->regex);
160+
free(query->regex);
153161
}
154162

155163
for (info = &rule->info; info; last = info, info = info->next)

src/options.c

+39-2
Original file line numberDiff line numberDiff line change
@@ -373,9 +373,46 @@ parse_color_name(const char *color, struct line_rule *rule, const char **prefix_
373373

374374
memset(rule, 0, sizeof(*rule));
375375
if (is_quoted(*color)) {
376-
rule->line = color + 1;
377-
rule->linelen = strlen(color) - 2;
376+
/* Interpret strings of the form "/.../" as regular expressions. */
377+
if (strlen(color) >= 4 && color[1] == '/' && color[strlen(color) - 2] == '/') {
378+
int regex_err;
379+
380+
/* rule->line and rule->regex are allocated here rather
381+
* than in add_line_rule() to allow proper error reporting.
382+
* Though only rule->regex will be used for matching regular
383+
* expressions, rule->line and rule->linelen are still filled
384+
* to look up exiting rules when defining view-specific
385+
* colors. */
386+
rule->linelen = strlen(color) - 4;
387+
rule->line = strndup(color + 2, rule->linelen);
388+
if (!rule->line)
389+
return ERROR_OUT_OF_MEMORY;
390+
391+
rule->regex = calloc(1, sizeof(*rule->regex));
392+
if (!rule->regex) {
393+
free((void *) rule->line);
394+
return ERROR_OUT_OF_MEMORY;
395+
}
396+
397+
regex_err = regcomp(rule->regex, rule->line, REG_EXTENDED);
398+
399+
if (regex_err != 0) {
400+
char buf[SIZEOF_STR];
401+
regerror(regex_err, rule->regex, buf, sizeof(buf));
402+
free((void *) rule->line);
403+
free(rule->regex);
404+
return error("Invalid color mapping: %s", buf);
405+
}
406+
} else {
407+
rule->linelen = strlen(color) - 2;
408+
rule->line = strndup(color + 1, rule->linelen);
409+
if (!rule->line)
410+
return ERROR_OUT_OF_MEMORY;
411+
}
378412
} else {
413+
/* Built-in area names are preloaded on first call to
414+
* find_line_rule(), so rule->name is only used to look
415+
* up an existing rule and does not need to persist. */
379416
rule->name = color;
380417
rule->namelen = strlen(color);
381418
}

0 commit comments

Comments
 (0)