Fix bugs, correct one-line format_grid function

This commit is contained in:
Griatch 2021-05-12 09:32:08 +02:00
parent 770fac275d
commit 055bbcfee3
7 changed files with 162 additions and 113 deletions

View file

@ -51,14 +51,13 @@ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
class CmdChannel(COMMAND_DEFAULT_CLASS):
"""
Talk on and manage in-game channels.
Use and manage in-game channels.
Usage:
channel
channel channelname <msg>
channel channel name [= <msg>]
channel/list
channel/all
channel channel name = <msg>
channel (show all subscription)
channel/all (show available channels)
channel/alias channelname = alias[;alias...]
channel/unalias alias
channel/who channelname
@ -73,10 +72,10 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
channel/desc channelname = description
channel/lock channelname = lockstring
channel/unlock channelname = lockstring
channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]
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/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]
# subtopics
@ -85,8 +84,8 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
Usage: channel channelname msg
channel channel name = msg (with space in channel name)
This sends a message to the channel. Note that you will rarely use this command
like this; instead you can use the alias
This sends a message to the channel. Note that you will rarely use this
command like this; instead you can use the alias
channelname <msg>
channelalias <msg>
@ -117,8 +116,8 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
wguild Hello
warchannel Hello
Note that this will not work if the alias has a space in it. So the 'warrior guild'
alias must be used with the `channel` command:
Note that this will not work if the alias has a space in it. So the
'warrior guild' alias must be used with the `channel` command:
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
index number, you will step that many lines back before viewing those 20 lines.
For example:
channel/history public = 35
@ -184,12 +184,37 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
control - who controls the channel. This is usually the one creating
the channel.
Common lockfuncs are all() and perm(). To make a channel everyone can listen
to but only builders can talk on, use this:
Common lockfuncs are all() and perm(). To make a channel everyone can
listen to but only builders can talk on, use this:
listen:all()
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"
aliases = ["chan", "channels"]
@ -505,7 +530,7 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
new_chan = create.create_channel(
name, aliases=aliases, desc=description, locks=lockstring, typeclass=typeclass)
new_chan.connect(caller)
self.sub_to_channel(new_chan)
return new_chan, ""
def destroy_channel(self, channel, message=None):
@ -831,12 +856,12 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
table = self.display_all_channels(subscribed, available)
self.msg(
"\n|wAvailable channels|n (use /list to "
f"only show subscriptions)\n{table}")
"\n|wAvailable channels|n (use no argument to "
f"only show your subscriptions)\n{table}")
return
if not channel_names:
# (empty or /list) show only subscribed channels
# empty arg show only subscribed channels
subscribed, _ = self.list_channels()
table = self.display_subbed_channels(subscribed)

View file

@ -31,8 +31,6 @@ DEFAULT_HELP_CATEGORY = settings.DEFAULT_HELP_CATEGORY
# limit symbol import for API
__all__ = ("CmdHelp", "CmdSetHelp")
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
_SEP = "|C" + "-" * _DEFAULT_WIDTH + "|n"
@dataclass
@ -59,7 +57,7 @@ class HelpCategory:
class CmdHelp(COMMAND_DEFAULT_CLASS):
"""
View help or a list of topics
Get help.
Usage:
help
@ -67,8 +65,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
help <topic>/<subtopic>
help <topic>/<subtopic>/<subsubtopic> ...
Use the help command alone to see an index of all help topics, organized by
category. Some long topics may offer additional sub-topics.
Use the 'help' command alone to see an index of all help topics, organized
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"}))
@staticmethod
def format_help_entry(topic="", help_text="", aliases=None, suggested=None,
def format_help_entry(self, topic="", help_text="", aliases=None, suggested=None,
subtopics=None):
"""
This visually formats the help entry.
@ -142,7 +139,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
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"
@ -156,26 +154,27 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
help_text = "\n\n" + dedent(help_text.strip('\n')) + "\n" if help_text else ""
if subtopics:
subtopics = [f"|w{topic}/{subtop}|n" for subtop in subtopics]
subtopics = (
"\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:
subtopics = ''
if suggested:
suggested = [f"|w{sug}|n" for sug in suggested]
suggested = (
"\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:
suggested = ''
end = f"\n{_SEP}"
end = f"\n{separator}"
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):
"""
Output a category-ordered g for displaying the main help, grouped by

View file

@ -439,8 +439,8 @@ class TestHelp(CommandTest):
"Help for test\n\n"
"Main help text\n\n"
"Subtopics:\n"
" test/creating extra stuff\n"
" test/something else\n"
" test/creating extra stuff"
" test/something else"
" test/more"
),
("test/creating extra stuff", # subtopic, full match
@ -483,14 +483,14 @@ class TestHelp(CommandTest):
"Help for test/more/second-more\n\n"
"The Second More text.\n\n"
"Subtopics:\n"
" test/more/second-more/more again\n"
" test/more/second-more/more again"
" test/more/second-more/third more"
),
("test/More/-more", # partial match
"Help for test/more/second-more\n\n"
"The Second More text.\n\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 again",

View file

@ -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.
* Talking NPC (Griatch 2011) - A talking NPC object that offers a
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
branching EvMenu with selection options sourced from a single
multi-line string.

View file

@ -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)
structure = {None: topic.strip()}
structure = {None: topic.strip('\n')}
if subtopics:
subtopics = subtopics[0]

View file

@ -321,7 +321,7 @@ class TestFormatGrid(TestCase):
"""Grid with small variations"""
elements = self._generate_elements(3, 1, 30)
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))
def test_disparate_grid(self):
@ -356,7 +356,7 @@ class TestFormatGrid(TestCase):
"lock",
)
rows = utils.format_grid(elements, width=78)
self.assertEqual(len(rows), 2)
self.assertEqual(len(rows), 3)
for element in elements:
self.assertTrue(element in "\n".join(rows), f"element {element} is missing.")

View file

@ -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
as horizontal bars.
"""
if not elements:
return []
if not verbatim_elements:
verbatim_elements = []
def _minimal_rows(elements):
"""
Minimalistic distribution with minimal spacing, good for single-line
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_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
row += " " * max(0, width - len(row))
rows.append(row)
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():
"""