diff --git a/happybot/unicode/unicode.py b/happybot/unicode/unicode.py index d4a230b..ebc2fe0 100644 --- a/happybot/unicode/unicode.py +++ b/happybot/unicode/unicode.py @@ -47,6 +47,7 @@ def irc(chan): from collections import defaultdict from math import ceil, log +from random import choice import json # Copied from: https://www.unicode.org/reports/tr44/#GC_Values_Table @@ -91,7 +92,7 @@ Cn Unassigned a reserved unassigned code point or a noncharacter C Other Cc | Cf | Cs | Co | Cn '''.strip().split('\n') categories = [ row.split('\t', 2) for row in categories ] -categories = { left: right.replace('_', ' ') for left, right, ignore in categories if len(left) == 2 } +categories = { left: (right.replace('_', ' '), righter) for left, right, righter in categories if len(left) == 2 } def utf8uni(ordinal): s = hex(int.from_bytes(chr(ordinal).encode('utf-8'), 'big'))[2:] @@ -109,8 +110,29 @@ invalid = [None, "Cn", False, [""]] unknown = [None, "Cn", False, [""]] def doit(flags, query): - quiet = 'q' in flags - verbose = 'v' in flags + if 'q' in flags and 'v' in flags: + sub1 = 1 + else: + sub1 = 0 + + verbosity = (flags.count('v') - sub1) - (flags.count('q') - sub1) + if verbosity > 0: + quiet = False + verbose = True + elif verbosity < 0: + quiet = True + verbose = False + elif verbosity == 0: + if sub1: + quiet = True + verbose = True + else: + quiet = False + verbose = False + + if verbosity < -1: + return choice(['Ssssshh.', '[silence]', 'Complete quie-- oh, darn, I spoke.', '[the sound of nothing]', '[complete silence]']) + decode = 'd' in flags utf8 = '8' in flags @@ -171,13 +193,15 @@ def doit(flags, query): search.append(missed) results = [[] for _ in range(len(search))] + whymatched = defaultdict(set) + numbers = defaultdict(list) - strings = defaultdict(list) + strings = defaultdict(set) for i, elem in enumerate(search): if isinstance(elem, int): numbers[elem].append(i) elif isinstance(elem, str): - strings[elem.lower()].append(i) + strings[elem.lower()].add(i) numbers = list(sorted(numbers.items(), reverse=True)) # The actual searching. @@ -202,14 +226,27 @@ def doit(flags, query): elif not strings: break for string, indices in strings.items(): - if any(string in name.lower() for name in row[4]): - num = row[0] - if row[1]: - num = -num - cache[num] = row - for index in indices: - filled.add(index) - results[index].append(num) + for i, name in enumerate(row[4]): + if string in name.lower(): + num = row[0] + if row[1]: + num = -num + cache[num] = row + # Prioritize shown name matches first for string matches. + # Also save _why_ it matched. + if i > 0: + whymatched[num].add(name) + for index in indices: + filled.add(index) + if not results[index]: + results[index] = [[], []] + results[index][i > 0].append(num) + break + + # Merge priority and non-priority matches. + for string, indices in strings.items(): + for index in indices & filled: + results[index] = results[index][0] + results[index][1] missing = set(range(len(search))) - filled numbers = defaultdict(list) @@ -267,10 +304,16 @@ def doit(flags, query): fmt = '{char}' join = ' ' elif verbose and not quiet: - fmt = '{code} [{long_category}] {names}: {char}' + if verbosity > 1: + fmt = '{code} [{long_category}, {extra_category}] {names}: {char}' + else: + fmt = '{code} [{long_category}] {names}: {char}' + join = '\n' + elif quiet and verbose: + fmt = '{code} [{category}] {name}: {char}' join = '\n' else: - fmt = '{code} [{category}] {name}: {char}' + fmt = '{code} [{category}] {namee}: {char}' join = '\n' def get_output(results): @@ -292,13 +335,20 @@ def doit(flags, query): if compose: char = '\u25cc' + char code = unif(num) + + extra = '' + if num in whymatched: + extra = ' (' + ', '.join(whymatched[num]) + ')' + output.append(fmt.format( code=code, name=names[0], + namee=names[0] + extra, names=', '.join(names), char=char, category=category, - long_category=categories[category], + long_category=categories[category][0], + extra_category=categories[category][1], )) return join.join(output)