mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Cleanup/refactoring of olc menus
This commit is contained in:
parent
e49993fbb5
commit
298b2c23c6
6 changed files with 216 additions and 92 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -26,8 +26,23 @@
|
|||
- A `goto` option callable returning None (rather than the name of the next node) will now rerun the
|
||||
current node instead of failing.
|
||||
- Better error handling of in-node syntax errors.
|
||||
- Improve dedent of default text/helptext formatter. Right-strip whitespace.
|
||||
|
||||
|
||||
### Utils
|
||||
|
||||
- Added new `columnize` function for easily splitting text into multiple columns. At this point it
|
||||
is not working too well with ansi-colored text however.
|
||||
- Extend the `dedent` function with a new `baseline_index` kwarg. This allows to force all lines to
|
||||
the indentation given by the given line regardless of if other lines were already a 0 indentation.
|
||||
This removes a problem with the original `textwrap.dedent` which will only dedent to the least
|
||||
indented part of a text.
|
||||
- Added `exit_cmd` to EvMore pager, to allow for calling a command (e.g. 'look') when leaving the pager.
|
||||
|
||||
### Genaral
|
||||
|
||||
- Start structuring the `CHANGELOG` to list features in more detail.
|
||||
|
||||
|
||||
# Overviews
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,10 @@ def _wizard_options(curr_node, prev_node, next_node, color="|W", search=False):
|
|||
return options
|
||||
|
||||
|
||||
def _set_actioninfo(caller, string):
|
||||
caller.ndb._menutree.actioninfo = string
|
||||
|
||||
|
||||
def _path_cropper(pythonpath):
|
||||
"Crop path to only the last component"
|
||||
return pythonpath.split('.')[-1]
|
||||
|
|
@ -278,30 +282,65 @@ def _format_list_actions(*args, **kwargs):
|
|||
prefix = kwargs.get('prefix', "|WSelect with |w<num>|W. Other actions:|n ")
|
||||
for action in args:
|
||||
actions.append("|w{}|n|W{} |w<num>|n".format(action[0], action[1:]))
|
||||
return prefix + "|W,|n ".join(actions)
|
||||
return prefix + " |W|||n ".join(actions)
|
||||
|
||||
|
||||
def _get_current_value(caller, keyname, formatter=str, only_inherit=False):
|
||||
"Return current value, marking if value comes from parent or set in this prototype"
|
||||
prot = _get_menu_prototype(caller)
|
||||
if keyname in prot:
|
||||
# value in current prot
|
||||
def _get_current_value(caller, keyname, comparer=None, formatter=str, only_inherit=False):
|
||||
"""
|
||||
Return current value, marking if value comes from parent or set in this prototype.
|
||||
|
||||
Args:
|
||||
keyname (str): Name of prototoype key to get current value of.
|
||||
comparer (callable, optional): This will be called as comparer(prototype_value,
|
||||
flattened_value) and is expected to return the value to show as the current
|
||||
or inherited one. If not given, a straight comparison is used and what is returned
|
||||
depends on the only_inherit setting.
|
||||
formatter (callable, optional)): This will be called with the result of comparer.
|
||||
only_inherit (bool, optional): If a current value should only be shown if all
|
||||
the values are inherited from the prototype parent (otherwise, show an empty string).
|
||||
Returns:
|
||||
current (str): The current value.
|
||||
|
||||
"""
|
||||
def _default_comparer(protval, flatval):
|
||||
if only_inherit:
|
||||
return ''
|
||||
return "Current {}: {}".format(keyname, formatter(prot[keyname]))
|
||||
flat_prot = _get_flat_menu_prototype(caller)
|
||||
if keyname in flat_prot:
|
||||
# value in flattened prot
|
||||
if keyname == 'prototype_key':
|
||||
# we don't inherit prototype_keys
|
||||
return "[No prototype_key set] (|rnot inherited|n)"
|
||||
return "" if protval else flatval
|
||||
else:
|
||||
ret = "Current {} (|binherited|n): {}".format(keyname, formatter(flat_prot[keyname]))
|
||||
if only_inherit:
|
||||
return "{}\n\n".format(ret)
|
||||
return ret
|
||||
return protval if protval else flatval
|
||||
|
||||
return "[No {} set]".format(keyname)
|
||||
if not callable(comparer):
|
||||
comparer = _default_comparer
|
||||
|
||||
prot = _get_menu_prototype(caller)
|
||||
flat_prot = _get_flat_menu_prototype(caller)
|
||||
|
||||
out = ""
|
||||
if keyname in prot:
|
||||
if keyname in flat_prot:
|
||||
out = formatter(comparer(prot[keyname], flat_prot[keyname]))
|
||||
if only_inherit:
|
||||
if out:
|
||||
return "|WCurrent|n {} |W(|binherited|W):|n {}".format(keyname, out)
|
||||
return ""
|
||||
else:
|
||||
if out:
|
||||
return "|WCurrent|n {}|W:|n {}".format(keyname, out)
|
||||
return "|W[No {} set]|n".format(keyname)
|
||||
elif only_inherit:
|
||||
return ""
|
||||
else:
|
||||
out = formatter(prot[keyname])
|
||||
return "|WCurrent|n {}|W:|n {}".format(keyname, out)
|
||||
elif keyname in flat_prot:
|
||||
out = formatter(flat_prot[keyname])
|
||||
if out:
|
||||
return "|WCurrent|n {} |W(|n|binherited|W):|n {}".format(keyname, out)
|
||||
else:
|
||||
return ""
|
||||
elif only_inherit:
|
||||
return ""
|
||||
else:
|
||||
return "|W[No {} set]|n".format(keyname)
|
||||
|
||||
|
||||
def _default_parse(raw_inp, choices, *args):
|
||||
|
|
@ -491,10 +530,9 @@ def node_search_object(caller, raw_inp, **kwargs):
|
|||
text = """
|
||||
Found {num} match{post}.
|
||||
|
||||
{actions}
|
||||
(|RWarning: creating a prototype will |roverwrite|r |Rthe current prototype!)|n""".format(
|
||||
num=nmatches, post="es" if nmatches > 1 else "",
|
||||
actions=_format_list_actions(
|
||||
num=nmatches, post="es" if nmatches > 1 else "")
|
||||
_set_actioninfo(caller, _format_list_actions(
|
||||
"examine", "create prototype from object", prefix="Actions: "))
|
||||
else:
|
||||
text = "Enter search criterion."
|
||||
|
|
@ -758,8 +796,6 @@ def node_prototype_parent(caller):
|
|||
parent is given, this prototype must define the typeclass (next menu node).
|
||||
|
||||
{current}
|
||||
|
||||
{actions}
|
||||
"""
|
||||
helptext = """
|
||||
Prototypes can inherit from one another. Changes in the child replace any values set in a
|
||||
|
|
@ -767,6 +803,8 @@ def node_prototype_parent(caller):
|
|||
prototype to be valid.
|
||||
"""
|
||||
|
||||
_set_actioninfo(caller, _format_list_actions("examine", "add", "remove"))
|
||||
|
||||
ptexts = []
|
||||
if prot_parent_keys:
|
||||
for pkey in utils.make_iter(prot_parent_keys):
|
||||
|
|
@ -782,8 +820,7 @@ def node_prototype_parent(caller):
|
|||
if not ptexts:
|
||||
ptexts.append("[No prototype_parent set]")
|
||||
|
||||
text = text.format(current="\n\n".join(ptexts),
|
||||
actions=_format_list_actions("examine", "add", "remove"))
|
||||
text = text.format(current="\n\n".join(ptexts))
|
||||
|
||||
text = (text, helptext)
|
||||
|
||||
|
|
@ -854,8 +891,6 @@ def node_typeclass(caller):
|
|||
one of the prototype's |cparents|n.
|
||||
|
||||
{current}
|
||||
|
||||
{actions}
|
||||
""".format(current=_get_current_value(caller, "typeclass"),
|
||||
actions="|WSelect with |w<num>|W. Other actions: "
|
||||
"|we|Wxamine |w<num>|W, |wr|Wemove selection")
|
||||
|
|
@ -962,11 +997,15 @@ def node_aliases(caller):
|
|||
|cAliases|n are alternative ways to address an object, next to its |cKey|n. Aliases are not
|
||||
case sensitive.
|
||||
|
||||
{actions}
|
||||
{current}
|
||||
""".format(actions=_format_list_actions("remove",
|
||||
prefix="|w<text>|W to add new alias. Other action: "),
|
||||
current)
|
||||
""".format(current=_get_current_value(
|
||||
caller, 'aliases',
|
||||
comparer=lambda propval, flatval: [al for al in flatval if al not in propval],
|
||||
formatter=lambda lst: "\n" + ", ".join(lst), only_inherit=True))
|
||||
_set_actioninfo(caller,
|
||||
_format_list_actions(
|
||||
"remove",
|
||||
prefix="|w<text>|W to add new alias. Other action: "))
|
||||
|
||||
helptext = """
|
||||
Aliases are fixed alternative identifiers and are stored with the new object.
|
||||
|
|
@ -1009,14 +1048,13 @@ def _display_attribute(attr_tuple):
|
|||
attrkey, value, category, locks = attr_tuple
|
||||
value = protlib.protfunc_parser(value)
|
||||
typ = type(value)
|
||||
out = ("|cAttribute key:|n '{attrkey}' "
|
||||
"(|ccategory:|n {category}, "
|
||||
"|clocks:|n {locks})\n"
|
||||
"|cValue|n |W(parsed to {typ})|n:\n{value}").format(
|
||||
attrkey=attrkey,
|
||||
category=category if category else "|wNone|n",
|
||||
locks=locks if locks else "|wNone|n",
|
||||
typ=typ, value=value)
|
||||
out = ("{attrkey} |c=|n {value} |W({typ}{category}{locks})|n".format(
|
||||
attrkey=attrkey,
|
||||
value=value,
|
||||
typ=typ,
|
||||
category=", category={}".format(category) if category else '',
|
||||
locks=", locks={}".format(";".join(locks)) if any(locks) else ''))
|
||||
|
||||
return out
|
||||
|
||||
|
||||
|
|
@ -1130,6 +1168,12 @@ def _attrs_actions(caller, raw_inp, **kwargs):
|
|||
@list_node(_caller_attrs, _attr_select)
|
||||
def node_attrs(caller):
|
||||
|
||||
def _currentcmp(propval, flatval):
|
||||
"match by key + category"
|
||||
cmp1 = [(tup[0].lower(), tup[2].lower() if tup[2] else None) for tup in propval]
|
||||
return [tup for tup in flatval if (tup[0].lower(), tup[2].lower()
|
||||
if tup[2] else None) not in cmp1]
|
||||
|
||||
text = """
|
||||
|cAttributes|n are custom properties of the object. Enter attributes on one of these forms:
|
||||
|
||||
|
|
@ -1140,8 +1184,14 @@ def node_attrs(caller):
|
|||
To give an attribute without a category but with a lockstring, leave that spot empty
|
||||
(attrname;;lockstring=value). Attribute values can have embedded $protfuncs.
|
||||
|
||||
{actions}
|
||||
""".format(actions=_format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
{current}
|
||||
""".format(
|
||||
current=_get_current_value(
|
||||
caller, "attrs",
|
||||
comparer=_currentcmp,
|
||||
formatter=lambda lst: "\n" + "\n".join(_display_attribute(tup) for tup in lst),
|
||||
only_inherit=True))
|
||||
_set_actioninfo(caller, _format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
|
||||
helptext = """
|
||||
Most commonly, Attributes don't need any categories or locks. If using locks, the lock-types
|
||||
|
|
@ -1290,6 +1340,13 @@ def _tags_actions(caller, raw_inp, **kwargs):
|
|||
|
||||
@list_node(_caller_tags, _tag_select)
|
||||
def node_tags(caller):
|
||||
|
||||
def _currentcmp(propval, flatval):
|
||||
"match by key + category"
|
||||
cmp1 = [(tup[0].lower(), tup[1].lower() if tup[2] else None) for tup in propval]
|
||||
return [tup for tup in flatval if (tup[0].lower(), tup[1].lower()
|
||||
if tup[1] else None) not in cmp1]
|
||||
|
||||
text = """
|
||||
|cTags|n are used to group objects so they can quickly be found later. Enter tags on one of
|
||||
the following forms:
|
||||
|
|
@ -1297,8 +1354,14 @@ def node_tags(caller):
|
|||
tagname;category
|
||||
tagname;category;data
|
||||
|
||||
{actions}
|
||||
""".format(actions=_format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
{current}
|
||||
""".format(
|
||||
current=_get_current_value(
|
||||
caller, 'tags',
|
||||
comparer=_currentcmp,
|
||||
formatter=lambda lst: "\n" + "\n".join(_display_tag(tup) for tup in lst),
|
||||
only_inherit=True))
|
||||
_set_actioninfo(caller, _format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
|
||||
helptext = """
|
||||
Tags are shared between all objects with that tag. So the 'data' field (which is not
|
||||
|
|
@ -1325,18 +1388,7 @@ def _caller_locks(caller):
|
|||
|
||||
|
||||
def _locks_display(caller, lock):
|
||||
try:
|
||||
locktype, lockdef = lock.split(":", 1)
|
||||
except ValueError:
|
||||
txt = "Malformed lock string - Missing ':'"
|
||||
else:
|
||||
txt = ("{lockstr}\n\n"
|
||||
"|WLocktype: |w{locktype}|n\n"
|
||||
"|WLock def: |w{lockdef}|n\n").format(
|
||||
lockstr=lock,
|
||||
locktype=locktype,
|
||||
lockdef=lockdef)
|
||||
return txt
|
||||
return lock
|
||||
|
||||
|
||||
def _lock_select(caller, lockstr):
|
||||
|
|
@ -1395,6 +1447,11 @@ def _locks_actions(caller, raw_inp, **kwargs):
|
|||
@list_node(_caller_locks, _lock_select)
|
||||
def node_locks(caller):
|
||||
|
||||
def _currentcmp(propval, flatval):
|
||||
"match by locktype"
|
||||
cmp1 = [lck.split(":", 1)[0] for lck in propval.split(';')]
|
||||
return ";".join(lstr for lstr in flatval.split(';') if lstr.split(':', 1)[0] not in cmp1)
|
||||
|
||||
text = """
|
||||
The |cLock string|n defines limitations for accessing various properties of the object once
|
||||
it's spawned. The string should be on one of the following forms:
|
||||
|
|
@ -1402,8 +1459,15 @@ def node_locks(caller):
|
|||
locktype:[NOT] lockfunc(args)
|
||||
locktype: [NOT] lockfunc(args) [AND|OR|NOT] lockfunc(args) [AND|OR|NOT] ...
|
||||
|
||||
{action}
|
||||
""".format(action=_format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
{current}{action}
|
||||
""".format(
|
||||
current=_get_current_value(
|
||||
caller, 'locks',
|
||||
comparer=_currentcmp,
|
||||
formatter=lambda lockstr: "\n".join(_locks_display(caller, lstr)
|
||||
for lstr in lockstr.split(';')),
|
||||
only_inherit=True),
|
||||
action=_format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
|
||||
helptext = """
|
||||
Here is an example of two lock strings:
|
||||
|
|
@ -1438,16 +1502,17 @@ def _caller_permissions(caller):
|
|||
return perms
|
||||
|
||||
|
||||
def _display_perm(caller, permission):
|
||||
def _display_perm(caller, permission, only_hierarchy=False):
|
||||
hierarchy = settings.PERMISSION_HIERARCHY
|
||||
perm_low = permission.lower()
|
||||
txt = ''
|
||||
if perm_low in [prm.lower() for prm in hierarchy]:
|
||||
txt = "Permission (in hieararchy): {}".format(
|
||||
", ".join(
|
||||
["|w[{}]|n".format(prm)
|
||||
if prm.lower() == perm_low else "|W{}|n".format(prm)
|
||||
for prm in hierarchy]))
|
||||
else:
|
||||
elif not only_hierarchy:
|
||||
txt = "Permission: '{}'".format(permission)
|
||||
return txt
|
||||
|
||||
|
|
@ -1500,12 +1565,23 @@ def _permissions_actions(caller, raw_inp, **kwargs):
|
|||
@list_node(_caller_permissions, _permission_select)
|
||||
def node_permissions(caller):
|
||||
|
||||
def _currentcmp(pval, fval):
|
||||
cmp1 = [perm.lower() for perm in pval]
|
||||
return [perm for perm in fval if perm.lower() not in cmp1]
|
||||
|
||||
text = """
|
||||
|cPermissions|n are simple strings used to grant access to this object. A permission is used
|
||||
when a |clock|n is checked that contains the |wperm|n or |wpperm|n lock functions.
|
||||
when a |clock|n is checked that contains the |wperm|n or |wpperm|n lock functions. Certain
|
||||
permissions belong in the |cpermission hierarchy|n together with the |Wperm()|n lock
|
||||
function.
|
||||
|
||||
{actions}
|
||||
""".format(actions=_format_list_actions("examine", "remove"), prefix="Actions: ")
|
||||
{current}
|
||||
""".format(
|
||||
current=_get_current_value(
|
||||
caller, 'permissions',
|
||||
comparer=_currentcmp,
|
||||
formatter=lambda lst: "\n" + "\n".join(prm for prm in lst), only_inherit=True))
|
||||
_set_actioninfo(caller, _format_list_actions("examine", "remove", prefix="Actions: "))
|
||||
|
||||
helptext = """
|
||||
Any string can act as a permission as long as a lock is set to look for it. Depending on the
|
||||
|
|
@ -1538,7 +1614,6 @@ def node_location(caller):
|
|||
inventory of |c{caller}|n by default.
|
||||
|
||||
{current}
|
||||
|
||||
""".format(caller=caller.key, current=_get_current_value(caller, "location"))
|
||||
|
||||
helptext = """
|
||||
|
|
@ -1734,9 +1809,13 @@ def node_prototype_tags(caller):
|
|||
|cPrototype-Tags|n can be used to classify and find prototypes in listings Tag names are not
|
||||
case-sensitive and can have not have a custom category.
|
||||
|
||||
{actions}
|
||||
""".format(actions=_format_list_actions(
|
||||
"remove", prefix="|w<text>|n|W to add Tag. Other Action:|n "))
|
||||
{current}
|
||||
""".format(
|
||||
current=_get_current_value(
|
||||
caller, 'prototype_tags',
|
||||
formatter=lambda lst: ", ".join(tg for tg in lst), only_inherit=True))
|
||||
_set_actioninfo(caller, _format_list_actions(
|
||||
"remove", prefix="|w<text>|n|W to add Tag. Other Action:|n "))
|
||||
helptext = """
|
||||
Using prototype-tags is a good way to organize and group large numbers of prototypes by
|
||||
genre, type etc. Under the hood, prototypes' tags will all be stored with the category
|
||||
|
|
@ -1827,8 +1906,14 @@ def node_prototype_locks(caller):
|
|||
|
||||
If unsure, keep the open defaults.
|
||||
|
||||
{actions}
|
||||
""".format(actions=_format_list_actions('examine', "remove", prefix="Actions: "))
|
||||
{current}
|
||||
""".format(
|
||||
current=_get_current_value(
|
||||
caller, 'prototype_locks',
|
||||
formatter=lambda lstring: "\n".join(_locks_display(caller, lstr)
|
||||
for lstr in lstring.split(';')),
|
||||
only_inherit=True))
|
||||
_set_actioninfo(caller, _format_list_actions('examine', "remove", prefix="Actions: "))
|
||||
|
||||
helptext = """
|
||||
Prototype locks can be used to vary access for different tiers of builders. It also allows
|
||||
|
|
@ -2204,9 +2289,8 @@ def node_prototype_load(caller, **kwargs):
|
|||
|
||||
text = """
|
||||
Select a prototype to load. This will replace any prototype currently being edited!
|
||||
|
||||
{actions}
|
||||
""".format(actions=_format_list_actions("examine", "delete"))
|
||||
"""
|
||||
_set_actioninfo(caller, _format_list_actions("examine", "delete"))
|
||||
|
||||
helptext = """
|
||||
Loading a prototype will load it and return you to the main index. It can be a good idea
|
||||
|
|
@ -2230,6 +2314,13 @@ class OLCMenu(EvMenu):
|
|||
A custom EvMenu with a different formatting for the options.
|
||||
|
||||
"""
|
||||
def nodetext_formatter(self, nodetext):
|
||||
"""
|
||||
Format the node text itself.
|
||||
|
||||
"""
|
||||
return super(OLCMenu, self).nodetext_formatter(nodetext)
|
||||
|
||||
def options_formatter(self, optionlist):
|
||||
"""
|
||||
Split the options into two blocks - olc options and normal options
|
||||
|
|
@ -2237,6 +2328,7 @@ class OLCMenu(EvMenu):
|
|||
"""
|
||||
olc_keys = ("index", "forward", "back", "previous", "next", "validate prototype",
|
||||
"save prototype", "load prototype", "spawn prototype", "search objects")
|
||||
actioninfo = self.actioninfo + "\n" if hasattr(self, 'actioninfo') else ''
|
||||
olc_options = []
|
||||
other_options = []
|
||||
for key, desc in optionlist:
|
||||
|
|
@ -2247,7 +2339,8 @@ class OLCMenu(EvMenu):
|
|||
else:
|
||||
other_options.append((key, desc))
|
||||
|
||||
olc_options = " | ".join(olc_options) + " | " + "|wQ|Wuit" if olc_options else ""
|
||||
olc_options = actioninfo + \
|
||||
" |W|||n ".join(olc_options) + " |W|||n " + "|wQ|Wuit" if olc_options else ""
|
||||
other_options = super(OLCMenu, self).options_formatter(other_options)
|
||||
sep = "\n\n" if olc_options and other_options else ""
|
||||
|
||||
|
|
@ -2257,10 +2350,10 @@ class OLCMenu(EvMenu):
|
|||
"""
|
||||
Show help text
|
||||
"""
|
||||
return "|c --- Help ---|n\n" + helptext
|
||||
return "|c --- Help ---|n\n" + utils.dedent(helptext)
|
||||
|
||||
def display_helptext(self):
|
||||
evmore.msg(self.caller, self.helptext, session=self._session)
|
||||
evmore.msg(self.caller, self.helptext, session=self._session, exit_cmd='look')
|
||||
|
||||
|
||||
def start_olc(caller, session=None, prototype=None):
|
||||
|
|
|
|||
|
|
@ -192,16 +192,14 @@ def prototype_to_str(prototype):
|
|||
category = "|ccategory:|n {}".format(category) if category else ''
|
||||
cat_locks = ""
|
||||
if category or locks:
|
||||
cat_locks = "(|ccategory:|n {category}, ".format(
|
||||
cat_locks = " (|ccategory:|n {category}, ".format(
|
||||
category=category if category else "|wNone|n")
|
||||
out.append(
|
||||
"{attrkey} "
|
||||
"{cat_locks}\n"
|
||||
" |c=|n {value}".format(
|
||||
attrkey=attrkey,
|
||||
cat_locks=cat_locks,
|
||||
locks=locks if locks else "|wNone|n",
|
||||
value=value))
|
||||
"{attrkey}{cat_locks} |c=|n {value}".format(
|
||||
attrkey=attrkey,
|
||||
cat_locks=cat_locks,
|
||||
locks=locks if locks else "|wNone|n",
|
||||
value=value))
|
||||
attrs = "|cattrs:|n\n {attrs}".format(attrs="\n ".join(out))
|
||||
tags = prototype.get('tags', '')
|
||||
if tags:
|
||||
|
|
@ -209,10 +207,10 @@ def prototype_to_str(prototype):
|
|||
for (tagkey, category, data) in tags:
|
||||
out.append("{tagkey} (category: {category}{dat})".format(
|
||||
tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else ""))
|
||||
tags = "|ctags:|n\n {tags}".format(tags="\n ".join(out))
|
||||
tags = "|ctags:|n\n {tags}".format(tags=", ".join(out))
|
||||
locks = prototype.get('locks', '')
|
||||
if locks:
|
||||
locks = "|clocks:|n\n {locks}".format(locks="\n ".join(locks.split(";")))
|
||||
locks = "|clocks:|n\n {locks}".format(locks=locks)
|
||||
permissions = prototype.get("permissions", '')
|
||||
if permissions:
|
||||
permissions = "|cpermissions:|n {perms}".format(perms=", ".join(permissions))
|
||||
|
|
|
|||
|
|
@ -167,14 +167,13 @@ from __future__ import print_function
|
|||
import random
|
||||
from builtins import object, range
|
||||
|
||||
from textwrap import dedent
|
||||
from inspect import isfunction, getargspec
|
||||
from django.conf import settings
|
||||
from evennia import Command, CmdSet
|
||||
from evennia.utils import logger
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.ansi import strip_ansi
|
||||
from evennia.utils.utils import mod_import, make_iter, pad, m_len, is_iter
|
||||
from evennia.utils.utils import mod_import, make_iter, pad, m_len, is_iter, dedent
|
||||
from evennia.commands import cmdhandler
|
||||
|
||||
# read from protocol NAWS later?
|
||||
|
|
@ -896,7 +895,7 @@ class EvMenu(object):
|
|||
nodetext (str): The formatted node text.
|
||||
|
||||
"""
|
||||
return dedent(nodetext).strip()
|
||||
return dedent(nodetext.strip('\n'), baseline_index=0).rstrip()
|
||||
|
||||
def helptext_formatter(self, helptext):
|
||||
"""
|
||||
|
|
@ -909,7 +908,7 @@ class EvMenu(object):
|
|||
helptext (str): The formatted help text.
|
||||
|
||||
"""
|
||||
return dedent(helptext).strip()
|
||||
return dedent(helptext.strip('\n'), baseline_index=0).rstrip()
|
||||
|
||||
def options_formatter(self, optionlist):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -122,7 +122,8 @@ class EvMore(object):
|
|||
"""
|
||||
|
||||
def __init__(self, caller, text, always_page=False, session=None,
|
||||
justify_kwargs=None, exit_on_lastpage=False, **kwargs):
|
||||
justify_kwargs=None, exit_on_lastpage=False,
|
||||
exit_cmd=None, **kwargs):
|
||||
"""
|
||||
Initialization of the text handler.
|
||||
|
||||
|
|
@ -141,6 +142,10 @@ class EvMore(object):
|
|||
page being completely filled, exit pager immediately. If unset,
|
||||
another move forward is required to exit. If set, the pager
|
||||
exit message will not be shown.
|
||||
exit_cmd (str, optional): If given, this command-string will be executed on
|
||||
the caller when the more page exits. Note that this will be using whatever
|
||||
cmdset the user had *before* the evmore pager was activated (so none of
|
||||
the evmore commands will be available when this is run).
|
||||
kwargs (any, optional): These will be passed on
|
||||
to the `caller.msg` method.
|
||||
|
||||
|
|
@ -151,6 +156,7 @@ class EvMore(object):
|
|||
self._npages = []
|
||||
self._npos = []
|
||||
self.exit_on_lastpage = exit_on_lastpage
|
||||
self.exit_cmd = exit_cmd
|
||||
self._exit_msg = "Exited |wmore|n pager."
|
||||
if not session:
|
||||
# if not supplied, use the first session to
|
||||
|
|
@ -269,6 +275,8 @@ class EvMore(object):
|
|||
if not quiet:
|
||||
self._caller.msg(text=self._exit_msg, **self._kwargs)
|
||||
self._caller.cmdset.remove(CmdSetMore)
|
||||
if self.exit_cmd:
|
||||
self._caller.execute_cmd(self.exit_cmd, session=self._session)
|
||||
|
||||
|
||||
def msg(caller, text="", always_page=False, session=None,
|
||||
|
|
|
|||
|
|
@ -160,12 +160,16 @@ def crop(text, width=None, suffix="[...]"):
|
|||
return to_str(utext)
|
||||
|
||||
|
||||
def dedent(text):
|
||||
def dedent(text, baseline_index=None):
|
||||
"""
|
||||
Safely clean all whitespace at the left of a paragraph.
|
||||
|
||||
Args:
|
||||
text (str): The text to dedent.
|
||||
baseline_index (int or None, optional): Which row to use as a 'base'
|
||||
for the indentation. Lines will be dedented to this level but
|
||||
no further. If None, indent so as to completely deindent the
|
||||
least indented text.
|
||||
|
||||
Returns:
|
||||
text (str): Dedented string.
|
||||
|
|
@ -178,7 +182,14 @@ def dedent(text):
|
|||
"""
|
||||
if not text:
|
||||
return ""
|
||||
return textwrap.dedent(text)
|
||||
if baseline_index is None:
|
||||
return textwrap.dedent(text)
|
||||
else:
|
||||
lines = text.split('\n')
|
||||
baseline = lines[baseline_index]
|
||||
spaceremove = len(baseline) - len(baseline.lstrip(' '))
|
||||
return "\n".join(line[min(spaceremove, len(line) - len(line.lstrip(' '))):]
|
||||
for line in lines)
|
||||
|
||||
|
||||
def justify(text, width=None, align="f", indent=0):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue