From a95e32b372e7fa3174f64aeef97278f2bde0013d Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 26 Jun 2016 14:21:19 +0200 Subject: [PATCH] Update the multidesc contrib to support named descs and adding descs together to form a final desc. This means the format for the multidesc storage has changed, so if you tested it already, delete the .db.multidesc attribute and reload the server. --- evennia/contrib/multidescer.py | 285 +++++++++++++++++++-------------- 1 file changed, 165 insertions(+), 120 deletions(-) diff --git a/evennia/contrib/multidescer.py b/evennia/contrib/multidescer.py index a19d700ca8..f389ba6763 100644 --- a/evennia/contrib/multidescer.py +++ b/evennia/contrib/multidescer.py @@ -26,94 +26,122 @@ Reload the server and you should have the +desc command available (it will replace the default `desc` command). """ +import re from evennia import default_cmds from evennia.utils.utils import crop from evennia.utils.eveditor import EvEditor +# regex for the set functionality +_RE_KEYS = re.compile(r"([\w\s]+)(?:\+*?)", re.U + re.I) + + # Helper functions for the Command +class DescValidateError(ValueError): + "Used for tracebacks from desc systems" + pass -def _update_store(caller, num=0, text=None, replace=False): +def _get_desc(caller, key, raise_error=False): + """ + Retrieve description from storage + + Args: + caller (Object): The caller with the description + key (str): The desc-key to search for + raise_error (bool, optional): Raise an exception + when not finding the description. + + Returns: + desc (str): The matching description or `None` if + `raise_error` is false + + Raises: + DescValidateError: If desc is not found and raise_error=True. + + """ + if not caller.db.multidesc: + _update_store(caller) + key = key.lower() + for mkey, desc in caller.db.multidesc: + if mkey == key: + return desc + if raise_error: + raise DescValidateError("Description key '%s' not found." % key) + return None + +def _update_store(caller, key=None, desc=None, delete=False, swapkey=None): """ Helper function for updating the database store. Args: caller (Object): The caller of the command. - num (int): Index of store to update. - text (str): Description text. - replace (bool): Replace or amend description. + key (str): Description identifier + desc (str): Description text. + delete (bool): Delete given key. + swapkey (str): Swap list positions of `key` and this key. """ if not caller.db.multidesc: # initialize the multidesc attribute - caller.db.multidesc = [caller.db.desc or ""] - if not text: + caller.db.multidesc = [("caller", caller.db.desc or "")] + if not key: return - if replace: - caller.db.multidesc[num] = text + lokey = key.lower() + match = [ind for ind, tup in enumerate(caller.db.multidesc) if tup[0] == lokey] + if match: + idesc = match[0] + if delete: + # delete entry + del caller.db.multidesc[idesc] + elif swapkey: + # swap positions + loswapkey = swapkey.lower() + swapmatch = [ind for ind, tup in enumerate(caller.db.multidesc) if tup[0] == loswapkey] + if swapmatch: + iswap = swapmatch[0] + if idesc == iswap: + raise DescValidateError("Swapping a key with itself does nothing.") + temp = caller.db.multidesc[idesc] + caller.db.multidesc[idesc] = caller.db.multidesc[iswap] + caller.db.multidesc[iswap] = temp + else: + raise DescValidateError("Description key '|w%s|n' not found." % swapkey) + elif desc: + # update in-place + caller.db.multidesc[idesc] = (lokey, desc) + else: + raise DescValidateError("No description was set.") else: - caller.db.multidesc.insert(num, text) - - -class NumValidateError(ValueError): - "Used for tracebacks from _validate_num" - pass - - -def _validate_num(caller, num): - """ - Check so the given num is a valid number in the storage interval. - - Args: - caller (Object): The caller of the command - num (str): The number to validate. - - Returns: - num (int): Returns the valid index (starting from 0) - - Raises: - NumValidateError: For malformed numbers. - - Notes: - This function will also report errors. - - """ - if not caller.db.multidesc: - _update_store(caller) - nlen = len(caller.db.multidesc) - if not num.strip().isdigit() or not (0 < int(num) <= nlen): - raise NumValidateError("%s must be a number between 1 and %i." % - ('%s' % num or "Argument", nlen)) - else: - return int(num) - 1 - + # no matching key + if delete or swapkey: + raise DescValidateError("Description key '|w%s|n' not found." % key) + elif desc: + # insert new at the top of the stack + caller.db.multidesc.insert(0, (lokey, desc)) + else: + raise DescValidateError("No description was set.") # eveditor save/load/quit functions def _save_editor(caller, buffer): "Called when the editor saves its contents" - num = caller.db._multidesc_editnum - replace = caller.db._multidesc_editreplace - if num is not None: - _update_store(caller, num, buffer, replace=replace) - caller.msg("Saved the buffer to description slot %i." % (num + 1)) - return True + key = caller.db._multidesc_editkey + _update_store(caller, key, buffer) + caller.msg("Saved description to key '%s'." % key) + return True def _load_editor(caller): "Called when the editor loads contents" - num = caller.db._multidesc_editnum - if num is not None: - try: - return caller.db.multidesc[num] - except IndexError: - pass + key = caller.db._multidesc_editkey + match = [ind for ind, tup in enumerate(caller.db.multidesc) if tup[0] == key] + if match: + return caller.db.multidesc[match[0]][1] return "" def _quit_editor(caller): "Called when the editor quits" - del caller.db._multidesc_editnum - del caller.db._multidesc_editreplace + del caller.db._multidesc_editkey caller.msg("Exited editor.") @@ -124,14 +152,21 @@ class CmdMultiDesc(default_cmds.MuxCommand): Manage multiple descriptions Usage: - +desc [n] - show current or desc - +desc/list - list descriptions (abbreviated) - +desc/list/full - list descriptions (full texts) - +desc/add [ =] - add new desc or replace desc - +desc/edit [n] - open editor to modify current or desc - +desc/del [n] - delete current or desc - +desc/swap - - reorder list by swapping #n1 and - +desc/set - set which desc as active desc + +desc [key] - show current desc desc with + +desc = - add/replace desc with + +desc/list - list descriptions (abbreviated) + +desc/list/full - list descriptions (full texts) + +desc/edit - add/edit desc in line editor + +desc/del - delete desc + +desc/swap - - swap positions of and in list + +desc/set [+key+...] - set desc as default or combine multiple descs + + Notes: + When combining multiple descs with +desc/set + + ..., + you can add custom text between the '+' signs. Any text + not a '+' or identified a as a desc key will be added to the + final string. Use e.g. ansi line break ||/ to add a new + paragraph. Whitespace around keys (only) will be stripped. """ key = "+desc" @@ -155,83 +190,93 @@ class CmdMultiDesc(default_cmds.MuxCommand): # Note that we list starting from 1, not from 0. _update_store(caller) do_crop = not "full" in switches - outtext = "|wStored descs:|n" - for inum, desc in enumerate(caller.db.multidesc): - outtext += "\n|w%i:|n %s" % (inum + 1, crop(desc) if do_crop else "\n%s" % desc) - caller.msg(outtext) - - elif "add" in switches: - # add text directly to a new entry or an existing one. - if self.rhs: - # this means a '=' was given - num, desc = _validate_num(caller, self.lhs), self.rhs - replace = True + if do_crop: + outtext = ["|w%s:|n %s" % (key, crop(desc)) + for key, desc in caller.db.multidesc] else: - num, desc = 0, self.args - replace = False - if not desc: - caller.msg("No description given.") - return - _update_store(caller, num, desc, replace=replace) - caller.msg("Stored description in slot %i: \"%s\"" % (num + 1, crop(desc))) + outtext = ["\n|w%s:|n|n\n%s\n%s" % (key, "-" * (len(key) + 1), desc) + for key, desc in caller.db.multidesc] + + caller.msg("|wStored descs:|n\n" + "\n".join(outtext)) + return elif "edit" in switches: - # Use the eveditor to edit the description. - if args: - num = _validate_num(caller, args) - replace = True - else: - num = 1 - replace = False + # Use the eveditor to edit/create the named description + if not args: + caller.msg("Usage: %s/edit key" % self.key) + return + # this is used by the editor to know what to edit; it's deleted automatically - caller.db._multidesc_editnum = num - caller.db._multidesc_editreplace = replace + caller.db._multidesc_editkey = args # start the editor EvEditor(caller, loadfunc=_load_editor, savefunc=_save_editor, quitfunc=_quit_editor, key="multidesc editor", persistent=True) elif "delete" in switches or "del" in switches: # delete a multidesc entry. - num = _validate_num(caller, args) - del caller.db.multidesc[num] - caller.msg("Deleted description number %i." % (num + 1)) + if not args: + caller.msg("Usage: %s/delete key" % self.key) + return + _update_store(caller, args, delete=True) + caller.msg("Deleted description with key '%s'." % args) elif "swap" in switches or "switch" in switches or "reorder" in switches: # Reorder list by swapping two entries. We expect numbers starting from 1 - nums = [_validate_num(caller, arg) for arg in args.split("-", 1)] - if not len(nums) == 2: - caller.msg("To swap two desc entries, use |w%s/swap - |n" % (self.key)) - return - num1, num2 = nums - if num1 == num2: - caller.msg("Swapping position with itself changes nothing.") + keys = [arg for arg in args.split("-", 1)] + if not len(keys) == 2: + caller.msg("Usage: %s/swap key1-key2" % self.key) return + key1, key2 = keys # perform the swap - desc1, desc2 = caller.db.multidesc[num1], caller.db.multidesc[num2] - caller.db.multidesc[num2] = desc1 - caller.db.multidesc[num1] = desc2 - caller.msg("Swapped descs numbers %i and %i." % (num1 + 1, num2 + 1)) + _update_store(caller, key1, swapkey=key2) + caller.msg("Swapped descs '%s' and '%s'." % (key1, key2)) elif "set" in switches: - # switches one of the multidescs to be the "active", - # description, with numbers starting from 1 - if args: - num = _validate_num(caller, args) - else: - _update_store(caller) - num = 0 - # activating this description - caller.db.desc = caller.db.multidesc[num] - caller.msg("|wSet description %i as the current one:|n\n%s" % (num + 1, crop(caller.db.desc))) + # switches one (or more) of the multidescs to be the "active" description + _update_store(caller) + if not args: + caller.msg("Usage: %s/set key [+ key2 + key3 + ...]" % self.key) + return + new_desc = [] + multidesc = caller.db.multidesc + for key in args.split("+"): + notfound = True + lokey = key.strip().lower() + for mkey, desc in multidesc: + if lokey == mkey: + new_desc.append(desc) + notfound = False + continue + if notfound: + # if we get here, there is no desc match, we add it as a normal string + new_desc.append(key) + new_desc = "".join(new_desc) + caller.db.desc = new_desc + caller.msg("%s\n\n|wThe above was set as the current description.|n" % new_desc) + + elif self.rhs or "add" in switches: + # add text directly to a new entry or an existing one. + if not (self.lhs and self.rhs): + caller.msg("Usage: %s/add key = description" % self.key) + return + key, desc = self.lhs, self.rhs + _update_store(caller, key, desc) + caller.msg("Stored description '%s': \"%s\"" % (key, crop(desc))) else: # display the current description or a numbered description + _update_store(caller) if args: - num = _validate_num(caller, args) - caller.msg("|wDecsription number %i:|n\n%s" % (num + 1, caller.db.multidesc[num])) + key = args.lower() + multidesc = caller.db.multidesc + for mkey, desc in multidesc: + if key == mkey: + caller.msg("|wDecsription %s:|n\n%s" % (key, desc)) + return + caller.msg("Description key '%s' not found." % key) else: caller.msg("|wCurrent desc:|n\n%s" % caller.db.desc) - except NumValidateError, err: - # This is triggered by _validate_num + except DescValidateError, err: + # This is triggered by _key_to_index caller.msg(err)