/*
SPDX-FileCopyrightText: 2003-2005 Anders Lund
SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann
SPDX-FileCopyrightText: 2001 Charles Samuels
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "katecmds.h"
#include "kateautoindent.h"
#include "katecmd.h"
#include "katedocument.h"
#include "katepartdebug.h"
#include "katerenderer.h"
#include "katesyntaxmanager.h"
#include "kateview.h"
#include
#include
#include
#include
// BEGIN CoreCommands
KateCommands::CoreCommands *KateCommands::CoreCommands::m_instance = nullptr;
// this returns whether the string s could be converted to
// a bool value, one of on|off|1|0|true|false. the argument val is
// set to the extracted value in case of success
static bool getBoolArg(const QString &t, bool *val)
{
bool res(false);
QString s = t.toLower();
res = (s == QLatin1String("on") || s == QLatin1String("1") || s == QLatin1String("true"));
if (res) {
*val = true;
return true;
}
res = (s == QLatin1String("off") || s == QLatin1String("0") || s == QLatin1String("false"));
if (res) {
*val = false;
return true;
}
return false;
}
bool KateCommands::CoreCommands::help(KTextEditor::View *, const QString &cmd, QString &msg)
{
QString realcmd = cmd.trimmed();
if (realcmd == QLatin1String("indent")) {
msg = i18n(
"indent
"
"Indents the selected lines or the current line
");
return true;
} else if (realcmd == QLatin1String("unindent")) {
msg = i18n(
"unindent
"
"Unindents the selected lines or current line.
");
return true;
} else if (realcmd == QLatin1String("cleanindent")) {
msg = i18n(
"cleanindent
"
"Cleans up the indentation of the selected lines or current line according to the indentation settings in the document.
");
return true;
} else if (realcmd == QLatin1String("comment")) {
msg = i18n(
"comment
"
"Inserts comment markers to make the selection or selected lines or current line a comment according to the text format as defined by the "
"syntax highlight definition for the document.
");
return true;
} else if (realcmd == QLatin1String("uncomment")) {
msg = i18n(
"uncomment
"
"Removes comment markers from the selection or selected lines or current line according to the text format as defined by the syntax highlight "
"definition for the document.
");
return true;
} else if (realcmd == QLatin1String("goto")) {
msg = i18n(
"goto line number
"
"This command navigates to the specified line number.
");
return true;
} else if (realcmd == QLatin1String("set-indent-pasted-text")) {
msg = i18n(
"set-indent-pasted-text enable
"
"If enabled, indentation of text pasted from the clipboard is adjusted using the current indenter.
"
"Possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("kill-line")) {
msg = i18n("Deletes the current line.");
return true;
} else if (realcmd == QLatin1String("set-tab-width")) {
msg = i18n(
"set-tab-width width
"
"Sets the tab width to the number width
");
return true;
} else if (realcmd == QLatin1String("set-replace-tab")) {
msg = i18n(
"set-replace-tab enable
"
"If enabled, tabs are replaced with spaces as you type.
"
"Possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-show-tabs")) {
msg = i18n(
"set-show-tabs enable
"
"If enabled, TAB characters and trailing whitespace will be visualized by a small dot.
"
"Possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-remove-trailing-spaces")) {
msg = i18n(
"set-remove-trailing-spaces mode
"
"Removes the trailing spaces in the document depending on the mode.
"
"Possible values:"
"
"
"- none: never remove trailing spaces.
"
"- modified: remove trailing spaces only of modified lines.
"
"- all: remove trailing spaces in the entire document.
"
"
");
return true;
} else if (realcmd == QLatin1String("set-indent-width")) {
msg = i18n(
"set-indent-width width
"
"Sets the indentation width to the number width. Used only if you are indenting with spaces.
");
return true;
} else if (realcmd == QLatin1String("set-indent-mode")) {
msg = i18n(
"set-indent-mode mode
"
"The mode parameter is a value as seen in the Tools - Indentation menu
");
return true;
} else if (realcmd == QLatin1String("set-auto-indent")) {
msg = i18n(
"set-auto-indent enable
"
"Enable or disable autoindentation.
"
"possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-line-numbers")) {
msg = i18n(
"set-line-numbers enable
"
"Sets the visibility of the line numbers pane.
"
" possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-folding-markers")) {
msg = i18n(
"set-folding-markers enable
"
"Sets the visibility of the folding markers pane.
"
" possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-icon-border")) {
msg = i18n(
"set-icon-border enable
"
"Sets the visibility of the icon border.
"
" possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-word-wrap")) {
msg = i18n(
"set-word-wrap enable
"
"Enables dynamic word wrap according to enable
"
" possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-word-wrap-column")) {
msg = i18n(
"set-word-wrap-column width
"
"Sets the line width for hard wrapping to width. This is used if you are having your text wrapped automatically.
");
return true;
} else if (realcmd == QLatin1String("set-replace-tabs-save")) {
msg = i18n(
"set-replace-tabs-save enable
"
"When enabled, tabs will be replaced with whitespace whenever the document is saved.
"
" possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("set-highlight")) {
msg = i18n(
"set-highlight highlight
"
"Sets the syntax highlighting system for the document. The argument must be a valid highlight name, as seen in the Tools → Highlighting menu. "
"This command provides an autocompletion list for its argument.
");
return true;
} else if (realcmd == QLatin1String("set-mode")) {
msg = i18n(
"set-mode mode
"
"Sets the mode as seen in Tools - Mode
");
return true;
} else if (realcmd == QLatin1String("set-show-indent")) {
msg = i18n(
"set-show-indent enable
"
"If enabled, indentation will be visualized by a vertical dotted line.
"
" possible true values: 1 on true
"
"possible false values: 0 off false
");
return true;
} else if (realcmd == QLatin1String("print")) {
msg = i18n("Open the Print dialog to print the current document.
");
return true;
} else {
return false;
}
}
bool KateCommands::CoreCommands::exec(KTextEditor::View *view, const QString &_cmd, QString &errorMsg, const KTextEditor::Range &range)
{
#define KCC_ERR(s) \
{ \
errorMsg = s; \
return false; \
}
// cast it hardcore, we know that it is really a kateview :)
KTextEditor::ViewPrivate *v = static_cast(view);
if (!v) {
KCC_ERR(i18n("Could not access view"));
}
// create a list of args
QStringList args(_cmd.split(QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts));
QString cmd(args.takeFirst());
// ALL commands that takes no arguments.
if (cmd == QLatin1String("indent")) {
if (range.isValid()) {
v->doc()->editStart();
for (int line = range.start().line(); line <= range.end().line(); line++) {
v->doc()->indent(KTextEditor::Range(line, 0, line, 0), 1);
}
v->doc()->editEnd();
} else {
v->indent();
}
return true;
} else if (cmd == QLatin1String("unindent")) {
if (range.isValid()) {
v->doc()->editStart();
for (int line = range.start().line(); line <= range.end().line(); line++) {
v->doc()->indent(KTextEditor::Range(line, 0, line, 0), -1);
}
v->doc()->editEnd();
} else {
v->unIndent();
}
return true;
} else if (cmd == QLatin1String("cleanindent")) {
if (range.isValid()) {
v->doc()->editStart();
for (int line = range.start().line(); line <= range.end().line(); line++) {
v->doc()->indent(KTextEditor::Range(line, 0, line, 0), 0);
}
v->doc()->editEnd();
} else {
v->cleanIndent();
}
return true;
} else if (cmd == QLatin1String("fold")) {
return (v->textFolding().newFoldingRange(range.isValid() ? range : v->selectionRange(), Kate::TextFolding::Persistent | Kate::TextFolding::Folded)
!= -1);
} else if (cmd == QLatin1String("tfold")) {
return (v->textFolding().newFoldingRange(range.isValid() ? range : v->selectionRange(), Kate::TextFolding::Folded) != -1);
} else if (cmd == QLatin1String("unfold")) {
QList> startingRanges = v->textFolding().foldingRangesStartingOnLine(v->cursorPosition().line());
bool unfolded = false;
for (int i = 0; i < startingRanges.size(); ++i) {
if (startingRanges[i].second & Kate::TextFolding::Folded) {
unfolded = v->textFolding().unfoldRange(startingRanges[i].first) || unfolded;
}
}
return unfolded;
} else if (cmd == QLatin1String("comment")) {
if (range.isValid()) {
v->doc()->editStart();
for (int line = range.start().line(); line <= range.end().line(); line++) {
v->doc()->comment(v, line, 0, KTextEditor::DocumentPrivate::Comment);
}
v->doc()->editEnd();
} else {
v->comment();
}
return true;
} else if (cmd == QLatin1String("uncomment")) {
if (range.isValid()) {
v->doc()->editStart();
for (int line = range.start().line(); line <= range.end().line(); line++) {
v->doc()->comment(v, line, 0, KTextEditor::DocumentPrivate::UnComment);
}
v->doc()->editEnd();
} else {
v->uncomment();
}
return true;
} else if (cmd == QLatin1String("kill-line")) {
if (range.isValid()) {
v->doc()->editStart();
for (int line = range.start().line(); line <= range.end().line(); line++) {
v->doc()->removeLine(range.start().line());
}
v->doc()->editEnd();
} else {
v->killLine();
}
return true;
} else if (cmd == QLatin1String("print")) {
v->print();
return true;
}
// ALL commands that take a string argument
else if (cmd == QLatin1String("set-indent-mode") || cmd == QLatin1String("set-highlight") || cmd == QLatin1String("set-mode")) {
// need at least one item, otherwise args.first() crashes
if (args.isEmpty()) {
KCC_ERR(i18n("Missing argument. Usage: %1 ", cmd));
}
if (cmd == QLatin1String("set-indent-mode")) {
v->doc()->config()->setIndentationMode(args.join(QLatin1Char(' ')));
v->doc()->rememberUserDidSetIndentationMode();
return true;
} else if (cmd == QLatin1String("set-highlight")) {
if (v->doc()->setHighlightingMode(args.join(QLatin1Char(' ')))) {
static_cast(v->doc())->setDontChangeHlOnSave();
return true;
}
KCC_ERR(i18n("No such highlighting '%1'", args.first()));
} else if (cmd == QLatin1String("set-mode")) {
if (v->doc()->setMode(args.first())) {
return true;
}
KCC_ERR(i18n("No such mode '%1'", args.first()));
}
}
// ALL commands that takes exactly one integer argument.
else if (cmd == QLatin1String("set-tab-width") || cmd == QLatin1String("set-indent-width") || cmd == QLatin1String("set-word-wrap-column")
|| cmd == QLatin1String("goto")) {
// find a integer value > 0
if (args.isEmpty()) {
KCC_ERR(i18n("Missing argument. Usage: %1 ", cmd));
}
bool ok;
int val(args.first().toInt(&ok, 10)); // use base 10 even if the string starts with '0'
if (!ok)
KCC_ERR(i18n("Failed to convert argument '%1' to integer.", args.first()));
if (cmd == QLatin1String("set-tab-width")) {
if (val < 1) {
KCC_ERR(i18n("Width must be at least 1."));
}
v->doc()->config()->setTabWidth(val);
} else if (cmd == QLatin1String("set-indent-width")) {
if (val < 1) {
KCC_ERR(i18n("Width must be at least 1."));
}
v->doc()->config()->setIndentationWidth(val);
} else if (cmd == QLatin1String("set-word-wrap-column")) {
if (val < 2) {
KCC_ERR(i18n("Column must be at least 1."));
}
v->doc()->setWordWrapAt(val);
} else if (cmd == QLatin1String("goto")) {
if (args.first().at(0) == QLatin1Char('-') || args.first().at(0) == QLatin1Char('+')) {
// if the number starts with a minus or plus sign, add/subtract the number
val = v->cursorPosition().line() + val;
} else {
val--; // convert given line number to the internal representation of line numbers
}
// constrain cursor to the range [0, number of lines]
if (val < 0) {
val = 0;
} else if (val > v->doc()->lines() - 1) {
val = v->doc()->lines() - 1;
}
v->setCursorPosition(KTextEditor::Cursor(val, 0));
return true;
}
return true;
}
// ALL commands that takes 1 boolean argument.
else if (cmd == QLatin1String("set-icon-border") || cmd == QLatin1String("set-folding-markers") || cmd == QLatin1String("set-indent-pasted-text")
|| cmd == QLatin1String("set-line-numbers") || cmd == QLatin1String("set-replace-tabs") || cmd == QLatin1String("set-show-tabs")
|| cmd == QLatin1String("set-word-wrap") || cmd == QLatin1String("set-wrap-cursor") || cmd == QLatin1String("set-replace-tabs-save")
|| cmd == QLatin1String("set-show-indent")) {
if (args.isEmpty()) {
KCC_ERR(i18n("Usage: %1 on|off|1|0|true|false", cmd));
}
bool enable = false;
KateDocumentConfig *const config = v->doc()->config();
if (getBoolArg(args.first(), &enable)) {
if (cmd == QLatin1String("set-icon-border")) {
v->setIconBorder(enable);
} else if (cmd == QLatin1String("set-folding-markers")) {
v->setFoldingMarkersOn(enable);
} else if (cmd == QLatin1String("set-line-numbers")) {
v->setLineNumbersOn(enable);
} else if (cmd == QLatin1String("set-show-indent")) {
v->renderer()->setShowIndentLines(enable);
} else if (cmd == QLatin1String("set-indent-pasted-text")) {
config->setIndentPastedText(enable);
} else if (cmd == QLatin1String("set-replace-tabs")) {
config->setReplaceTabsDyn(enable);
} else if (cmd == QLatin1String("set-show-tabs")) {
config->setShowTabs(enable);
} else if (cmd == QLatin1String("set-show-trailing-spaces")) {
config->setShowSpaces(enable ? KateDocumentConfig::Trailing : KateDocumentConfig::None);
} else if (cmd == QLatin1String("set-word-wrap")) {
v->doc()->setWordWrap(enable);
}
return true;
} else
KCC_ERR(i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false", args.first(), cmd));
} else if (cmd == QLatin1String("set-remove-trailing-spaces")) {
// need at least one item, otherwise args.first() crashes
if (args.count() != 1) {
KCC_ERR(i18n("Usage: set-remove-trailing-spaces 0|-|none or 1|+|mod|modified or 2|*|all"));
}
QString tmp = args.first().toLower().trimmed();
if (tmp == QLatin1String("1") || tmp == QLatin1String("modified") || tmp == QLatin1String("mod") || tmp == QLatin1String("+")) {
v->doc()->config()->setRemoveSpaces(1);
} else if (tmp == QLatin1String("2") || tmp == QLatin1String("all") || tmp == QLatin1String("*")) {
v->doc()->config()->setRemoveSpaces(2);
} else {
v->doc()->config()->setRemoveSpaces(0);
}
}
// unlikely..
KCC_ERR(i18n("Unknown command '%1'", cmd));
}
bool KateCommands::CoreCommands::supportsRange(const QString &range)
{
static QStringList l;
if (l.isEmpty()) {
l << QStringLiteral("indent") << QStringLiteral("unindent") << QStringLiteral("cleanindent") << QStringLiteral("comment") << QStringLiteral("uncomment")
<< QStringLiteral("kill-line") << QStringLiteral("fold") << QStringLiteral("tfold");
}
return l.contains(range);
}
KCompletion *KateCommands::CoreCommands::completionObject(KTextEditor::View *view, const QString &cmd)
{
Q_UNUSED(view)
if (cmd == QLatin1String("set-highlight")) {
QStringList l;
l.reserve(KateHlManager::self()->modeList().size());
const auto modeList = KateHlManager::self()->modeList();
for (const auto &hl : modeList) {
l << hl.name();
}
KateCmdShellCompletion *co = new KateCmdShellCompletion();
co->setItems(l);
co->setIgnoreCase(true);
return co;
} else if (cmd == QLatin1String("set-remove-trailing-spaces")) {
QStringList l;
l << QStringLiteral("none") << QStringLiteral("modified") << QStringLiteral("all");
KateCmdShellCompletion *co = new KateCmdShellCompletion();
co->setItems(l);
co->setIgnoreCase(true);
return co;
} else if (cmd == QLatin1String("set-indent-mode")) {
QStringList l = KateAutoIndent::listIdentifiers();
KateCmdShellCompletion *co = new KateCmdShellCompletion();
co->setItems(l);
co->setIgnoreCase(true);
return co;
}
return nullptr;
}
// END CoreCommands
// BEGIN Character
KateCommands::Character *KateCommands::Character::m_instance = nullptr;
bool KateCommands::Character::help(class KTextEditor::View *, const QString &cmd, QString &msg)
{
if (cmd.trimmed() == QLatin1String("char")) {
msg = i18n(
" char identifier
"
"This command allows you to insert literal characters by their numerical identifier, in decimal, octal or hexadecimal form.
"
"Examples:
"
"- char 234
"
"- char 0x1234
"
"
");
return true;
}
return false;
}
bool KateCommands::Character::exec(KTextEditor::View *view, const QString &_cmd, QString &, const KTextEditor::Range &)
{
QString cmd = _cmd;
// hex, octal, base 9+1
static const QRegularExpression num(QStringLiteral("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,5})$"));
const QRegularExpressionMatch match = num.match(cmd);
if (!match.hasMatch()) {
return false;
}
cmd = match.captured(1);
// identify the base
unsigned short int number = 0;
int base = 10;
if (cmd.startsWith(QLatin1Char('x'))) {
cmd.remove(0, 1);
base = 16;
} else if (cmd.startsWith(QLatin1String("0x"))) {
cmd.remove(0, 2);
base = 16;
} else if (cmd[0] == QLatin1Char('0')) {
base = 8;
}
bool ok;
number = cmd.toUShort(&ok, base);
if (!ok || number == 0) {
return false;
}
if (number <= 255) {
char buf[2];
buf[0] = (char)number;
buf[1] = 0;
view->document()->insertText(view->cursorPosition(), QString::fromLatin1(buf));
} else {
// do the unicode thing
QChar c(number);
view->document()->insertText(view->cursorPosition(), QString(&c, 1));
}
return true;
}
// END Character
// BEGIN Date
KateCommands::Date *KateCommands::Date::m_instance = nullptr;
bool KateCommands::Date::help(class KTextEditor::View *, const QString &cmd, QString &msg)
{
if (cmd.trimmed() == QLatin1String("date")) {
msg = i18n(
"date or date format
"
"Inserts a date/time string as defined by the specified format, or the format yyyy-MM-dd hh:mm:ss if none is specified.
"
"Possible format specifiers are:"
"
"
"d | The day as number without a leading zero (1-31). |
"
"dd | The day as number with a leading zero (01-31). |
"
"ddd | The abbreviated localized day name (e.g. 'Mon'..'Sun'). |
"
"dddd | The long localized day name (e.g. 'Monday'..'Sunday'). |
"
"M | The month as number without a leading zero (1-12). |
"
"MM | The month as number with a leading zero (01-12). |
"
"MMM | The abbreviated localized month name (e.g. 'Jan'..'Dec'). |
"
"yy | The year as two digit number (00-99). |
"
"yyyy | The year as four digit number (1752-8000). |
"
"h | The hour without a leading zero (0..23 or 1..12 if AM/PM display). |
"
"hh | The hour with a leading zero (00..23 or 01..12 if AM/PM display). |
"
"m | The minute without a leading zero (0..59). |
"
"mm | The minute with a leading zero (00..59). |
"
"s | The second without a leading zero (0..59). |
"
"ss | The second with a leading zero (00..59). |
"
"z | The milliseconds without leading zeroes (0..999). |
"
"zzz | The milliseconds with leading zeroes (000..999). |
"
"AP | Use AM/PM display. AP will be replaced by either \"AM\" or \"PM\". |
"
"ap | Use am/pm display. ap will be replaced by either \"am\" or \"pm\". |
"
"
");
return true;
}
return false;
}
bool KateCommands::Date::exec(KTextEditor::View *view, const QString &cmd, QString &, const KTextEditor::Range &)
{
if (!cmd.startsWith(QLatin1String("date"))) {
return false;
}
if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length() - 5)).length() > 0) {
view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length() - 5)));
} else {
view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")));
}
return true;
}
// END Date
KateCommands::EditingCommands *KateCommands::EditingCommands::s_instance = nullptr;
KateCommands::EditingCommands::EditingCommands()
: KTextEditor::Command({
QStringLiteral("uniq"),
QStringLiteral("sortuniq"),
QStringLiteral("natsort"),
QStringLiteral("sort"),
})
{
}
QList KateCommands::EditingCommands::allCommands()
{
static QList cmds{
{.name = i18n("Remove Duplicate Lines"), .cmd = QStringLiteral("uniq")},
{.name = i18n("Remove Duplicates and Sort Text Alphabetically"), .cmd = QStringLiteral("sortuniq")},
{.name = i18n("Sort Text Naturally"), .cmd = QStringLiteral("natsort")},
{.name = i18n("Sort Selected Text Alphabetically"), .cmd = QStringLiteral("sort")},
};
return cmds;
}
bool KateCommands::EditingCommands::exec(KTextEditor::View *view, const QString &cmd, QString &, const KTextEditor::Range &)
{
auto getDocAndRange = [view]() -> std::pair {
KTextEditor::Range r = view->document()->documentRange();
if (view->selection()) {
return {view->document(), view->selectionRange()};
}
return {view->document(), r};
};
auto apply = [](KTextEditor::Range range, KTextEditor::Document *doc, const QStringList &lines) {
if (range == doc->documentRange()) {
doc->setText(lines);
} else {
doc->replaceText(range, lines);
}
};
if (cmd == QStringLiteral("uniq")) {
const auto [doc, range] = getDocAndRange();
const QStringList lines = doc->textLines(range);
QSet seenLines;
QStringList uniqueLines;
for (const auto &line : lines) {
auto it = seenLines.find(line);
if (it == seenLines.end()) {
seenLines.insert(line);
uniqueLines.push_back(line);
}
}
apply(range, doc, uniqueLines);
return true;
} else if (cmd == QStringLiteral("sortuniq")) {
const auto [doc, range] = getDocAndRange();
QStringList lines = doc->textLines(range);
std::sort(lines.begin(), lines.end());
auto it = std::unique(lines.begin(), lines.end());
if (it != lines.end()) {
lines.erase(it, lines.end());
}
apply(range, doc, lines);
return true;
} else if (cmd == QStringLiteral("natsort")) {
const auto [doc, range] = getDocAndRange();
QStringList lines = doc->textLines(range);
QCollator col;
col.setNumericMode(true);
std::sort(lines.begin(), lines.end(), col);
apply(range, doc, lines);
return true;
} else if (cmd == QStringLiteral("sort")) {
const auto [doc, range] = getDocAndRange();
QStringList lines = doc->textLines(range);
std::sort(lines.begin(), lines.end());
apply(range, doc, lines);
return true;
}
return false;
}
bool KateCommands::EditingCommands::help(class KTextEditor::View *, const QString &cmd, QString &msg)
{
if (cmd == QStringLiteral("sort")) {
msg = i18n(
"sort
"
"Sort the selected text or whole document if there is no selection
");
} else if (cmd == QStringLiteral("uniq")) {
msg = i18n(
"uniq
"
"Remove duplicate lines from the selected text or whole document if there is no selection.
");
} else if (cmd == QStringLiteral("sortuniq")) {
msg = i18n(
"sortuniq
"
"Sort the selected text or whole document and then remove all duplicate lines.
");
} else if (cmd == QStringLiteral("natsort")) {
msg = i18n(
"natsort
"
"Sort the selected text or whole document in natural order.
Here is an example to show the difference to the normal sort "
"method:
sort(a10, a1, a2) => a1, a10, a2
natsort(a10, a1, a2) => a1, a2, a10
");
} else {
return false;
}
return true;
}