ref-filter: implement %(if), %(then), and %(else) atoms
Implement %(if), %(then) and %(else) atoms. Used as %(if)...%(then)...%(end) or %(if)...%(then)...%(else)...%(end). If the format string between %(if) and %(then) expands to an empty string, or to only whitespaces, then the whole %(if)...%(end) expands to the string following %(then). Otherwise, it expands to the string following %(else), if any. Nesting of this construct is possible. This is in preparation for porting over `git branch -l` to use ref-filter APIs for printing. Add documentation and tests regarding the same. Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:

committed by
Junio C Hamano

parent
1d1bdafd64
commit
c58492d434
134
ref-filter.c
134
ref-filter.c
@ -22,6 +22,12 @@ struct align {
|
||||
unsigned int width;
|
||||
};
|
||||
|
||||
struct if_then_else {
|
||||
unsigned int then_atom_seen : 1,
|
||||
else_atom_seen : 1,
|
||||
condition_satisfied : 1;
|
||||
};
|
||||
|
||||
/*
|
||||
* An atom is a valid field atom listed below, possibly prefixed with
|
||||
* a "*" to denote deref_tag().
|
||||
@ -214,6 +220,9 @@ static struct {
|
||||
{ "color", FIELD_STR, color_atom_parser },
|
||||
{ "align", FIELD_STR, align_atom_parser },
|
||||
{ "end" },
|
||||
{ "if" },
|
||||
{ "then" },
|
||||
{ "else" },
|
||||
};
|
||||
|
||||
#define REF_FORMATTING_STATE_INIT { 0, NULL }
|
||||
@ -221,7 +230,7 @@ static struct {
|
||||
struct ref_formatting_stack {
|
||||
struct ref_formatting_stack *prev;
|
||||
struct strbuf output;
|
||||
void (*at_end)(struct ref_formatting_stack *stack);
|
||||
void (*at_end)(struct ref_formatting_stack **stack);
|
||||
void *at_end_data;
|
||||
};
|
||||
|
||||
@ -354,13 +363,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
|
||||
*stack = prev;
|
||||
}
|
||||
|
||||
static void end_align_handler(struct ref_formatting_stack *stack)
|
||||
static void end_align_handler(struct ref_formatting_stack **stack)
|
||||
{
|
||||
struct align *align = (struct align *)stack->at_end_data;
|
||||
struct ref_formatting_stack *cur = *stack;
|
||||
struct align *align = (struct align *)cur->at_end_data;
|
||||
struct strbuf s = STRBUF_INIT;
|
||||
|
||||
strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
|
||||
strbuf_swap(&stack->output, &s);
|
||||
strbuf_utf8_align(&s, align->position, align->width, cur->output.buf);
|
||||
strbuf_swap(&cur->output, &s);
|
||||
strbuf_release(&s);
|
||||
}
|
||||
|
||||
@ -374,6 +384,104 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s
|
||||
new->at_end_data = &atomv->u.align;
|
||||
}
|
||||
|
||||
static void if_then_else_handler(struct ref_formatting_stack **stack)
|
||||
{
|
||||
struct ref_formatting_stack *cur = *stack;
|
||||
struct ref_formatting_stack *prev = cur->prev;
|
||||
struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;
|
||||
|
||||
if (!if_then_else->then_atom_seen)
|
||||
die(_("format: %%(if) atom used without a %%(then) atom"));
|
||||
|
||||
if (if_then_else->else_atom_seen) {
|
||||
/*
|
||||
* There is an %(else) atom: we need to drop one state from the
|
||||
* stack, either the %(else) branch if the condition is satisfied, or
|
||||
* the %(then) branch if it isn't.
|
||||
*/
|
||||
if (if_then_else->condition_satisfied) {
|
||||
strbuf_reset(&cur->output);
|
||||
pop_stack_element(&cur);
|
||||
} else {
|
||||
strbuf_swap(&cur->output, &prev->output);
|
||||
strbuf_reset(&cur->output);
|
||||
pop_stack_element(&cur);
|
||||
}
|
||||
} else if (!if_then_else->condition_satisfied) {
|
||||
/*
|
||||
* No %(else) atom: just drop the %(then) branch if the
|
||||
* condition is not satisfied.
|
||||
*/
|
||||
strbuf_reset(&cur->output);
|
||||
}
|
||||
|
||||
*stack = cur;
|
||||
free(if_then_else);
|
||||
}
|
||||
|
||||
static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||
{
|
||||
struct ref_formatting_stack *new;
|
||||
struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);
|
||||
|
||||
push_stack_element(&state->stack);
|
||||
new = state->stack;
|
||||
new->at_end = if_then_else_handler;
|
||||
new->at_end_data = if_then_else;
|
||||
}
|
||||
|
||||
static int is_empty(const char *s)
|
||||
{
|
||||
while (*s != '\0') {
|
||||
if (!isspace(*s))
|
||||
return 0;
|
||||
s++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||
{
|
||||
struct ref_formatting_stack *cur = state->stack;
|
||||
struct if_then_else *if_then_else = NULL;
|
||||
|
||||
if (cur->at_end == if_then_else_handler)
|
||||
if_then_else = (struct if_then_else *)cur->at_end_data;
|
||||
if (!if_then_else)
|
||||
die(_("format: %%(then) atom used without an %%(if) atom"));
|
||||
if (if_then_else->then_atom_seen)
|
||||
die(_("format: %%(then) atom used more than once"));
|
||||
if (if_then_else->else_atom_seen)
|
||||
die(_("format: %%(then) atom used after %%(else)"));
|
||||
if_then_else->then_atom_seen = 1;
|
||||
/*
|
||||
* If there exists non-empty string between the 'if' and
|
||||
* 'then' atom then the 'if' condition is satisfied.
|
||||
*/
|
||||
if (cur->output.len && !is_empty(cur->output.buf))
|
||||
if_then_else->condition_satisfied = 1;
|
||||
strbuf_reset(&cur->output);
|
||||
}
|
||||
|
||||
static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||
{
|
||||
struct ref_formatting_stack *prev = state->stack;
|
||||
struct if_then_else *if_then_else = NULL;
|
||||
|
||||
if (prev->at_end == if_then_else_handler)
|
||||
if_then_else = (struct if_then_else *)prev->at_end_data;
|
||||
if (!if_then_else)
|
||||
die(_("format: %%(else) atom used without an %%(if) atom"));
|
||||
if (!if_then_else->then_atom_seen)
|
||||
die(_("format: %%(else) atom used without a %%(then) atom"));
|
||||
if (if_then_else->else_atom_seen)
|
||||
die(_("format: %%(else) atom used more than once"));
|
||||
if_then_else->else_atom_seen = 1;
|
||||
push_stack_element(&state->stack);
|
||||
state->stack->at_end_data = prev->at_end_data;
|
||||
state->stack->at_end = prev->at_end;
|
||||
}
|
||||
|
||||
static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
|
||||
{
|
||||
struct ref_formatting_stack *current = state->stack;
|
||||
@ -381,14 +489,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
|
||||
|
||||
if (!current->at_end)
|
||||
die(_("format: %%(end) atom used without corresponding atom"));
|
||||
current->at_end(current);
|
||||
current->at_end(&state->stack);
|
||||
|
||||
/* Stack may have been popped within at_end(), hence reset the current pointer */
|
||||
current = state->stack;
|
||||
|
||||
/*
|
||||
* Perform quote formatting when the stack element is that of
|
||||
* a supporting atom. If nested then perform quote formatting
|
||||
* only on the topmost supporting atom.
|
||||
*/
|
||||
if (!state->stack->prev->prev) {
|
||||
if (!current->prev->prev) {
|
||||
quote_formatting(&s, current->output.buf, state->quote_style);
|
||||
strbuf_swap(¤t->output, &s);
|
||||
}
|
||||
@ -1049,6 +1160,15 @@ static void populate_value(struct ref_array_item *ref)
|
||||
} else if (!strcmp(name, "end")) {
|
||||
v->handler = end_atom_handler;
|
||||
continue;
|
||||
} else if (!strcmp(name, "if")) {
|
||||
v->handler = if_atom_handler;
|
||||
continue;
|
||||
} else if (!strcmp(name, "then")) {
|
||||
v->handler = then_atom_handler;
|
||||
continue;
|
||||
} else if (!strcmp(name, "else")) {
|
||||
v->handler = else_atom_handler;
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
|
||||
|
Reference in New Issue
Block a user