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:
Karthik Nayak
2017-01-10 14:19:34 +05:30
committed by Junio C Hamano
parent 1d1bdafd64
commit c58492d434
3 changed files with 237 additions and 7 deletions

View File

@ -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(&current->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;