Fix #117783: Allow Multiple Negatives in Numerical Inputs with Units

Support multiple unary operators before a number where only a single
negative value worked in the past.

Ref !117827.
This commit is contained in:
Campbell Barton 2024-02-06 09:28:30 +11:00
parent 72d324bd81
commit c527056f0c
2 changed files with 45 additions and 2 deletions

View File

@ -1844,6 +1844,18 @@ static bool ch_is_op(char op)
}
}
static bool ch_is_op_unary(char op)
{
switch (op) {
case '+':
case '-':
case '~':
return true;
default:
return false;
}
}
/**
* Helper function for #unit_distribute_negatives to find the next negative to distribute.
*
@ -1912,6 +1924,18 @@ static char *find_next_op(const char *str, char *remaining_str, int remaining_st
return remaining_str + i;
}
/**
* Skip over multiple successive unary operators (typically `-`), skipping spaces.
* This allows for `--90d` to be handled properly, see: #117783.
*/
static char *skip_unary_op(char *str)
{
while (*str == ' ' || ch_is_op_unary(*str)) {
str++;
}
return str;
}
/**
* Put parentheses around blocks of values after negative signs to get rid of an implied "+"
* between numbers without an operation between them. For example:
@ -1937,8 +1961,9 @@ static bool unit_distribute_negatives(char *str, const int str_maxncpy)
memmove(remaining_str + 1, remaining_str, remaining_str_maxncpy - 2);
*remaining_str = '(';
/* Add the ')' before the next operation or at the end. */
remaining_str = find_next_op(str, remaining_str + 1, remaining_str_maxncpy);
/* Add the ')' before the next operation or at the end.
* Unary operators are skipped to allow `--` to be a supported prefix. */
remaining_str = find_next_op(str, skip_unary_op(remaining_str + 1), remaining_str_maxncpy);
remaining_str_maxncpy = str_maxncpy - int(remaining_str - str);
memmove(remaining_str + 1, remaining_str, remaining_str_maxncpy - 2);
*remaining_str = ')';

View File

@ -29,6 +29,24 @@ class UnitsTesting(unittest.TestCase):
('METRIC', 'LENGTH', "", "1+1ft", 1.3048), # no metric units, we default to meters.
('IMPERIAL', 'LENGTH', "", "3+1in+1ft", 0.3048 * 4 + 0.0254), # bigger unit becomes default one!
('IMPERIAL', 'LENGTH', "", "(3+1)in+1ft", 0.3048 + 0.0254 * 4),
# Support successive leading unary operators.
('IMPERIAL', 'LENGTH', "", "-1ft", -0.3048),
('IMPERIAL', 'LENGTH', "", "--1ft", --0.3048),
('IMPERIAL', 'LENGTH', "", "---1ft", ---0.3048),
('IMPERIAL', 'LENGTH', "", "- 1ft", -0.3048),
('IMPERIAL', 'LENGTH', "", "- - 1ft", --0.3048),
('IMPERIAL', 'LENGTH', "", "- - - 1ft", ---0.3048),
('IMPERIAL', 'LENGTH', "", "-+1ft", -+0.3048),
('IMPERIAL', 'LENGTH', "", "+-1ft", +-0.3048),
('METRIC', 'LENGTH', "", "~+-32m", ~+-32),
('METRIC', 'LENGTH', "", "-+~32m", -+~32),
('METRIC', 'LENGTH', "", "~ + - 32m", ~+-32),
('METRIC', 'LENGTH', "", "- + ~ 32m", -+~32),
)
# From 'internal' Blender value to user-friendly printing