[Bf-blender-cvs] [61072f08193] blender-v2.82-release: Fix T72964: Text editor Python syntax highlighting for numerals

Peter Lönnebring noreply at git.blender.org
Fri Jan 17 08:02:01 CET 2020


Commit: 61072f08193c44ef345d2513a787e9614ba7c507
Author: Peter Lönnebring
Date:   Fri Jan 17 17:56:29 2020 +1100
Branches: blender-v2.82-release
https://developer.blender.org/rB61072f08193c44ef345d2513a787e9614ba7c507

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

===================================================================

M	source/blender/editors/space_text/text_format_py.c

===================================================================

diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c
index d84beb79be6..243e9908fc8 100644
--- a/source/blender/editors/space_text/text_format_py.c
+++ b/source/blender/editors/space_text/text_format_py.c
@@ -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) {



More information about the Bf-blender-cvs mailing list