Start add help subtopics
This commit is contained in:
parent
a10a297c55
commit
e301a1410f
3 changed files with 192 additions and 5 deletions
|
|
@ -76,7 +76,14 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
channel/unban[/quiet] channelname[, channelname, ...] = subscribername
|
channel/unban[/quiet] channelname[, channelname, ...] = subscribername
|
||||||
channel/who channelname
|
channel/who channelname
|
||||||
|
|
||||||
This handles all operations on channels.
|
# help-subcategories
|
||||||
|
## channel/list
|
||||||
|
|
||||||
|
This handles all operations on channels. Note that the default operation is to
|
||||||
|
assign a nick/alias for sending to a channel. This would mean you can send
|
||||||
|
using 'foo Hello world' instead of using 'channel foo = Hello world'. Note that
|
||||||
|
aliases set when creating the channel are made available as aliases to subscribers
|
||||||
|
automatically.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "channel"
|
key = "channel"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ set. The normal, database-tied help system is used for collaborative
|
||||||
creation of other help topics such as RP help or game-world aides.
|
creation of other help topics such as RP help or game-world aides.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from evennia.utils.utils import fill, dedent
|
from evennia.utils.utils import fill, dedent
|
||||||
|
|
@ -25,6 +26,12 @@ COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
HELP_MORE = settings.HELP_MORE
|
HELP_MORE = settings.HELP_MORE
|
||||||
CMD_IGNORE_PREFIXES = settings.CMD_IGNORE_PREFIXES
|
CMD_IGNORE_PREFIXES = settings.CMD_IGNORE_PREFIXES
|
||||||
|
|
||||||
|
_RE_HELP_SUBTOPICS_START = re.compile(
|
||||||
|
r"^\s*?#\s*?help[- ]subtopics\s*?$", re.I + re.M)
|
||||||
|
_RE_HELP_SUBTOPIC_SPLIT = re.compile(r"^\s*?(\#{2,6}\s*?\w+?)$", re.M)
|
||||||
|
_RE_HELP_SUBTOPIC_PARSE = re.compile(
|
||||||
|
r"^(?P<nesting>\#{2,6})\s*?(?P<name>.*?)$", re.I + re.M)
|
||||||
|
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("CmdHelp", "CmdSetHelp")
|
__all__ = ("CmdHelp", "CmdSetHelp")
|
||||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||||
|
|
@ -152,6 +159,122 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
self.msg(text=(text, {"type": "help"}))
|
self.msg(text=(text, {"type": "help"}))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_entry_for_subcategories(entry):
|
||||||
|
"""
|
||||||
|
Parse a command docstring for special sub-category blocks:
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entry (str): A help entry to parse
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A mapping that splits the entry into subcategories. This
|
||||||
|
will always hold a key `None` for the main help entry and
|
||||||
|
zero or more keys holding the subcategories. Each is itself
|
||||||
|
a dict with a key `None` for the main text of that subcategory
|
||||||
|
followed by any sub-sub-categories down to a max-depth of 5.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
::
|
||||||
|
|
||||||
|
'''
|
||||||
|
Main topic text
|
||||||
|
|
||||||
|
# help-subcategories
|
||||||
|
|
||||||
|
## foo
|
||||||
|
|
||||||
|
A subcategory of the main entry, accessible as `help topic foo`
|
||||||
|
(or using /, like `help topic/foo`)
|
||||||
|
|
||||||
|
## bar
|
||||||
|
|
||||||
|
Another subcategory, accessed as `help topic bar`
|
||||||
|
(or `help topic/bar`)
|
||||||
|
|
||||||
|
### moo
|
||||||
|
|
||||||
|
A subcategory of bar, accessed as `help bar moo`
|
||||||
|
(or `help bar/moo`)
|
||||||
|
|
||||||
|
#### dum
|
||||||
|
|
||||||
|
A subcategory of moo, accessed `help bar moo dum`
|
||||||
|
(or `help bar/moo/dum`)
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
This will result in this returned entry structure:
|
||||||
|
::
|
||||||
|
|
||||||
|
{
|
||||||
|
None: "Main topic text":
|
||||||
|
"foo": {
|
||||||
|
None: "main topic/foo text"
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
None: "Main topic/bar text",
|
||||||
|
"moo": {
|
||||||
|
None: "topic/bar/moo text"
|
||||||
|
"dum": {
|
||||||
|
None: "topic/bar/moo/dum text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Apart from making
|
||||||
|
sub-categories at the bottom of the entry.
|
||||||
|
|
||||||
|
This will be applied both to command docstrings and database-based help
|
||||||
|
entries.
|
||||||
|
|
||||||
|
"""
|
||||||
|
topic, *subcategories = _RE_HELP_SUBTOPICS_START.split(entry, maxsplit=1)
|
||||||
|
structure = {None: topic.strip()}
|
||||||
|
|
||||||
|
if subcategories:
|
||||||
|
subcategories = subcategories[0]
|
||||||
|
else:
|
||||||
|
return structure
|
||||||
|
|
||||||
|
keypath = []
|
||||||
|
current_nesting = 0
|
||||||
|
subtopic = None
|
||||||
|
|
||||||
|
for part in _RE_HELP_SUBTOPIC_SPLIT.split(subcategories):
|
||||||
|
subtopic_match = _RE_HELP_SUBTOPIC_PARSE.match(part)
|
||||||
|
if subtopic_match:
|
||||||
|
# a new sub(-sub..) category starts.
|
||||||
|
mdict = subtopic_match.groupdict()
|
||||||
|
subtopic = mdict['name'].strip()
|
||||||
|
new_nesting = len(mdict['nesting']) - 1
|
||||||
|
nestdiff = new_nesting - current_nesting
|
||||||
|
if nestdiff < 0:
|
||||||
|
# jumping back up in nesting
|
||||||
|
for _ in range(abs(nestdiff) + 1):
|
||||||
|
try:
|
||||||
|
keypath.pop()
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
keypath.append(subtopic)
|
||||||
|
current_nesting = new_nesting
|
||||||
|
else:
|
||||||
|
# an entry belonging to a subtopic - find the nested location
|
||||||
|
dct = structure
|
||||||
|
if not keypath and subtopic is not None:
|
||||||
|
structure[subtopic] = part.strip()
|
||||||
|
else:
|
||||||
|
for key in keypath:
|
||||||
|
if key in dct:
|
||||||
|
dct = dct[key]
|
||||||
|
else:
|
||||||
|
dct[key] = {
|
||||||
|
None: part.strip()
|
||||||
|
}
|
||||||
|
return structure
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_help_entry(title, help_text, aliases=None, suggested=None):
|
def format_help_entry(title, help_text, aliases=None, suggested=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -260,6 +383,7 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
self.original_args = self.args.strip()
|
self.original_args = self.args.strip()
|
||||||
self.args = self.args.strip().lower()
|
self.args = self.args.strip().lower()
|
||||||
|
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
Run the dynamic help entry creator.
|
Run the dynamic help entry creator.
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ from evennia import DefaultRoom, DefaultExit, ObjectDB
|
||||||
from evennia.commands.default.cmdset_character import CharacterCmdSet
|
from evennia.commands.default.cmdset_character import CharacterCmdSet
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
from evennia.commands.default import (
|
from evennia.commands.default import (
|
||||||
help,
|
help as help_module,
|
||||||
general,
|
general,
|
||||||
system,
|
system,
|
||||||
admin,
|
admin,
|
||||||
|
|
@ -407,6 +407,9 @@ class TestGeneral(CommandTest):
|
||||||
|
|
||||||
|
|
||||||
class TestHelp(CommandTest):
|
class TestHelp(CommandTest):
|
||||||
|
|
||||||
|
maxDiff = None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
# we need to set up a logger here since lunr takes over the logger otherwise
|
# we need to set up a logger here since lunr takes over the logger otherwise
|
||||||
|
|
@ -421,15 +424,68 @@ class TestHelp(CommandTest):
|
||||||
logging.disable(level=logging.ERROR)
|
logging.disable(level=logging.ERROR)
|
||||||
|
|
||||||
def test_help(self):
|
def test_help(self):
|
||||||
self.call(help.CmdHelp(), "", "Admin", cmdset=CharacterCmdSet())
|
self.call(help_module.CmdHelp(), "", "Admin", cmdset=CharacterCmdSet())
|
||||||
|
|
||||||
def test_set_help(self):
|
def test_set_help(self):
|
||||||
self.call(
|
self.call(
|
||||||
help.CmdSetHelp(),
|
help_module.CmdSetHelp(),
|
||||||
"testhelp, General = This is a test",
|
"testhelp, General = This is a test",
|
||||||
"Topic 'testhelp' was successfully created.",
|
"Topic 'testhelp' was successfully created.",
|
||||||
)
|
)
|
||||||
self.call(help.CmdHelp(), "testhelp", "Help for testhelp", cmdset=CharacterCmdSet())
|
self.call(help_module.CmdHelp(), "testhelp", "Help for testhelp", cmdset=CharacterCmdSet())
|
||||||
|
|
||||||
|
def test_parse_entry(self):
|
||||||
|
"""
|
||||||
|
Test for subcategories
|
||||||
|
|
||||||
|
"""
|
||||||
|
entry = """
|
||||||
|
Main topic text
|
||||||
|
|
||||||
|
# help-subtopics
|
||||||
|
|
||||||
|
## foo
|
||||||
|
|
||||||
|
Foo sub-category
|
||||||
|
|
||||||
|
### moo
|
||||||
|
|
||||||
|
Foo/Moo subsub-category
|
||||||
|
|
||||||
|
#### dum
|
||||||
|
|
||||||
|
Foo/Moo/Dum subsubsub-category
|
||||||
|
|
||||||
|
## bar
|
||||||
|
|
||||||
|
Bar subcategory
|
||||||
|
|
||||||
|
### moo
|
||||||
|
|
||||||
|
Bar/Moo subcategory
|
||||||
|
|
||||||
|
"""
|
||||||
|
expected = {
|
||||||
|
None: "Main topic text",
|
||||||
|
"foo": {
|
||||||
|
None: "Foo sub-category",
|
||||||
|
"moo": {
|
||||||
|
None: "Foo/Moo subsub-category",
|
||||||
|
"dum": {
|
||||||
|
None: "Foo/Moo/Dum subsubsub-category",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
None: "Bar subcategory",
|
||||||
|
"moo": {
|
||||||
|
None: "Bar/Moo subcategory"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_result = help_module.CmdHelp.parse_entry_for_subcategories(entry)
|
||||||
|
self.assertEqual(expected, actual_result)
|
||||||
|
|
||||||
|
|
||||||
class TestSystem(CommandTest):
|
class TestSystem(CommandTest):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue