summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2017-06-01 01:33:54 +0200
committerMarkus Mittendrein <git@maxmitti.tk>2017-06-01 12:10:53 +0200
commitccd8b87ba062a99e91f58bbe2e7494bcf3301aee (patch)
tree7374048c5153878eb555a5209fe22259a1b9f460
parent8bd7d77ba07d84b7829201befe30c07b12c5568a (diff)
downloadmanager-ccd8b87ba062a99e91f58bbe2e7494bcf3301aee.tar.gz
manager-ccd8b87ba062a99e91f58bbe2e7494bcf3301aee.zip
Correct/suggest command names with the help of damerau levenshtein distance
-rw-r--r--src/crsm.cpp108
-rw-r--r--src/crsm.hpp8
m---------src/qt-config0
3 files changed, 101 insertions, 15 deletions
diff --git a/src/crsm.cpp b/src/crsm.cpp
index aa1ef13..e187123 100644
--- a/src/crsm.cpp
+++ b/src/crsm.cpp
@@ -245,6 +245,21 @@ bool CRSM::watchdog(const QString& id)
return false;
}
+QString CRSM::cmdErrorText(const QString& command, const ClientInfo& client, bool& ret)
+{
+ QString retText;
+ QStringList corrections;
+ if(!(ret = cmd(command, client, corrections)))
+ {
+ retText = "Unbekannter Befehl: \"" + command + "\"!\n";
+ if(!corrections.isEmpty())
+ {
+ retText += "Meintest du: " + corrections.join(", ") + "?\n";
+ }
+ }
+ return retText;
+}
+
bool CRSM::clientMessage(ClientInfo& client, const QString& message, ClonkOutputInterface::MessageType type, const QTime& time)
{
bool isMeMessage = (type == Action);
@@ -281,11 +296,16 @@ bool CRSM::clientMessage(ClientInfo& client, const QString& message, ClonkOutput
QString command = getCommand(message);
if(!command.isEmpty())
{
- if(!cmd(command, client))
+ bool ret;
+ const QString& txt = cmdErrorText(command, client, ret);
+ if(ret)
+ {
+ return true;
+ }
+ else
{
- respond(client, "Unbekannter Befehl: \"" + command + "\"!\n");
+ respond(client, txt);
}
- return true;
}
}
}
@@ -699,7 +719,8 @@ void CRSM::newManagementData()
if(data.at(0) == '/')
{
QString command = data.mid(1).trimmed();
- if(cmd(command, ClientInfo::managementClient(conn)))
+ QStringList corrections;
+ if(cmd(command, ClientInfo::managementClient(conn), corrections))
{
continue;
}
@@ -1431,12 +1452,58 @@ bool CRSM::cmdExists(const QString &name, ClientInterface interface)
return cmds.contains(name) && cmds.value(name).interfaces & interface;
}
-CmdFunctionRef* CRSM::findCommand(const QString &cmd, ClientInterface interface, QString &args)
+QStringList CRSM::guessCmd(const QString& name, ClientInterface interface)
+{
+ int minDistance = -1;
+ QString minDistanceCmd;
+ QStringList minDistanceCmds;
+
+ auto applyDistance = [&name, &minDistance, &minDistanceCmd, &minDistanceCmds](const QString& cmdName)
+ {
+ int distance = Util::damerauLevenshteinDistance(name, cmdName);
+ if(minDistance == -1 || distance < minDistance)
+ {
+ minDistance = distance;
+ minDistanceCmd = cmdName;
+ minDistanceCmds.clear();
+ }
+ else if(minDistance == distance && !minDistanceCmds.contains(cmdName))
+ {
+ minDistanceCmds.append(cmdName);
+ }
+ };
+
+ for(const auto& cmd : cmds)
+ {
+ if(cmd.interfaces & interface)
+ {
+ applyDistance(cmd.name);
+ }
+ }
+
+ for(const auto& alias : Config.CRSM.CommandAlias.keys())
+ {
+ applyDistance(alias);
+ }
+
+ if(!minDistanceCmds.contains(minDistanceCmd))
+ {
+ minDistanceCmds.append(minDistanceCmd);
+ }
+
+ if(minDistanceCmds.length() == 1 && minDistance >= std::min(3, name.length() - 1))
+ {
+ minDistanceCmds.clear();
+ }
+ return minDistanceCmds;
+}
+
+CmdFunctionRef* CRSM::findCommand(const QString &cmd, ClientInterface interface, QString &args, QStringList& corrections)
{
- return findCommand(cmd.split(QRegularExpression(R"(\s)"), QString::KeepEmptyParts), interface, args);
+ return findCommand(cmd.split(QRegularExpression(R"(\s)"), QString::KeepEmptyParts), interface, args, corrections);
}
-CmdFunctionRef* CRSM::findCommand(QStringList&& cmd, ClientInterface interface, QString &realCmd)
+CmdFunctionRef* CRSM::findCommand(QStringList&& cmd, ClientInterface interface, QString &realCmd, QStringList& corrections)
{
if(cmd.length() > 0)
{
@@ -1457,8 +1524,23 @@ CmdFunctionRef* CRSM::findCommand(QStringList&& cmd, ClientInterface interface,
}
else
{
+ const QStringList& guesses = guessCmd(cmdPart, interface);
+ if(guesses.length() == 1)
+ {
+ QString guess = guesses.first();
+ substituteCommandAlias(guess);
+ if(cmdExists(guess, interface))
+ {
+ return &cmds[guess];
+ }
+ }
+ else if(guesses.length() > 1)
+ {
+ corrections.append(guesses);
+ }
+
cmd.removeLast();
- return findCommand(std::move(cmd), interface, realCmd);
+ return findCommand(std::move(cmd), interface, realCmd, corrections);
}
}
}
@@ -1468,12 +1550,12 @@ CmdFunctionRef* CRSM::findCommand(QStringList&& cmd, ClientInterface interface,
}
}
-bool CRSM::cmd(const QString& cmd, const ClientInfo &client)
+bool CRSM::cmd(const QString& cmd, const ClientInfo &client, QStringList& corrections)
{
Log.commandLog(cmd, client, false);
CmdFunctionRef* cmdPtr;
QString realCmd = cmd;
- if((cmdPtr = findCommand(cmd, client.interface, realCmd)) != nullptr)
+ if((cmdPtr = findCommand(cmd, client.interface, realCmd, corrections)) != nullptr)
{
CmdFunctionRef cmdRef = *cmdPtr;
QString args = cmd.mid(realCmd.length()).trimmed();
@@ -2327,9 +2409,11 @@ void CRSM::handleIrcMessage(const ClientInfo &client, QString message, const QSt
if(!command.isEmpty())
{
- if(!cmd(command, client))
+ bool ret;
+ const QString& txt = cmdErrorText(command, client, ret);
+ if(!ret)
{
- respond(client, "Unbekannter Befehl: \"" + command + "\"!", (privateMessage ? RespondType::PrivateNotice : RespondType::Normal), true);
+ respond(client, txt, (privateMessage ? RespondType::PrivateNotice : RespondType::Normal), true);
}
}
diff --git a/src/crsm.hpp b/src/crsm.hpp
index 4e3aa34..c4c8482 100644
--- a/src/crsm.hpp
+++ b/src/crsm.hpp
@@ -239,9 +239,11 @@ private:
void addCommand(const QString& name, CmdFunction func, int interfaces = Clonk | IRC, UserType userType = User, const QString& shortDescription = "", const QString &argList = "", const QString &longDescription = "");
inline void addCommandGroup(const QString& name, int interfaces = Clonk | IRC, UserType userType = User, const QString& shortDescription = "", const QString &longDescription = "", CmdFunction defaultFunc = &CRSM::groupinfo);
bool cmdExists(const QString& name, ClientInterface interface);
- CmdFunctionRef* findCommand(const QString& cmd, ClientInterface interface, QString& args);
- CmdFunctionRef* findCommand(QStringList &&cmd, ClientInterface interface, QString& args);
- bool cmd(const QString &cmd, const ClientInfo& client);
+ QStringList guessCmd(const QString& name, ClientInterface interface);
+ CmdFunctionRef* findCommand(const QString& cmd, ClientInterface interface, QString& args, QStringList& corrections);
+ CmdFunctionRef* findCommand(QStringList &&cmd, ClientInterface interface, QString& args, QStringList& corrections);
+ QString cmdErrorText(const QString& command, const ClientInfo& client, bool& ret);
+ bool cmd(const QString &cmd, const ClientInfo& client, QStringList& corrections);
void rightsFailMessage(const ClientInfo& info, UserType minUserType);
UserType clientUserType(const ClientInfo& client);
diff --git a/src/qt-config b/src/qt-config
-Subproject 0035a128ba00259cf6a5ada68e05c0efaf9f95a
+Subproject 9e411a03acc60aaff7244f26cc32597fd6452df