diff --git a/src/commands/cmdset.py b/src/commands/cmdset.py index cb93d9cc99..53c3006b8f 100644 --- a/src/commands/cmdset.py +++ b/src/commands/cmdset.py @@ -84,6 +84,7 @@ def instantiate(cmd): return cmd() return cmd + class CmdSet(object): """ This class describes a unique cmdset that understands priorities. CmdSets diff --git a/src/help/models.py b/src/help/models.py index 4f3e31ebaa..3783048453 100644 --- a/src/help/models.py +++ b/src/help/models.py @@ -150,9 +150,3 @@ class HelpEntry(SharedMemoryModel): def __unicode__(self): return u'%s' % self.key - - def get_entrytext_ingame(self): - """ - Gets the entry text for in-game viewing. - """ - return ansi.parse_ansi(self.entrytext) diff --git a/src/players/models.py b/src/players/models.py index cac3f6b257..b758d164ba 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -48,7 +48,6 @@ from src.server import sessionhandler from src.players import manager from src.typeclasses.models import Attribute, TypedObject from src.permissions import permissions -from src.utils.ansi import parse_ansi from src.utils import logger #------------------------------------------------------------ @@ -248,12 +247,11 @@ class PlayerDB(TypedObject): "Delete permission from old ones" return permissions.del_perm(self, perm) - # # PlayerDB class access methods # - def msg(self, message, from_obj=None): + def msg(self, message, from_obj=None, markup=True): """ This duplicates the same-named method on the Character. It forwards messages to the character or uses @@ -268,8 +266,9 @@ class PlayerDB(TypedObject): except Exception: pass if self.at_msg_receive(message, from_obj): - for session in self.sessions: - session.msg(parse_ansi(message)) + for session in object.__getattribute__(self, 'sessions'): + session.msg(message, markup) + def emit_to(self, message, from_obj=None): """ Deprecated. Use msg instead. diff --git a/src/server/session.py b/src/server/session.py index 85eb00a5b8..20ddbcaf2b 100644 --- a/src/server/session.py +++ b/src/server/session.py @@ -125,11 +125,7 @@ class SessionProtocol(StatefulTelnetProtocol): except Exception, e: self.sendLine(str(e)) return - if markup: - message = ansi.parse_ansi(message) - else: - message = ansi.clean_ansi(message) - self.sendLine(message) + self.sendLine(ansi.parse_ansi(message, strip_ansi=not markup)) def get_character(self): """ diff --git a/src/utils/ansi.py b/src/utils/ansi.py index 4593ea5102..d219b6955f 100644 --- a/src/utils/ansi.py +++ b/src/utils/ansi.py @@ -1,11 +1,25 @@ """ -ANSI - gives colour to text. +ANSI - Gives colour to text. + +Use the codes defined in ANSIPARSER in your text +to apply colour to text according to the ANSI standard. + +Examples: + This is %crRed text%cn and this is normal again. + This is {rRed text{n and this is normal again. + +Mostly you should not need to call parse_ansi() explicitly; +it is run by Evennia just before returning data to/from the +user. + """ import re class ANSITable(object): """ - A table of ANSI characters to use when replacing things. + A table defining the + standard ANSI command sequences. + """ ansi = {} ansi["beep"] = "\07" @@ -46,41 +60,19 @@ class ANSITable(object): ansi["tab"] = "\t" ansi["space"] = " " -class BaseParser(object): - def parse_ansi(self, string, strip_ansi=False, strip_formatting=False): - """ - Parses a string, subbing color codes as needed. - """ - if string == None or string == '': - return '' - - # Convert to string to prevent problems with lists, ints, and other types. - string = str(string) - - # if strip_formatting: - # char_return = "" - # char_tab = "" - # char_space = "" - # else: - # char_return = ANSITable.ansi["return"] - # char_tab = ANSITable.ansi["tab"] - # char_space = ANSITable.ansi["space"] - - for sub in self.ansi_subs: - p = re.compile(sub[0], re.DOTALL) - if strip_ansi: - string = p.sub("", string) - else: - string = p.sub(sub[1], string) - - if strip_ansi: - return '%s' % (string) - else: - return '%s%s' % (string, ANSITable.ansi["normal"]) -class MuxANSIParser(BaseParser): +class ANSIParser(object): + """ + A class that parses ansi markup + to ANSI command sequences + """ + def __init__(self): - self.ansi_subs = [ + "Sets the mappings" + + # MUX-style mappings %cr %cn etc + + mux_ansi_map = [ (r'%r', ANSITable.ansi["return"]), (r'%t', ANSITable.ansi["tab"]), (r'%b', ANSITable.ansi["space"]), @@ -104,52 +96,68 @@ class MuxANSIParser(BaseParser): (r'%cC', ANSITable.ansi["back_cyan"]), (r'%cw', ANSITable.ansi["white"]), (r'%cW', ANSITable.ansi["back_white"]), - ] + ] + + # Expanded mapping {r {n etc -class ExtendedANSIParser(MuxANSIParser): - """ - Extends the standard mux colour commands with {-style commands - (shortcuts for writing light/dark text without background) - """ - def __init__(self): - super(ExtendedANSIParser, self).__init__() hilite = ANSITable.ansi['hilite'] normal = ANSITable.ansi['normal'] - self.ansi_subs.extend( [ - (r'{r', hilite + ANSITable.ansi['red']), - (r'{R', normal + ANSITable.ansi['red']), - (r'{g', hilite + ANSITable.ansi['green']), - (r'{G', normal + ANSITable.ansi['green']), - (r'{y', hilite + ANSITable.ansi['yellow']), - (r'{Y', normal + ANSITable.ansi['yellow']), - (r'{b', hilite + ANSITable.ansi['blue']), - (r'{B', normal + ANSITable.ansi['blue']), - (r'{m', hilite + ANSITable.ansi['magenta']), - (r'{M', normal + ANSITable.ansi['magenta']), - (r'{c', hilite + ANSITable.ansi['cyan']), - (r'{C', normal + ANSITable.ansi['cyan']), - (r'{w', hilite + ANSITable.ansi['white']), #white - (r'{W', normal + ANSITable.ansi['white']), #light grey - (r'{x', hilite + ANSITable.ansi['black']), #dark grey - (r'{X', normal + ANSITable.ansi['black']), #pure black - (r'{n', normal) #reset - ] ) - -#ANSI_PARSER = MuxANSIParser() -ANSI_PARSER = ExtendedANSIParser() + ext_ansi_map = [ + (r'{r', hilite + ANSITable.ansi['red']), + (r'{R', normal + ANSITable.ansi['red']), + (r'{g', hilite + ANSITable.ansi['green']), + (r'{G', normal + ANSITable.ansi['green']), + (r'{y', hilite + ANSITable.ansi['yellow']), + (r'{Y', normal + ANSITable.ansi['yellow']), + (r'{b', hilite + ANSITable.ansi['blue']), + (r'{B', normal + ANSITable.ansi['blue']), + (r'{m', hilite + ANSITable.ansi['magenta']), + (r'{M', normal + ANSITable.ansi['magenta']), + (r'{c', hilite + ANSITable.ansi['cyan']), + (r'{C', normal + ANSITable.ansi['cyan']), + (r'{w', hilite + ANSITable.ansi['white']), #white + (r'{W', normal + ANSITable.ansi['white']), #light grey + (r'{x', hilite + ANSITable.ansi['black']), #dark grey + (r'{X', normal + ANSITable.ansi['black']), #pure black + (r'{n', normal) #reset + ] -def parse_ansi(string, strip_ansi=False, strip_formatting=False, parser=ANSI_PARSER): + self.ansi_map = mux_ansi_map + ext_ansi_map + + # prepare regex matching + self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1]) + for sub in self.ansi_map] + # prepare matching ansi codes overall + self.ansi_regex = re.compile("\033\[[0-9;]+m") + + def parse_ansi(self, string, strip_ansi=False): + """ + Parses a string, subbing color codes according to + the stored mapping. + + strip_ansi flag instead removes all ansi markup. + + """ + if not string: + return '' + string = str(string) + for sub in self.ansi_sub: + # go through all available mappings and translate them + string = sub[0].sub(sub[1], string) + if strip_ansi: + # remove all ANSI escape codes + string = self.ansi_regex.sub("", string) + return string + +ANSI_PARSER = ANSIParser() + +# +# Access function +# + +def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER): """ Parses a string, subbing color codes as needed. + """ - return parser.parse_ansi(string, strip_ansi=strip_ansi, - strip_formatting=strip_formatting) -def clean_ansi(string): - """ - Cleans all ansi symbols from a string - """ - # convert all to their ansi counterpart - string = parse_ansi(string) - # next, strip it all away - regex = re.compile("\033\[[0-9;]+m") - return regex.sub("", string) #replace all matches with empty strings + return parser.parse_ansi(string, strip_ansi=strip_ansi)