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.

This commit is contained in:
Griatch 2016-06-26 14:21:19 +02:00
parent 96542418c0
commit a95e32b372

View file

@ -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 <n>
+desc/list - list descriptions (abbreviated)
+desc/list/full - list descriptions (full texts)
+desc/add [<n> =] <text> - add new desc or replace desc <n>
+desc/edit [n] - open editor to modify current or desc <n>
+desc/del [n] - delete current or desc <n>
+desc/swap <n1>-<n2> - reorder list by swapping #n1 and <n2>
+desc/set <n> - set which desc <n> as active desc
+desc [key] - show current desc desc with <key>
+desc <key> = <text> - add/replace desc with <key>
+desc/list - list descriptions (abbreviated)
+desc/list/full - list descriptions (full texts)
+desc/edit <key> - add/edit desc <key> in line editor
+desc/del <key> - delete desc <key>
+desc/swap <key1>-<key2> - swap positions of <key1> and <key2> in list
+desc/set <key> [+key+...] - set desc as default or combine multiple descs
Notes:
When combining multiple descs with +desc/set <key> + <key2> + ...,
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 <num1> - <num2>|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)