Apply semantic highlighting to text.
(cls, text: str, colorize: bool = True)
| 69 | |
| 70 | @classmethod |
| 71 | def highlight(cls, text: str, colorize: bool = True) -> str: |
| 72 | """Apply semantic highlighting to text.""" |
| 73 | if not colorize: |
| 74 | return text |
| 75 | |
| 76 | result = text |
| 77 | |
| 78 | # Handle tags at the start (e.g., [UI], [NET], [SYS]) |
| 79 | tag_match = cls.TAG_PATTERN.match(result) |
| 80 | if tag_match: |
| 81 | full_tag = tag_match.group(0) |
| 82 | tag_name = tag_match.group(1) |
| 83 | # Use source color if available, otherwise use TAG color |
| 84 | normalized = normalize_source(tag_name) |
| 85 | tag_color = Colors.SOURCES.get(normalized, Colors.TAG) |
| 86 | colored_tag = f"{tag_color}[{tag_name}]{Colors.RESET} " |
| 87 | result = colored_tag + result[len(full_tag) :] |
| 88 | |
| 89 | # Highlight URLs first (before strings, to avoid conflict) |
| 90 | def replace_url(match: re.Match[str]) -> str: |
| 91 | return f"{Colors.URL}{Style.DIM}{match.group(1)}{Colors.RESET}" |
| 92 | |
| 93 | result = cls.URL_PATTERN.sub(replace_url, result) |
| 94 | |
| 95 | # Highlight model IDs |
| 96 | def replace_model_id(match: re.Match[str]) -> str: |
| 97 | return f"{Colors.STRING}{match.group(1)}{Colors.RESET}" |
| 98 | |
| 99 | result = cls.MODEL_ID_PATTERN.sub(replace_model_id, result) |
| 100 | |
| 101 | # Highlight quoted strings |
| 102 | def replace_string(match: re.Match[str]) -> str: |
| 103 | # Match group 1 is single-quote, group 2 is double-quote |
| 104 | content = match.group(1) if match.group(1) else match.group(2) |
| 105 | quote = "'" if match.group(1) else '"' |
| 106 | return f"{Colors.STRING}{quote}{content}{quote}{Colors.RESET}" |
| 107 | |
| 108 | result = cls.STRING_PATTERN.sub(replace_string, result) |
| 109 | |
| 110 | # Highlight booleans with distinct colors |
| 111 | def replace_boolean(match: re.Match[str]) -> str: |
| 112 | val = match.group(1) |
| 113 | if val == "True": |
| 114 | return f"{Colors.BOOLEAN_TRUE}{val}{Colors.RESET}" |
| 115 | elif val == "False": |
| 116 | return f"{Colors.BOOLEAN_FALSE}{val}{Colors.RESET}" |
| 117 | else: # None |
| 118 | return f"{Colors.BOOLEAN_NONE}{val}{Colors.RESET}" |
| 119 | |
| 120 | result = cls.BOOLEAN_PATTERN.sub(replace_boolean, result) |
| 121 | |
| 122 | # Highlight numbers (avoid matching inside ANSI codes) |
| 123 | def replace_number(match: re.Match[str]) -> str: |
| 124 | return f"{Colors.NUMBER}{match.group(1)}{Colors.RESET}" |
| 125 | |
| 126 | result = cls.NUMBER_PATTERN.sub(replace_number, result) |
| 127 | |
| 128 | # Highlight hex numbers |