Fix bugs, correct one-line format_grid function
This commit is contained in:
parent
770fac275d
commit
055bbcfee3
7 changed files with 162 additions and 113 deletions
|
|
@ -51,14 +51,13 @@ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||||
|
|
||||||
class CmdChannel(COMMAND_DEFAULT_CLASS):
|
class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
Talk on and manage in-game channels.
|
Use and manage in-game channels.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
channel
|
|
||||||
channel channelname <msg>
|
channel channelname <msg>
|
||||||
channel channel name [= <msg>]
|
channel channel name = <msg>
|
||||||
channel/list
|
channel (show all subscription)
|
||||||
channel/all
|
channel/all (show available channels)
|
||||||
channel/alias channelname = alias[;alias...]
|
channel/alias channelname = alias[;alias...]
|
||||||
channel/unalias alias
|
channel/unalias alias
|
||||||
channel/who channelname
|
channel/who channelname
|
||||||
|
|
@ -73,10 +72,10 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
channel/desc channelname = description
|
channel/desc channelname = description
|
||||||
channel/lock channelname = lockstring
|
channel/lock channelname = lockstring
|
||||||
channel/unlock channelname = lockstring
|
channel/unlock channelname = lockstring
|
||||||
channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]
|
|
||||||
channel/ban channelname (list bans)
|
channel/ban channelname (list bans)
|
||||||
channe/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]
|
channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: logmessage]
|
||||||
channel/unban[/quiet] channelname[, channelname, ...] = subscribername
|
channel/unban[/quiet] channelname[, channelname, ...] = subscribername
|
||||||
|
channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]
|
||||||
|
|
||||||
# subtopics
|
# subtopics
|
||||||
|
|
||||||
|
|
@ -85,8 +84,8 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
Usage: channel channelname msg
|
Usage: channel channelname msg
|
||||||
channel channel name = msg (with space in channel name)
|
channel channel name = msg (with space in channel name)
|
||||||
|
|
||||||
This sends a message to the channel. Note that you will rarely use this command
|
This sends a message to the channel. Note that you will rarely use this
|
||||||
like this; instead you can use the alias
|
command like this; instead you can use the alias
|
||||||
|
|
||||||
channelname <msg>
|
channelname <msg>
|
||||||
channelalias <msg>
|
channelalias <msg>
|
||||||
|
|
@ -117,8 +116,8 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
wguild Hello
|
wguild Hello
|
||||||
warchannel Hello
|
warchannel Hello
|
||||||
|
|
||||||
Note that this will not work if the alias has a space in it. So the 'warrior guild'
|
Note that this will not work if the alias has a space in it. So the
|
||||||
alias must be used with the `channel` command:
|
'warrior guild' alias must be used with the `channel` command:
|
||||||
|
|
||||||
channel warrior guild = Hello
|
channel warrior guild = Hello
|
||||||
|
|
||||||
|
|
@ -138,6 +137,7 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
This will display the last |c20|n lines of channel history. By supplying an
|
This will display the last |c20|n lines of channel history. By supplying an
|
||||||
index number, you will step that many lines back before viewing those 20 lines.
|
index number, you will step that many lines back before viewing those 20 lines.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
channel/history public = 35
|
channel/history public = 35
|
||||||
|
|
@ -184,12 +184,37 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
control - who controls the channel. This is usually the one creating
|
control - who controls the channel. This is usually the one creating
|
||||||
the channel.
|
the channel.
|
||||||
|
|
||||||
Common lockfuncs are all() and perm(). To make a channel everyone can listen
|
Common lockfuncs are all() and perm(). To make a channel everyone can
|
||||||
to but only builders can talk on, use this:
|
listen to but only builders can talk on, use this:
|
||||||
|
|
||||||
listen:all()
|
listen:all()
|
||||||
send: perm(Builders)
|
send: perm(Builders)
|
||||||
|
|
||||||
|
## boot and ban
|
||||||
|
|
||||||
|
Usage: channel/ban channelname (list bans)
|
||||||
|
channel/ban channelname[, channelname, ...] = subscribername [: logmessage]
|
||||||
|
channel/unban channelname[, channelname, ...] = subscribername
|
||||||
|
channel/unban channelname
|
||||||
|
channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]
|
||||||
|
|
||||||
|
Booting will kick a named subscriber from a channel temporarily. It will
|
||||||
|
also remove all their aliases. They are still able to re-connect unless
|
||||||
|
they are also banned. The 'reason' given will be echoed to the user and
|
||||||
|
channel.
|
||||||
|
|
||||||
|
Banning will block a given subscriber's ability to connect to a channel. It
|
||||||
|
will not automatically boot them. The 'logmessage' will be stored on the
|
||||||
|
channel and shown when you list your bans (so you can remember why they
|
||||||
|
were banned).
|
||||||
|
|
||||||
|
So to permanently get rid of a user, the way to do so is to first ban them
|
||||||
|
and then boot them.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
ban mychannel1,mychannel2 = EvilUser : Was banned for spamming.
|
||||||
|
boot mychannel1,mychannel2 = EvilUser : No more spamming!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "channel"
|
key = "channel"
|
||||||
aliases = ["chan", "channels"]
|
aliases = ["chan", "channels"]
|
||||||
|
|
@ -505,7 +530,7 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
new_chan = create.create_channel(
|
new_chan = create.create_channel(
|
||||||
name, aliases=aliases, desc=description, locks=lockstring, typeclass=typeclass)
|
name, aliases=aliases, desc=description, locks=lockstring, typeclass=typeclass)
|
||||||
new_chan.connect(caller)
|
self.sub_to_channel(new_chan)
|
||||||
return new_chan, ""
|
return new_chan, ""
|
||||||
|
|
||||||
def destroy_channel(self, channel, message=None):
|
def destroy_channel(self, channel, message=None):
|
||||||
|
|
@ -831,12 +856,12 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
table = self.display_all_channels(subscribed, available)
|
table = self.display_all_channels(subscribed, available)
|
||||||
|
|
||||||
self.msg(
|
self.msg(
|
||||||
"\n|wAvailable channels|n (use /list to "
|
"\n|wAvailable channels|n (use no argument to "
|
||||||
f"only show subscriptions)\n{table}")
|
f"only show your subscriptions)\n{table}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not channel_names:
|
if not channel_names:
|
||||||
# (empty or /list) show only subscribed channels
|
# empty arg show only subscribed channels
|
||||||
subscribed, _ = self.list_channels()
|
subscribed, _ = self.list_channels()
|
||||||
table = self.display_subbed_channels(subscribed)
|
table = self.display_subbed_channels(subscribed)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ DEFAULT_HELP_CATEGORY = settings.DEFAULT_HELP_CATEGORY
|
||||||
|
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("CmdHelp", "CmdSetHelp")
|
__all__ = ("CmdHelp", "CmdSetHelp")
|
||||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
|
||||||
_SEP = "|C" + "-" * _DEFAULT_WIDTH + "|n"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -59,7 +57,7 @@ class HelpCategory:
|
||||||
|
|
||||||
class CmdHelp(COMMAND_DEFAULT_CLASS):
|
class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
View help or a list of topics
|
Get help.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
help
|
help
|
||||||
|
|
@ -67,8 +65,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
help <topic>/<subtopic>
|
help <topic>/<subtopic>
|
||||||
help <topic>/<subtopic>/<subsubtopic> ...
|
help <topic>/<subtopic>/<subsubtopic> ...
|
||||||
|
|
||||||
Use the help command alone to see an index of all help topics, organized by
|
Use the 'help' command alone to see an index of all help topics, organized
|
||||||
category. Some long topics may offer additional sub-topics.
|
by category.eSome big topics may offer additional sub-topics.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -123,8 +121,7 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
self.msg(text=(text, {"type": "help"}))
|
self.msg(text=(text, {"type": "help"}))
|
||||||
|
|
||||||
@staticmethod
|
def format_help_entry(self, topic="", help_text="", aliases=None, suggested=None,
|
||||||
def format_help_entry(topic="", help_text="", aliases=None, suggested=None,
|
|
||||||
subtopics=None):
|
subtopics=None):
|
||||||
"""
|
"""
|
||||||
This visually formats the help entry.
|
This visually formats the help entry.
|
||||||
|
|
@ -142,7 +139,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
Returns the formatted string, ready to be sent.
|
Returns the formatted string, ready to be sent.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
start = f"{_SEP}\n"
|
separator = "|C" + "-" * self.client_width() + "|n"
|
||||||
|
start = f"{separator}\n"
|
||||||
|
|
||||||
title = f"|CHelp for |w{topic}|n" if topic else "|rNo help found|n"
|
title = f"|CHelp for |w{topic}|n" if topic else "|rNo help found|n"
|
||||||
|
|
||||||
|
|
@ -156,26 +154,27 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
help_text = "\n\n" + dedent(help_text.strip('\n')) + "\n" if help_text else ""
|
help_text = "\n\n" + dedent(help_text.strip('\n')) + "\n" if help_text else ""
|
||||||
|
|
||||||
if subtopics:
|
if subtopics:
|
||||||
|
subtopics = [f"|w{topic}/{subtop}|n" for subtop in subtopics]
|
||||||
subtopics = (
|
subtopics = (
|
||||||
"\n|CSubtopics:|n\n {}".format(
|
"\n|CSubtopics:|n\n {}".format(
|
||||||
"\n ".join(f"|w{topic}/{subtop}|n" for subtop in subtopics))
|
"\n ".join(format_grid(subtopics, width=self.client_width())))
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
subtopics = ''
|
subtopics = ''
|
||||||
|
|
||||||
if suggested:
|
if suggested:
|
||||||
|
suggested = [f"|w{sug}|n" for sug in suggested]
|
||||||
suggested = (
|
suggested = (
|
||||||
"\n|CSuggestions:|n\n{}".format(
|
"\n|CSuggestions:|n\n{}".format(
|
||||||
fill("|C,|n ".join(f"|w{sug}|n" for sug in suggested), indent=2))
|
"\n ".join(format_grid(suggested, width=self.client_width())))
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
suggested = ''
|
suggested = ''
|
||||||
|
|
||||||
end = f"\n{_SEP}"
|
end = f"\n{separator}"
|
||||||
|
|
||||||
return "".join((start, title, aliases, help_text, subtopics, suggested, end))
|
return "".join((start, title, aliases, help_text, subtopics, suggested, end))
|
||||||
|
|
||||||
|
|
||||||
def format_help_index(self, cmd_help_dict=None, db_help_dict=None, title_lone_category=False):
|
def format_help_index(self, cmd_help_dict=None, db_help_dict=None, title_lone_category=False):
|
||||||
"""
|
"""
|
||||||
Output a category-ordered g for displaying the main help, grouped by
|
Output a category-ordered g for displaying the main help, grouped by
|
||||||
|
|
|
||||||
|
|
@ -439,8 +439,8 @@ class TestHelp(CommandTest):
|
||||||
"Help for test\n\n"
|
"Help for test\n\n"
|
||||||
"Main help text\n\n"
|
"Main help text\n\n"
|
||||||
"Subtopics:\n"
|
"Subtopics:\n"
|
||||||
" test/creating extra stuff\n"
|
" test/creating extra stuff"
|
||||||
" test/something else\n"
|
" test/something else"
|
||||||
" test/more"
|
" test/more"
|
||||||
),
|
),
|
||||||
("test/creating extra stuff", # subtopic, full match
|
("test/creating extra stuff", # subtopic, full match
|
||||||
|
|
@ -483,14 +483,14 @@ class TestHelp(CommandTest):
|
||||||
"Help for test/more/second-more\n\n"
|
"Help for test/more/second-more\n\n"
|
||||||
"The Second More text.\n\n"
|
"The Second More text.\n\n"
|
||||||
"Subtopics:\n"
|
"Subtopics:\n"
|
||||||
" test/more/second-more/more again\n"
|
" test/more/second-more/more again"
|
||||||
" test/more/second-more/third more"
|
" test/more/second-more/third more"
|
||||||
),
|
),
|
||||||
("test/More/-more", # partial match
|
("test/More/-more", # partial match
|
||||||
"Help for test/more/second-more\n\n"
|
"Help for test/more/second-more\n\n"
|
||||||
"The Second More text.\n\n"
|
"The Second More text.\n\n"
|
||||||
"Subtopics:\n"
|
"Subtopics:\n"
|
||||||
" test/more/second-more/more again\n"
|
" test/more/second-more/more again"
|
||||||
" test/more/second-more/third more"
|
" test/more/second-more/third more"
|
||||||
),
|
),
|
||||||
("test/more/second/more again",
|
("test/more/second/more again",
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ things you want from here into your game folder and change them there.
|
||||||
time to pass depending on if you are walking/running etc.
|
time to pass depending on if you are walking/running etc.
|
||||||
* Talking NPC (Griatch 2011) - A talking NPC object that offers a
|
* Talking NPC (Griatch 2011) - A talking NPC object that offers a
|
||||||
menu-driven conversation tree.
|
menu-driven conversation tree.
|
||||||
|
* Traits (Whitenoise, Griatch 2021) - Properties for handling and tracking
|
||||||
|
changing RPG skill and stat values.
|
||||||
* Tree Select (FlutterSprite 2017) - A simple system for creating a
|
* Tree Select (FlutterSprite 2017) - A simple system for creating a
|
||||||
branching EvMenu with selection options sourced from a single
|
branching EvMenu with selection options sourced from a single
|
||||||
multi-line string.
|
multi-line string.
|
||||||
|
|
|
||||||
|
|
@ -173,16 +173,9 @@ def parse_entry_for_subcategories(entry):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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, *subtopics = _RE_HELP_SUBTOPICS_START.split(entry, maxsplit=1)
|
topic, *subtopics = _RE_HELP_SUBTOPICS_START.split(entry, maxsplit=1)
|
||||||
structure = {None: topic.strip()}
|
structure = {None: topic.strip('\n')}
|
||||||
|
|
||||||
if subtopics:
|
if subtopics:
|
||||||
subtopics = subtopics[0]
|
subtopics = subtopics[0]
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ class TestFormatGrid(TestCase):
|
||||||
"""Grid with small variations"""
|
"""Grid with small variations"""
|
||||||
elements = self._generate_elements(3, 1, 30)
|
elements = self._generate_elements(3, 1, 30)
|
||||||
rows = utils.format_grid(elements, width=78)
|
rows = utils.format_grid(elements, width=78)
|
||||||
self.assertEqual(len(rows), 3)
|
self.assertEqual(len(rows), 4)
|
||||||
self.assertTrue(all(len(row) == 78 for row in rows))
|
self.assertTrue(all(len(row) == 78 for row in rows))
|
||||||
|
|
||||||
def test_disparate_grid(self):
|
def test_disparate_grid(self):
|
||||||
|
|
@ -356,7 +356,7 @@ class TestFormatGrid(TestCase):
|
||||||
"lock",
|
"lock",
|
||||||
)
|
)
|
||||||
rows = utils.format_grid(elements, width=78)
|
rows = utils.format_grid(elements, width=78)
|
||||||
self.assertEqual(len(rows), 2)
|
self.assertEqual(len(rows), 3)
|
||||||
for element in elements:
|
for element in elements:
|
||||||
self.assertTrue(element in "\n".join(rows), f"element {element} is missing.")
|
self.assertTrue(element in "\n".join(rows), f"element {element} is missing.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1869,14 +1869,27 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None):
|
||||||
like this to make it easier to insert decorations between rows, such
|
like this to make it easier to insert decorations between rows, such
|
||||||
as horizontal bars.
|
as horizontal bars.
|
||||||
"""
|
"""
|
||||||
if not elements:
|
def _minimal_rows(elements):
|
||||||
return []
|
"""
|
||||||
if not verbatim_elements:
|
Minimalistic distribution with minimal spacing, good for single-line
|
||||||
verbatim_elements = []
|
grids but will look messy over many lines.
|
||||||
|
"""
|
||||||
|
rows = [""]
|
||||||
|
for element in elements:
|
||||||
|
rowlen = len(rows[-1])
|
||||||
|
elen = len(element)
|
||||||
|
if rowlen + elen <= width:
|
||||||
|
rows[-1] += element
|
||||||
|
else:
|
||||||
|
rows.append(element)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def _weighted_rows(elements):
|
||||||
|
"""
|
||||||
|
Dynamic-space, good for making even columns in a multi-line grid but
|
||||||
|
will look strange for a single line.
|
||||||
|
"""
|
||||||
|
|
||||||
nelements = len(elements)
|
|
||||||
# add sep to all but the very last element
|
|
||||||
elements = [elements[ie] + sep for ie in range(nelements - 1)] + [elements[-1]]
|
|
||||||
wls = [len(elem) for elem in elements]
|
wls = [len(elem) for elem in elements]
|
||||||
wls_percentile = [wl for iw, wl in enumerate(wls) if iw not in verbatim_elements]
|
wls_percentile = [wl for iw, wl in enumerate(wls) if iw not in verbatim_elements]
|
||||||
|
|
||||||
|
|
@ -1945,9 +1958,26 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None):
|
||||||
# last element, make sure to store
|
# last element, make sure to store
|
||||||
row += " " * max(0, width - len(row))
|
row += " " * max(0, width - len(row))
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
if not elements:
|
||||||
|
return []
|
||||||
|
if not verbatim_elements:
|
||||||
|
verbatim_elements = []
|
||||||
|
|
||||||
|
nelements = len(elements)
|
||||||
|
# add sep to all but the very last element
|
||||||
|
elements = [elements[ie] + sep for ie in range(nelements - 1)] + [elements[-1]]
|
||||||
|
|
||||||
|
if sum(len(element) for element in elements) <= width:
|
||||||
|
# grid fits in one line
|
||||||
|
return _minimal_rows(elements)
|
||||||
|
else:
|
||||||
|
# full multi-line grid
|
||||||
|
return _weighted_rows(elements)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_evennia_pids():
|
def get_evennia_pids():
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue