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
|
- A `goto` option callable returning None (rather than the name of the next node) will now rerun the
|
||||||
current node instead of failing.
|
current node instead of failing.
|
||||||
- Better error handling of in-node syntax errors.
|
- 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
|
# Overviews
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,10 @@ def _wizard_options(curr_node, prev_node, next_node, color="|W", search=False):
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
def _set_actioninfo(caller, string):
|
||||||
|
caller.ndb._menutree.actioninfo = string
|
||||||
|
|
||||||
|
|
||||||
def _path_cropper(pythonpath):
|
def _path_cropper(pythonpath):
|
||||||
"Crop path to only the last component"
|
"Crop path to only the last component"
|
||||||
return pythonpath.split('.')[-1]
|
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 ")
|
prefix = kwargs.get('prefix', "|WSelect with |w<num>|W. Other actions:|n ")
|
||||||
for action in args:
|
for action in args:
|
||||||
actions.append("|w{}|n|W{} |w<num>|n".format(action[0], action[1:]))
|
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):
|
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"
|
"""
|
||||||
prot = _get_menu_prototype(caller)
|
Return current value, marking if value comes from parent or set in this prototype.
|
||||||
if keyname in prot:
|
|
||||||
# value in current prot
|
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:
|
if only_inherit:
|
||||||
return ''
|
return "" if protval else flatval
|
||||||
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)"
|
|
||||||
else:
|
else:
|
||||||
ret = "Current {} (|binherited|n): {}".format(keyname, formatter(flat_prot[keyname]))
|
return protval if protval else flatval
|
||||||
if only_inherit:
|
|
||||||
return "{}\n\n".format(ret)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
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):
|
def _default_parse(raw_inp, choices, *args):
|
||||||
|
|
@ -491,10 +530,9 @@ def node_search_object(caller, raw_inp, **kwargs):
|
||||||
text = """
|
text = """
|
||||||
Found {num} match{post}.
|
Found {num} match{post}.
|
||||||
|
|
||||||
{actions}
|
|
||||||
(|RWarning: creating a prototype will |roverwrite|r |Rthe current prototype!)|n""".format(
|
(|RWarning: creating a prototype will |roverwrite|r |Rthe current prototype!)|n""".format(
|
||||||
num=nmatches, post="es" if nmatches > 1 else "",
|
num=nmatches, post="es" if nmatches > 1 else "")
|
||||||
actions=_format_list_actions(
|
_set_actioninfo(caller, _format_list_actions(
|
||||||
"examine", "create prototype from object", prefix="Actions: "))
|
"examine", "create prototype from object", prefix="Actions: "))
|
||||||
else:
|
else:
|
||||||
text = "Enter search criterion."
|
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).
|
parent is given, this prototype must define the typeclass (next menu node).
|
||||||
|
|
||||||
{current}
|
{current}
|
||||||
|
|
||||||
{actions}
|
|
||||||
"""
|
"""
|
||||||
helptext = """
|
helptext = """
|
||||||
Prototypes can inherit from one another. Changes in the child replace any values set in a
|
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.
|
prototype to be valid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_set_actioninfo(caller, _format_list_actions("examine", "add", "remove"))
|
||||||
|
|
||||||
ptexts = []
|
ptexts = []
|
||||||
if prot_parent_keys:
|
if prot_parent_keys:
|
||||||
for pkey in utils.make_iter(prot_parent_keys):
|
for pkey in utils.make_iter(prot_parent_keys):
|
||||||
|
|
@ -782,8 +820,7 @@ def node_prototype_parent(caller):
|
||||||
if not ptexts:
|
if not ptexts:
|
||||||
ptexts.append("[No prototype_parent set]")
|
ptexts.append("[No prototype_parent set]")
|
||||||
|
|
||||||
text = text.format(current="\n\n".join(ptexts),
|
text = text.format(current="\n\n".join(ptexts))
|
||||||
actions=_format_list_actions("examine", "add", "remove"))
|
|
||||||
|
|
||||||
text = (text, helptext)
|
text = (text, helptext)
|
||||||
|
|
||||||
|
|
@ -854,8 +891,6 @@ def node_typeclass(caller):
|
||||||
one of the prototype's |cparents|n.
|
one of the prototype's |cparents|n.
|
||||||
|
|
||||||
{current}
|
{current}
|
||||||
|
|
||||||
{actions}
|
|
||||||
""".format(current=_get_current_value(caller, "typeclass"),
|
""".format(current=_get_current_value(caller, "typeclass"),
|
||||||
actions="|WSelect with |w<num>|W. Other actions: "
|
actions="|WSelect with |w<num>|W. Other actions: "
|
||||||
"|we|Wxamine |w<num>|W, |wr|Wemove selection")
|
"|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
|
|cAliases|n are alternative ways to address an object, next to its |cKey|n. Aliases are not
|
||||||
case sensitive.
|
case sensitive.
|
||||||
|
|
||||||
{actions}
|
|
||||||
{current}
|
{current}
|
||||||
""".format(actions=_format_list_actions("remove",
|
""".format(current=_get_current_value(
|
||||||
prefix="|w<text>|W to add new alias. Other action: "),
|
caller, 'aliases',
|
||||||
current)
|
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 = """
|
helptext = """
|
||||||
Aliases are fixed alternative identifiers and are stored with the new object.
|
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
|
attrkey, value, category, locks = attr_tuple
|
||||||
value = protlib.protfunc_parser(value)
|
value = protlib.protfunc_parser(value)
|
||||||
typ = type(value)
|
typ = type(value)
|
||||||
out = ("|cAttribute key:|n '{attrkey}' "
|
out = ("{attrkey} |c=|n {value} |W({typ}{category}{locks})|n".format(
|
||||||
"(|ccategory:|n {category}, "
|
attrkey=attrkey,
|
||||||
"|clocks:|n {locks})\n"
|
value=value,
|
||||||
"|cValue|n |W(parsed to {typ})|n:\n{value}").format(
|
typ=typ,
|
||||||
attrkey=attrkey,
|
category=", category={}".format(category) if category else '',
|
||||||
category=category if category else "|wNone|n",
|
locks=", locks={}".format(";".join(locks)) if any(locks) else ''))
|
||||||
locks=locks if locks else "|wNone|n",
|
|
||||||
typ=typ, value=value)
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1130,6 +1168,12 @@ def _attrs_actions(caller, raw_inp, **kwargs):
|
||||||
@list_node(_caller_attrs, _attr_select)
|
@list_node(_caller_attrs, _attr_select)
|
||||||
def node_attrs(caller):
|
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 = """
|
text = """
|
||||||
|cAttributes|n are custom properties of the object. Enter attributes on one of these forms:
|
|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
|
To give an attribute without a category but with a lockstring, leave that spot empty
|
||||||
(attrname;;lockstring=value). Attribute values can have embedded $protfuncs.
|
(attrname;;lockstring=value). Attribute values can have embedded $protfuncs.
|
||||||
|
|
||||||
{actions}
|
{current}
|
||||||
""".format(actions=_format_list_actions("examine", "remove", prefix="Actions: "))
|
""".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 = """
|
helptext = """
|
||||||
Most commonly, Attributes don't need any categories or locks. If using locks, the lock-types
|
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)
|
@list_node(_caller_tags, _tag_select)
|
||||||
def node_tags(caller):
|
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 = """
|
text = """
|
||||||
|cTags|n are used to group objects so they can quickly be found later. Enter tags on one of
|
|cTags|n are used to group objects so they can quickly be found later. Enter tags on one of
|
||||||
the following forms:
|
the following forms:
|
||||||
|
|
@ -1297,8 +1354,14 @@ def node_tags(caller):
|
||||||
tagname;category
|
tagname;category
|
||||||
tagname;category;data
|
tagname;category;data
|
||||||
|
|
||||||
{actions}
|
{current}
|
||||||
""".format(actions=_format_list_actions("examine", "remove", prefix="Actions: "))
|
""".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 = """
|
helptext = """
|
||||||
Tags are shared between all objects with that tag. So the 'data' field (which is not
|
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):
|
def _locks_display(caller, lock):
|
||||||
try:
|
return lock
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def _lock_select(caller, lockstr):
|
def _lock_select(caller, lockstr):
|
||||||
|
|
@ -1395,6 +1447,11 @@ def _locks_actions(caller, raw_inp, **kwargs):
|
||||||
@list_node(_caller_locks, _lock_select)
|
@list_node(_caller_locks, _lock_select)
|
||||||
def node_locks(caller):
|
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 = """
|
text = """
|
||||||
The |cLock string|n defines limitations for accessing various properties of the object once
|
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:
|
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)
|
||||||
locktype: [NOT] lockfunc(args) [AND|OR|NOT] lockfunc(args) [AND|OR|NOT] ...
|
locktype: [NOT] lockfunc(args) [AND|OR|NOT] lockfunc(args) [AND|OR|NOT] ...
|
||||||
|
|
||||||
{action}
|
{current}{action}
|
||||||
""".format(action=_format_list_actions("examine", "remove", prefix="Actions: "))
|
""".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 = """
|
helptext = """
|
||||||
Here is an example of two lock strings:
|
Here is an example of two lock strings:
|
||||||
|
|
@ -1438,16 +1502,17 @@ def _caller_permissions(caller):
|
||||||
return perms
|
return perms
|
||||||
|
|
||||||
|
|
||||||
def _display_perm(caller, permission):
|
def _display_perm(caller, permission, only_hierarchy=False):
|
||||||
hierarchy = settings.PERMISSION_HIERARCHY
|
hierarchy = settings.PERMISSION_HIERARCHY
|
||||||
perm_low = permission.lower()
|
perm_low = permission.lower()
|
||||||
|
txt = ''
|
||||||
if perm_low in [prm.lower() for prm in hierarchy]:
|
if perm_low in [prm.lower() for prm in hierarchy]:
|
||||||
txt = "Permission (in hieararchy): {}".format(
|
txt = "Permission (in hieararchy): {}".format(
|
||||||
", ".join(
|
", ".join(
|
||||||
["|w[{}]|n".format(prm)
|
["|w[{}]|n".format(prm)
|
||||||
if prm.lower() == perm_low else "|W{}|n".format(prm)
|
if prm.lower() == perm_low else "|W{}|n".format(prm)
|
||||||
for prm in hierarchy]))
|
for prm in hierarchy]))
|
||||||
else:
|
elif not only_hierarchy:
|
||||||
txt = "Permission: '{}'".format(permission)
|
txt = "Permission: '{}'".format(permission)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
|
|
@ -1500,12 +1565,23 @@ def _permissions_actions(caller, raw_inp, **kwargs):
|
||||||
@list_node(_caller_permissions, _permission_select)
|
@list_node(_caller_permissions, _permission_select)
|
||||||
def node_permissions(caller):
|
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 = """
|
text = """
|
||||||
|cPermissions|n are simple strings used to grant access to this object. A permission is used
|
|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}
|
{current}
|
||||||
""".format(actions=_format_list_actions("examine", "remove"), prefix="Actions: ")
|
""".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 = """
|
helptext = """
|
||||||
Any string can act as a permission as long as a lock is set to look for it. Depending on the
|
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.
|
inventory of |c{caller}|n by default.
|
||||||
|
|
||||||
{current}
|
{current}
|
||||||
|
|
||||||
""".format(caller=caller.key, current=_get_current_value(caller, "location"))
|
""".format(caller=caller.key, current=_get_current_value(caller, "location"))
|
||||||
|
|
||||||
helptext = """
|
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
|
|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.
|
case-sensitive and can have not have a custom category.
|
||||||
|
|
||||||
{actions}
|
{current}
|
||||||
""".format(actions=_format_list_actions(
|
""".format(
|
||||||
"remove", prefix="|w<text>|n|W to add Tag. Other Action:|n "))
|
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 = """
|
helptext = """
|
||||||
Using prototype-tags is a good way to organize and group large numbers of prototypes by
|
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
|
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.
|
If unsure, keep the open defaults.
|
||||||
|
|
||||||
{actions}
|
{current}
|
||||||
""".format(actions=_format_list_actions('examine', "remove", prefix="Actions: "))
|
""".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 = """
|
helptext = """
|
||||||
Prototype locks can be used to vary access for different tiers of builders. It also allows
|
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 = """
|
text = """
|
||||||
Select a prototype to load. This will replace any prototype currently being edited!
|
Select a prototype to load. This will replace any prototype currently being edited!
|
||||||
|
"""
|
||||||
{actions}
|
_set_actioninfo(caller, _format_list_actions("examine", "delete"))
|
||||||
""".format(actions=_format_list_actions("examine", "delete"))
|
|
||||||
|
|
||||||
helptext = """
|
helptext = """
|
||||||
Loading a prototype will load it and return you to the main index. It can be a good idea
|
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.
|
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):
|
def options_formatter(self, optionlist):
|
||||||
"""
|
"""
|
||||||
Split the options into two blocks - olc options and normal options
|
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",
|
olc_keys = ("index", "forward", "back", "previous", "next", "validate prototype",
|
||||||
"save prototype", "load prototype", "spawn prototype", "search objects")
|
"save prototype", "load prototype", "spawn prototype", "search objects")
|
||||||
|
actioninfo = self.actioninfo + "\n" if hasattr(self, 'actioninfo') else ''
|
||||||
olc_options = []
|
olc_options = []
|
||||||
other_options = []
|
other_options = []
|
||||||
for key, desc in optionlist:
|
for key, desc in optionlist:
|
||||||
|
|
@ -2247,7 +2339,8 @@ class OLCMenu(EvMenu):
|
||||||
else:
|
else:
|
||||||
other_options.append((key, desc))
|
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)
|
other_options = super(OLCMenu, self).options_formatter(other_options)
|
||||||
sep = "\n\n" if olc_options and other_options else ""
|
sep = "\n\n" if olc_options and other_options else ""
|
||||||
|
|
||||||
|
|
@ -2257,10 +2350,10 @@ class OLCMenu(EvMenu):
|
||||||
"""
|
"""
|
||||||
Show help text
|
Show help text
|
||||||
"""
|
"""
|
||||||
return "|c --- Help ---|n\n" + helptext
|
return "|c --- Help ---|n\n" + utils.dedent(helptext)
|
||||||
|
|
||||||
def display_helptext(self):
|
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):
|
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 ''
|
category = "|ccategory:|n {}".format(category) if category else ''
|
||||||
cat_locks = ""
|
cat_locks = ""
|
||||||
if category or locks:
|
if category or locks:
|
||||||
cat_locks = "(|ccategory:|n {category}, ".format(
|
cat_locks = " (|ccategory:|n {category}, ".format(
|
||||||
category=category if category else "|wNone|n")
|
category=category if category else "|wNone|n")
|
||||||
out.append(
|
out.append(
|
||||||
"{attrkey} "
|
"{attrkey}{cat_locks} |c=|n {value}".format(
|
||||||
"{cat_locks}\n"
|
attrkey=attrkey,
|
||||||
" |c=|n {value}".format(
|
cat_locks=cat_locks,
|
||||||
attrkey=attrkey,
|
locks=locks if locks else "|wNone|n",
|
||||||
cat_locks=cat_locks,
|
value=value))
|
||||||
locks=locks if locks else "|wNone|n",
|
|
||||||
value=value))
|
|
||||||
attrs = "|cattrs:|n\n {attrs}".format(attrs="\n ".join(out))
|
attrs = "|cattrs:|n\n {attrs}".format(attrs="\n ".join(out))
|
||||||
tags = prototype.get('tags', '')
|
tags = prototype.get('tags', '')
|
||||||
if tags:
|
if tags:
|
||||||
|
|
@ -209,10 +207,10 @@ def prototype_to_str(prototype):
|
||||||
for (tagkey, category, data) in tags:
|
for (tagkey, category, data) in tags:
|
||||||
out.append("{tagkey} (category: {category}{dat})".format(
|
out.append("{tagkey} (category: {category}{dat})".format(
|
||||||
tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else ""))
|
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', '')
|
locks = prototype.get('locks', '')
|
||||||
if 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", '')
|
permissions = prototype.get("permissions", '')
|
||||||
if permissions:
|
if permissions:
|
||||||
permissions = "|cpermissions:|n {perms}".format(perms=", ".join(permissions))
|
permissions = "|cpermissions:|n {perms}".format(perms=", ".join(permissions))
|
||||||
|
|
|
||||||
|
|
@ -167,14 +167,13 @@ from __future__ import print_function
|
||||||
import random
|
import random
|
||||||
from builtins import object, range
|
from builtins import object, range
|
||||||
|
|
||||||
from textwrap import dedent
|
|
||||||
from inspect import isfunction, getargspec
|
from inspect import isfunction, getargspec
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia import Command, CmdSet
|
from evennia import Command, CmdSet
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
from evennia.utils.evtable import EvTable
|
from evennia.utils.evtable import EvTable
|
||||||
from evennia.utils.ansi import strip_ansi
|
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
|
from evennia.commands import cmdhandler
|
||||||
|
|
||||||
# read from protocol NAWS later?
|
# read from protocol NAWS later?
|
||||||
|
|
@ -896,7 +895,7 @@ class EvMenu(object):
|
||||||
nodetext (str): The formatted node text.
|
nodetext (str): The formatted node text.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return dedent(nodetext).strip()
|
return dedent(nodetext.strip('\n'), baseline_index=0).rstrip()
|
||||||
|
|
||||||
def helptext_formatter(self, helptext):
|
def helptext_formatter(self, helptext):
|
||||||
"""
|
"""
|
||||||
|
|
@ -909,7 +908,7 @@ class EvMenu(object):
|
||||||
helptext (str): The formatted help text.
|
helptext (str): The formatted help text.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return dedent(helptext).strip()
|
return dedent(helptext.strip('\n'), baseline_index=0).rstrip()
|
||||||
|
|
||||||
def options_formatter(self, optionlist):
|
def options_formatter(self, optionlist):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,8 @@ class EvMore(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, caller, text, always_page=False, session=None,
|
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.
|
Initialization of the text handler.
|
||||||
|
|
||||||
|
|
@ -141,6 +142,10 @@ class EvMore(object):
|
||||||
page being completely filled, exit pager immediately. If unset,
|
page being completely filled, exit pager immediately. If unset,
|
||||||
another move forward is required to exit. If set, the pager
|
another move forward is required to exit. If set, the pager
|
||||||
exit message will not be shown.
|
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
|
kwargs (any, optional): These will be passed on
|
||||||
to the `caller.msg` method.
|
to the `caller.msg` method.
|
||||||
|
|
||||||
|
|
@ -151,6 +156,7 @@ class EvMore(object):
|
||||||
self._npages = []
|
self._npages = []
|
||||||
self._npos = []
|
self._npos = []
|
||||||
self.exit_on_lastpage = exit_on_lastpage
|
self.exit_on_lastpage = exit_on_lastpage
|
||||||
|
self.exit_cmd = exit_cmd
|
||||||
self._exit_msg = "Exited |wmore|n pager."
|
self._exit_msg = "Exited |wmore|n pager."
|
||||||
if not session:
|
if not session:
|
||||||
# if not supplied, use the first session to
|
# if not supplied, use the first session to
|
||||||
|
|
@ -269,6 +275,8 @@ class EvMore(object):
|
||||||
if not quiet:
|
if not quiet:
|
||||||
self._caller.msg(text=self._exit_msg, **self._kwargs)
|
self._caller.msg(text=self._exit_msg, **self._kwargs)
|
||||||
self._caller.cmdset.remove(CmdSetMore)
|
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,
|
def msg(caller, text="", always_page=False, session=None,
|
||||||
|
|
|
||||||
|
|
@ -160,12 +160,16 @@ def crop(text, width=None, suffix="[...]"):
|
||||||
return to_str(utext)
|
return to_str(utext)
|
||||||
|
|
||||||
|
|
||||||
def dedent(text):
|
def dedent(text, baseline_index=None):
|
||||||
"""
|
"""
|
||||||
Safely clean all whitespace at the left of a paragraph.
|
Safely clean all whitespace at the left of a paragraph.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text (str): The text to dedent.
|
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:
|
Returns:
|
||||||
text (str): Dedented string.
|
text (str): Dedented string.
|
||||||
|
|
@ -178,7 +182,14 @@ def dedent(text):
|
||||||
"""
|
"""
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
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):
|
def justify(text, width=None, align="f", indent=0):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue