swift
simplecommandparser.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <algorithm>
7 
8 #include <QList>
9 #include <QSet>
10 #include <QStringBuilder>
11 
12 #include "misc/pq/pqstring.h"
13 #include "misc/stringutils.h"
14 
15 using namespace swift::misc::physical_quantities;
16 
17 namespace swift::misc
18 {
19  QList<CSimpleCommandParser::CommandHtmlHelp> CSimpleCommandParser::s_commands;
20  QSet<QString> CSimpleCommandParser::s_registered;
21 
22  CSimpleCommandParser::CSimpleCommandParser(const QStringList &knownCommands)
23  {
24  this->setCheckedCommandList(knownCommands);
25  }
26 
27  void CSimpleCommandParser::parse(const QString &commandLine)
28  {
29  m_knownCommand = false;
30  m_originalLine = commandLine;
31  m_cleanedLine = commandLine.trimmed().simplified();
32  if (!m_cleanedLine.isEmpty())
33  {
34  m_splitParts = m_cleanedLine.split(' ');
35  if (!m_splitParts.isEmpty())
36  {
37  const QString &first = m_splitParts.constFirst();
38  const QString formatted = formatCommand(first);
39  if (isCommand(first))
40  {
41  m_commandPart = formatted;
42  m_knownCommand = m_knownCommands.contains(formatted);
43  }
44  }
45  }
46  }
47 
48  const QString &CSimpleCommandParser::part(int index) const
49  {
50  static const QString empty("");
51  if (index < 0 || index >= m_splitParts.size()) return empty;
52  return m_splitParts.at(index);
53  }
54 
56  {
57  if (index < 0) { return m_cleanedLine; }
58  const QString p = this->part(index);
59  const int from = index < 1 ? 0 : nthIndexOf(m_cleanedLine, ' ', index, Qt::CaseInsensitive);
60  const int fi = m_cleanedLine.indexOf(p, from, Qt::CaseInsensitive);
61  if (fi < 0) { return {}; }
62  return m_originalLine.mid(fi).trimmed();
63  }
64 
65  int CSimpleCommandParser::countParts() const { return m_splitParts.count(); }
66 
67  bool CSimpleCommandParser::hasPart(int index) const { return index >= 0 && index < this->countParts(); }
68 
70  {
71  int c = countParts();
72  return c > 0 ? c - 1 : 0;
73  }
74 
75  bool CSimpleCommandParser::isInt(int index) const
76  {
77  const QString p = this->part(index);
78  if (p.isEmpty()) { return false; }
79  bool ok = false;
80  p.toInt(&ok); // cppcheck-suppress ignoredReturnValue
81  return ok;
82  }
83 
84  bool CSimpleCommandParser::isDouble(int index) const
85  {
86  const QString p = this->part(index);
87  if (p.isEmpty()) { return false; }
88  bool ok = false;
90  return ok;
91  }
92 
93  int CSimpleCommandParser::toInt(int index, int def) const
94  {
95  const QString p = this->part(index);
96  if (p.isEmpty()) { return def; }
97  bool ok = false;
98  int i = p.toInt(&ok);
99  return ok ? i : def;
100  }
101 
102  bool CSimpleCommandParser::toBool(int index, bool def) const
103  {
104  const QString p = this->part(index);
105  if (p.isEmpty()) { return def; }
106  const bool b = stringToBool(p);
107  return b;
108  }
109 
110  double CSimpleCommandParser::toDouble(int index, double def) const
111  {
112  const QString p = this->part(index);
113  if (p.isEmpty()) { return def; }
114  bool ok = false;
116  return ok ? d : def;
117  }
118 
119  bool CSimpleCommandParser::matchesPart(int index, const QString &toMatch, Qt::CaseSensitivity cs) const
120  {
121  if (toMatch.isEmpty()) { return false; }
122  if (!this->hasPart(index)) { return false; }
123  const QString p(this->part(index));
124  if (p.isEmpty()) { return false; }
125  return (p.length() == toMatch.length() && p.startsWith(toMatch, cs));
126  }
127 
129  {
130  for (const CommandHtmlHelp &help : std::as_const(s_commands))
131  {
132  // avoid duplicates
133  if (help.command == command.command) { return; }
134  }
135  s_commands.append(command);
136  }
137 
138  bool CSimpleCommandParser::registered(const QString &helpContext)
139  {
140  if (s_registered.contains(helpContext)) { return true; }
141  s_registered.insert(helpContext);
142  return false;
143  }
144 
146  {
147  if (s_commands.isEmpty()) { return {}; }
148 
149  static const QString html("<table style=\"font-size: 8pt; white-space: nowrap;\">\n%1\n</table>");
150  static const QString row("<td>%1</td><td>%2</td>");
151  static const QString rowHeader("<th>%1</th><th>%2</th><th>%3</th><th>%4</th>");
152 
153  QString rows;
154  QList<CommandHtmlHelp> cmds(s_commands);
155  std::sort(cmds.begin(), cmds.end(), CommandHtmlHelp::less);
156 
157  static const QString cmdCol(QString().fill('-', 20));
158  static const QString textCol(QString().fill('-', 40));
159  rows += u"<tr>" % rowHeader.arg("command", "synopsis", "command", "synopsis") % u"</tr>\n" % u"<tr>" %
160  rowHeader.arg(cmdCol, textCol, cmdCol, textCol) % u"</tr>\n";
161 
162  for (int i = 0; i < cmds.size(); i++)
163  {
164  CommandHtmlHelp help = cmds[i];
165  rows += u"<tr>" % row.arg(help.command, help.help);
166  i++;
167  if (i < cmds.size())
168  {
169  help = cmds[i];
170  rows += row.arg(help.command, help.help);
171  }
172  else { rows += row.arg("", ""); }
173  rows += "</tr>\n";
174  }
175  return html.arg(rows);
176  }
177 
178  QString CSimpleCommandParser::removeLeadingDot(const QString &candidate)
179  {
180  if (!candidate.startsWith('.')) { return candidate; }
181  return candidate.right(candidate.length() - 1);
182  }
183 
184  QString CSimpleCommandParser::formatCommand(const QString &command)
185  {
186  return removeLeadingDot(command.trimmed().toLower());
187  }
188 
189  bool CSimpleCommandParser::isCommand(const QString &candidate) { return candidate.startsWith('.'); }
190 
191  bool CSimpleCommandParser::matchesCommand(const QString &checkCommand, const QString &alias1, const QString &alias2)
192  {
193  if (m_commandPart.isEmpty()) { return false; }
194  if (!checkCommand.isEmpty() && formatCommand(checkCommand) == m_commandPart) { return true; }
195  if (!alias1.isEmpty() && formatCommand(alias1) == m_commandPart) { return true; }
196  if (!alias2.isEmpty() && formatCommand(alias2) == m_commandPart) { return true; }
197  return false;
198  }
199 
200  bool CSimpleCommandParser::commandStartsWith(const QString &startPattern) const
201  {
202  if (m_commandPart.isEmpty()) { return false; }
203  return m_commandPart.startsWith(formatCommand(startPattern));
204  }
205 
206  bool CSimpleCommandParser::commandEndsWith(const QString &endPattern) const
207  {
208  if (m_commandPart.isEmpty()) { return false; }
209  return m_commandPart.endsWith(endPattern);
210  }
211 
212  void CSimpleCommandParser::setCheckedCommandList(const QStringList &commands)
213  {
214  for (const QString &c : commands) { m_knownCommands.append(formatCommand(c)); }
215  }
216 } // namespace swift::misc
static void registerCommand(const CommandHtmlHelp &command)
Register a command.
static QString commandsHtmlHelp()
HTML commands HELP.
int countPartsWithoutCommand() const
Count parts, command excluded.
int toInt(int index, int def=-1) const
Part as integer.
bool isInt(int index) const
Is part an integer?
bool toBool(int index, bool def=false) const
Part as bool.
bool commandStartsWith(const QString &startPattern) const
Command starting with pattern?
CSimpleCommandParser(const QStringList &knownCommands)
Constructor.
bool matchesPart(int index, const QString &toMatch, Qt::CaseSensitivity cs=Qt::CaseInsensitive) const
Matches given part.
bool isDouble(int index) const
Is part a double?
void parse(const QString &commandLine)
Parse.
QString partAndRemainingStringAfter(int index) const
Part and remaing string after.
double toDouble(int index, double def=-1.0) const
Part as double.
bool matchesCommand(const QString &checkCommand, const QString &alias1="", const QString &alias2="")
Is given command current command of command line.
const QString & part(int index) const
Get part, 0 is command itself.
bool hasPart(int index) const
Existing part.
static bool registered(const QString &helpContext)
Help already registered.
bool commandEndsWith(const QString &endPattern) const
Command ending with pattern?
static double parseNumber(const QString &number, bool &success, SeparatorMode mode=SeparatorBestGuess)
Locale aware parsing.
Definition: pqstring.cpp:106
Free functions in swift::misc.
SWIFT_MISC_EXPORT int nthIndexOf(const QString &string, QChar ch, int nth=1, Qt::CaseSensitivity cs=Qt::CaseInsensitive)
nth index of ch
SWIFT_MISC_EXPORT bool stringToBool(const QString &boolString)
Convert string to bool.