Fix parsing precedence of long cmd-aliases. Resolve #3090

This commit is contained in:
Griatch 2023-02-26 20:15:34 +01:00
parent a2ebc720bf
commit 86f18c6686
3 changed files with 49 additions and 10 deletions

View file

@ -2,6 +2,9 @@
## Main branch (git)
- Bug fix: Make sure command parser gives precedence to longer cmd-aliases. So
if sending `smile at` and the cmd `smile` has alias `smile at`, the match is
ordered so the result is never interpreted as `smile` with an argument `at`.
- Bug fix: || (escaped color tags) were parsed too early in help entries,
leading to colors when wanting a | separator
- Bug fix: Make sure spawned objects get `typeclass_path` pointing to the true

View file

@ -11,7 +11,6 @@ import re
from django.conf import settings
from django.urls import reverse
from django.utils.text import slugify
from evennia.locks.lockhandler import LockHandler
from evennia.utils.ansi import ANSIString
from evennia.utils.evtable import EvTable
@ -54,10 +53,14 @@ def _init_command(cls, **kwargs):
cls.aliases = []
cls.aliases = list(set(alias for alias in cls.aliases if alias and alias != cls.key))
# optimization - a set is much faster to match against than a list
# optimization - a set is much faster to match against than a list. This is useful
# for 'does any match' kind of queries
cls._matchset = set([cls.key] + cls.aliases)
# optimization for looping over keys+aliases
cls._keyaliases = tuple(cls._matchset)
# optimization for looping over keys+aliases. We sort by longest entry first, since we
# want to be able to catch commands with spaces in the alias/key (so if you have key 'smile'
# and alias 'smile at', writing 'smile at' should not risk being interpreted as 'smile'
# with an argument 'at')
cls._keyaliases = tuple(sorted([cls.key] + cls.aliases, key=len, reverse=True))
# by default we don't save the command between runs
if not hasattr(cls, "save_for_next"):
@ -303,10 +306,13 @@ class Command(metaclass=CommandMeta):
matches.extend(x.lower() for x in self.aliases)
self._matchset = set(matches)
# optimization for looping over keys+aliases
self._keyaliases = tuple(self._matchset)
# optimization for looping over keys+aliases. We sort by longest entry first, since we
# want to be able to catch commands with spaces in the alias/key (so if you have key 'smile'
# and alias 'smile at', writing 'smile at' should not risk being interpreted as 'smile'
# with an argument 'at')
self._keyaliases = tuple(sorted(matches, key=len, reverse=True))
self._noprefix_aliases = {x.lstrip(CMD_IGNORE_PREFIXES): x for x in matches}
self._noprefix_aliases = {x.lstrip(CMD_IGNORE_PREFIXES): x for x in self._keyaliases}
def set_key(self, new_key):
"""

View file

@ -4,7 +4,6 @@ Unit testing for the Command system itself.
"""
from django.test import override_settings
from evennia.commands import cmdparser
from evennia.commands.cmdset import CmdSet
from evennia.commands.command import Command
@ -991,9 +990,8 @@ class TestOptionTransferReplace(TestCase):
import sys
from twisted.trial.unittest import TestCase as TwistedTestCase
from evennia.commands import cmdhandler
from twisted.trial.unittest import TestCase as TwistedTestCase
def _mockdelay(time, func, *args, **kwargs):
@ -1232,3 +1230,35 @@ class TestCmdSet(BaseEvenniaTest):
result = test_cmd_set.get("another command")
self.assertIsInstance(result, _CmdTest2)
class _CmdG(Command):
key = "smile"
aliases = ["smile at", "grin", "grin at"]
class _CmdSetG(CmdSet):
def at_cmdset_creation(self):
self.add(_CmdG())
class TestIssue3090(BaseEvenniaTest):
"""
Command aliases should be prioritized longest-match to shortest-match.
https://github.com/evennia/evennia/issues/3090
"""
def test_long_aliases(self):
cmdset_g = _CmdSetG()
# print(cmdset_g.commands[0]._keyaliases)
result = cmdparser.cmdparser("smile at", cmdset_g, None)[0]
self.assertEqual(result[0], "smile at")
self.assertEqual(result[1], "")
self.assertEqual(result[2].__class__, _CmdG)
self.assertEqual(result[3], 8)
self.assertEqual(result[4], 1.0)
self.assertEqual(result[5], "smile at")