forked from bartvdbraak/blender
Fix T72964: Text editor Python syntax highlighting for numerals
Less common notation for numbers wasn't highlighted eg: 0b0, 0o0, 0x0, 1.0e0, 1.0E-1, 100_000j
This commit is contained in:
parent
4aca02064f
commit
61072f0819
@ -170,6 +170,144 @@ static int txtfmt_py_find_bool(const char *string)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Numeral character matching. */
|
||||
#define TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_fn) \
|
||||
{ \
|
||||
uint count = 0; \
|
||||
for (; txtfmt_py_numeral_char_is_fn(*string); string += 1) { \
|
||||
count += 1; \
|
||||
} \
|
||||
return count; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* Binary. */
|
||||
static bool txtfmt_py_numeral_char_is_binary(const char c)
|
||||
{
|
||||
return ELEM(c, '0', '1') || (c == '_');
|
||||
}
|
||||
static uint txtfmt_py_numeral_string_count_binary(const char *string)
|
||||
{
|
||||
TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_binary);
|
||||
}
|
||||
|
||||
/* Octal. */
|
||||
static bool txtfmt_py_numeral_char_is_octal(const char c)
|
||||
{
|
||||
return (c >= '0' && c <= '7') || (c == '_');
|
||||
}
|
||||
static uint txtfmt_py_numeral_string_count_octal(const char *string)
|
||||
{
|
||||
TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_octal);
|
||||
}
|
||||
|
||||
/* Decimal. */
|
||||
static bool txtfmt_py_numeral_char_is_decimal(const char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') || (c == '_');
|
||||
}
|
||||
static uint txtfmt_py_numeral_string_count_decimal(const char *string)
|
||||
{
|
||||
TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_decimal);
|
||||
}
|
||||
|
||||
/* Hexadecimal. */
|
||||
static bool txtfmt_py_numeral_char_is_hexadecimal(const char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c == '_');
|
||||
}
|
||||
static uint txtfmt_py_numeral_string_count_hexadecimal(const char *string)
|
||||
{
|
||||
TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_hexadecimal);
|
||||
}
|
||||
|
||||
/* Zeros. */
|
||||
static bool txtfmt_py_numeral_char_is_zero(const char c)
|
||||
{
|
||||
return (c == '0') || (c == '_');
|
||||
}
|
||||
static uint txtfmt_py_numeral_string_count_zeros(const char *string)
|
||||
{
|
||||
TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_zero);
|
||||
}
|
||||
|
||||
#undef TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL
|
||||
|
||||
static int txtfmt_py_find_numeral_inner(const char *string)
|
||||
{
|
||||
if (string == NULL || *string == '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char first = *string, second = *(string + 1);
|
||||
|
||||
/* Decimal dot must be followed by a digit, any decimal digit.
|
||||
* Note that the there can be any number of leading zeros after
|
||||
* the decimal point (leading zeros are not allowed in integers) */
|
||||
if (first == '.') {
|
||||
if (text_check_digit(second)) {
|
||||
return 1 + txtfmt_py_numeral_string_count_decimal(string + 1);
|
||||
}
|
||||
}
|
||||
else if (first == '0') {
|
||||
/* Numerals starting with '0x' or '0X' is followed by hexadecimal digits. */
|
||||
if (ELEM(second, 'x', 'X')) {
|
||||
return 2 + txtfmt_py_numeral_string_count_hexadecimal(string + 2);
|
||||
}
|
||||
/* Numerals starting with '0o' or '0O' is followed by octal digits. */
|
||||
if (ELEM(second, 'o', 'O')) {
|
||||
return 2 + txtfmt_py_numeral_string_count_octal(string + 2);
|
||||
}
|
||||
/* Numerals starting with '0b' or '0B' is followed by binary digits. */
|
||||
if (ELEM(second, 'b', 'B')) {
|
||||
return 2 + txtfmt_py_numeral_string_count_binary(string + 2);
|
||||
}
|
||||
/* Other numerals starting with '0' can be followed by any number of '0' characters. */
|
||||
if (ELEM(second, '0', '_')) {
|
||||
return 2 + txtfmt_py_numeral_string_count_zeros(string + 2);
|
||||
}
|
||||
}
|
||||
/* Any non-zero digit is the start of a decimal number. */
|
||||
else if (first > '0' && first <= '9') {
|
||||
return 1 + txtfmt_py_numeral_string_count_decimal(string + 1);
|
||||
}
|
||||
/* A single zero is also allowed. */
|
||||
return (first == '0') ? 1 : 0;
|
||||
}
|
||||
|
||||
static int txtfmt_py_literal_numeral(const char *string, char prev_fmt)
|
||||
{
|
||||
if (string == NULL || *string == '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char first = *string, second = *(string + 1);
|
||||
|
||||
if (prev_fmt == FMT_TYPE_NUMERAL) {
|
||||
/* Previous was a number; if immediately followed by 'e' or 'E' and a digit,
|
||||
* it's a base 10 exponent (scientific notation). */
|
||||
if (ELEM(first, 'e', 'E') && (text_check_digit(second) || second == '-')) {
|
||||
return 1 + txtfmt_py_find_numeral_inner(string + 1);
|
||||
}
|
||||
/* Previous was a number; if immediately followed by '.' it's a floating point decimal number.
|
||||
* Note: keep the decimal point, it's needed to allow leading zeros. */
|
||||
if ((prev_fmt == FMT_TYPE_NUMERAL) && (first == '.')) {
|
||||
return txtfmt_py_find_numeral_inner(string);
|
||||
}
|
||||
/* "Imaginary" part of a complex number ends with 'j' */
|
||||
if (ELEM(first, 'j', 'J') && !text_check_digit(second)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if ((prev_fmt != FMT_TYPE_DEFAULT) &&
|
||||
(text_check_digit(first) || (first == '.' && text_check_digit(second)))) {
|
||||
/* New numeral, starting with a digit or a decimal point followed by a digit. */
|
||||
return txtfmt_py_find_numeral_inner(string);
|
||||
}
|
||||
/* Not a literal numeral. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char txtfmt_py_format_identifier(const char *str)
|
||||
{
|
||||
char fmt;
|
||||
@ -289,10 +427,9 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n
|
||||
else if (*str == ' ') {
|
||||
*fmt = FMT_TYPE_WHITESPACE;
|
||||
}
|
||||
/* Numbers (digits not part of an identifier and periods followed by digits) */
|
||||
else if ((prev != FMT_TYPE_DEFAULT && text_check_digit(*str)) ||
|
||||
(*str == '.' && text_check_digit(*(str + 1)))) {
|
||||
*fmt = FMT_TYPE_NUMERAL;
|
||||
/* Literal numerals, "numbers". */
|
||||
else if ((i = txtfmt_py_literal_numeral(str, prev)) > 0) {
|
||||
text_format_fill(&str, &fmt, FMT_TYPE_NUMERAL, i);
|
||||
}
|
||||
/* Booleans */
|
||||
else if (prev != FMT_TYPE_DEFAULT && (i = txtfmt_py_find_bool(str)) != -1) {
|
||||
|
Loading…
Reference in New Issue
Block a user