From 7de036c1d9347be464b9ca612930f1763ea0a904 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 28 Nov 2015 20:30:01 +0100 Subject: [PATCH] Merged nested inlinefuncs from the dev branch. Added |-style ansi-markers. Resolves #818. --- evennia/commands/default/player.py | 21 ++++---- evennia/commands/default/tests.py | 2 +- evennia/utils/ansi.py | 18 +++---- evennia/utils/nested_inlinefuncs.py | 79 +++++++++++++++++++++++++---- evennia/utils/tests.py | 6 +-- 5 files changed, 90 insertions(+), 36 deletions(-) diff --git a/evennia/commands/default/player.py b/evennia/commands/default/player.py index 3a78bab454..55aab5eb2d 100644 --- a/evennia/commands/default/player.py +++ b/evennia/commands/default/player.py @@ -538,20 +538,17 @@ class CmdColorTest(MuxPlayerCommand): ap = ansi.ANSI_PARSER # ansi colors # show all ansi color-related codes - col1 = ["%s%s{n" % (code, code.replace("{", "{{")) for code, _ in ap.ext_ansi_map[6:14]] - col2 = ["%s%s{n" % (code, code.replace("{", "{{")) for code, _ in ap.ext_ansi_map[14:22]] - col3 = ["%s%s{n" % (code.replace("\\",""), code.replace("{", "{{").replace("\\", "")) for code, _ in ap.ext_ansi_map[-8:]] + col1 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[6:14]] + col2 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[14:22]] + col3 = ["%s%s|n" % (code.replace("\\",""), code.replace("|", "||").replace("\\", "")) for code, _ in ap.ext_ansi_map[-8:]] col2.extend(["" for i in range(len(col1)-len(col2))]) - #hi = "%ch" - #col2 = ["%s%s{n" % (code, code.replace("%", "%%")) for code, _ in ap.mux_ansi_map[6:]] - #col3 = ["%s%s{n" % (hi + code, (hi + code).replace("%", "%%")) for code, _ in ap.mux_ansi_map[3:-2]] table = utils.format_table([col1, col2, col3]) string = "ANSI colors:" for row in table: string += "\n " + " ".join(row) self.msg(string) - self.msg("{{X : black. {{/ : return, {{- : tab, {{_ : space, {{* : invert, {{u : underline") - self.msg("To combine background and foreground, add background marker last, e.g. {{r{{[b.") + self.msg("||X : black. ||/ : return, ||- : tab, ||_ : space, ||* : invert, ||u : underline") + self.msg("To combine background and foreground, add background marker last, e.g. ||r||[B.") elif self.args.startswith("x"): # show xterm256 table @@ -560,11 +557,11 @@ class CmdColorTest(MuxPlayerCommand): for ig in range(6): for ib in range(6): # foreground table - table[ir].append("{%i%i%i%s{n" % (ir, ig, ib, "{{%i%i%i" % (ir, ig, ib))) + table[ir].append("|%i%i%i%s|n" % (ir, ig, ib, "||%i%i%i" % (ir, ig, ib))) # background table - table[6+ir].append("{[%i%i%i{%i%i%i%s{n" % (ir, ig, ib, + table[6+ir].append("|[%i%i%i|%i%i%i%s|n" % (ir, ig, ib, 5 - ir, 5 - ig, 5 - ib, - "{{[%i%i%i" % (ir, ig, ib))) + "||[%i%i%i" % (ir, ig, ib))) table = self.table_format(table) string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):" for row in table: @@ -573,7 +570,7 @@ class CmdColorTest(MuxPlayerCommand): #self.msg("(e.g. %%123 and %%[123 also work)") else: # malformed input - self.msg("Usage: @color ansi|xterm256") + self.msg("Usage: @color ansi||xterm256") class CmdQuell(MuxPlayerCommand): diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index fc2778f2a4..20d4685496 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -69,7 +69,7 @@ class CommandTest(EvenniaTest): cmdobj.at_post_cmd() # clean out prettytable sugar stored_msg = [args[0] for name, args, kwargs in receiver.msg.mock_calls] - returned_msg = "|".join(_RE.sub("", mess) for mess in stored_msg) + returned_msg = "||".join(_RE.sub("", mess) for mess in stored_msg) returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip() if msg is not None: if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()): diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index 28fa919855..54cc1431ed 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -62,7 +62,7 @@ ANSI_TAB = "\t" ANSI_SPACE = " " # Escapes -ANSI_ESCAPES = ("{{", "\\\\") +ANSI_ESCAPES = ("{{", "\\\\", "\|\|") from collections import OrderedDict _PARSE_CACHE = OrderedDict() @@ -408,14 +408,14 @@ class ANSIParser(object): ## |-style variations - (r'|[r', r'{[500'), - (r'|[g', r'{[050'), - (r'|[y', r'{[550'), - (r'|[b', r'{[005'), - (r'|[m', r'{[505'), - (r'|[c', r'{[055'), - (r'|[w', r'{[555'), # white background - (r'|[x', r'{[222')] # dark grey background + (r'|[r', r'|[500'), + (r'|[g', r'|[050'), + (r'|[y', r'|[550'), + (r'|[b', r'|[005'), + (r'|[m', r'|[505'), + (r'|[c', r'|[055'), + (r'|[w', r'|[555'), # white background + (r'|[x', r'|[222')] # dark grey background # xterm256 {123, %c134. These are replaced directly by # the sub_xterm256 method diff --git a/evennia/utils/nested_inlinefuncs.py b/evennia/utils/nested_inlinefuncs.py index aa59f6497c..f58ae2be46 100644 --- a/evennia/utils/nested_inlinefuncs.py +++ b/evennia/utils/nested_inlinefuncs.py @@ -4,7 +4,7 @@ Inline functions (nested form). This parser accepts nested inlinefunctions on the form ``` -$funcname{arg, arg, ...} +$funcname(arg, arg, ...) ``` embedded in any text where any arg can be another $funcname{} call. @@ -69,7 +69,20 @@ from evennia.utils import utils def pad(*args, **kwargs): """ - Pad to width. $pad{text, width, align, fillchar} + Inlinefunc. Pads text to given width. + + Args: + text (str, optional): Text to pad. + width (str, optional): Will be converted to integer. Width + of padding. + align (str, optional): Alignment of padding; one of 'c', 'l' or 'r'. + fillchar (str, optional): Character used for padding. Defaults to a space. + + Kwargs: + session (Session): Session performing the pad. + + Example: + `$pad(text, width, align, fillchar)` """ text, width, align, fillchar = "", 78, 'c', ' ' @@ -87,7 +100,19 @@ def pad(*args, **kwargs): def crop(*args, **kwargs): """ - Crop to width. $crop{text, width=78, suffix='[...]'} + Inlinefunc. Crops ingoing text to given widths. + + Args: + text (str, optional): Text to crop. + width (str, optional): Will be converted to an integer. Width of + crop in characters. + suffix (str, optional): End string to mark the fact that a part + of the string was cropped. Defaults to `[...]`. + Kwargs: + session (Session): Session performing the crop. + + Example: + `$crop(text, width=78, suffix='[...]')` """ text, width, suffix = "", 78, "[...]" @@ -101,6 +126,37 @@ def crop(*args, **kwargs): return utils.crop(text, width=width, suffix=suffix) +def clr(*args, **kwargs): + """ + Inlinefunc. Colorizes nested text. + + Args: + startclr (str, optional): An ANSI color abbreviation without the + prefix `|`, such as `r` (red foreground) or `[r` (red background). + text (str, optional): Text + endclr (str, optional): The color to use at the end of the string. Defaults + to `|n` (reset-color). + Kwargs: + session (Session): Session object triggering inlinefunc. + + Example: + `$clr(startclr, text, endclr)` + + """ + text = "" + nargs = len(args) + if nargs > 0: + color = args[0].strip() + if nargs > 1: + text = args[1] + text = "|" + color + text + if nargs > 2: + text += "|" + args[2].strip() + else: + text += "|n" + return text + + # we specify a default nomatch function to use if no matching func was # found. This will be overloaded by any nomatch function defined in # the imported modules. @@ -124,16 +180,16 @@ except AttributeError: # regex definitions -_RE_STARTTOKEN = re.compile(r"(?.*?)(?.*?)(?.*?)(?(?(?(?\\'|\\"|\\\)|\\$\w+\{)| # escaped tokens should re-appear in text - (?P[\w\s.-\/#!%\^&\*;:=\-_`~()\[\]]+) # everything else should also be included""", + (?P(?(?\\'|\\"|\\\)|\\$\w+\()| # escaped tokens should re-appear in text + (?P[\w\s.-\/#!%\^&\*;:=\-_`~\(}{\[\]]+) # everything else should also be included""", re.UNICODE + re.IGNORECASE + re.VERBOSE + re.DOTALL) @@ -268,7 +324,7 @@ def parse_inlinefunc(string, strip=False, **kwargs): _PARSING_CACHE[string] = stack # run the stack recursively - def _run_stack(item): + def _run_stack(item, depth=0): retval = item if isinstance(item, tuple): if strip: @@ -282,8 +338,9 @@ def parse_inlinefunc(string, strip=False, **kwargs): args.append("") else: # all other args should merge into one string - args[-1] += _run_stack(arg) + args[-1] += _run_stack(arg, depth=depth+1) # execute the inlinefunc at this point or strip it. + kwargs["inlinefunc_stack_depth"] = depth retval = "" if strip else func(*args, **kwargs) return utils.to_str(retval, force_string=True) diff --git a/evennia/utils/tests.py b/evennia/utils/tests.py index f8b05209da..300b5dc38b 100644 --- a/evennia/utils/tests.py +++ b/evennia/utils/tests.py @@ -326,15 +326,15 @@ class TestNestedInlineFuncs(TestCase): def test_single_func(self): self.assertEqual(nested_inlinefuncs.parse_inlinefunc( - "this is a test with $pad{centered, 20} text in it."), + "this is a test with $pad(centered, 20) text in it."), "this is a test with centered text in it.") def test_nested(self): self.assertEqual(nested_inlinefuncs.parse_inlinefunc( - "this $crop{is a test with $pad{padded, 20} text in $pad{pad2, 10} a crop, 80}"), + "this $crop(is a test with $pad(padded, 20) text in $pad(pad2, 10) a crop, 80)"), "this is a test with padded text in pad2 a crop") def test_escaped(self): self.assertEqual(nested_inlinefuncs.parse_inlinefunc( - "this should be $pad{'''escaped,''' and \"\"\"instead,\"\"\" cropped $crop{with a long,5} text., 80}"), + "this should be $pad('''escaped,''' and \"\"\"instead,\"\"\" cropped $crop(with a long,5) text., 80)"), "this should be escaped, and instead, cropped with text. ")