File indexing completed on 2024-04-14 03:55:28

0001 /*
0002     SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann <cullmann@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "katecmd.h"
0008 #include "kateglobal.h"
0009 
0010 #include "katepartdebug.h"
0011 #include <KCompletionMatches>
0012 
0013 #include <ktexteditor/command.h>
0014 
0015 // BEGIN KateCmd
0016 #define CMD_HIST_LENGTH 256
0017 
0018 KateCmd::KateCmd()
0019 {
0020     m_cmdCompletion.addItem(QStringLiteral("help"));
0021 }
0022 
0023 KateCmd::~KateCmd() = default;
0024 
0025 bool KateCmd::registerCommand(KTextEditor::Command *cmd)
0026 {
0027     const QStringList &l = cmd->cmds();
0028 
0029     for (int z = 0; z < l.count(); z++) {
0030         if (m_dict.contains(l[z])) {
0031             qCDebug(LOG_KTE) << "Command already registered: " << l[z] << ". Aborting.";
0032             return false;
0033         }
0034     }
0035 
0036     for (int z = 0; z < l.count(); z++) {
0037         m_dict.insert(l[z], cmd);
0038         // qCDebug(LOG_KTE)<<"Inserted command:"<<l[z];
0039     }
0040 
0041     m_cmds += l;
0042     m_cmdCompletion.insertItems(l);
0043 
0044     return true;
0045 }
0046 
0047 bool KateCmd::unregisterCommand(KTextEditor::Command *cmd)
0048 {
0049     QStringList l;
0050 
0051     QHash<QString, KTextEditor::Command *>::const_iterator i = m_dict.constBegin();
0052     while (i != m_dict.constEnd()) {
0053         if (i.value() == cmd) {
0054             l << i.key();
0055         }
0056         ++i;
0057     }
0058 
0059     for (QStringList::Iterator it1 = l.begin(); it1 != l.end(); ++it1) {
0060         m_dict.remove(*it1);
0061         m_cmdCompletion.removeItem(*it1);
0062         // qCDebug(LOG_KTE)<<"Removed command:"<<*it1;
0063     }
0064 
0065     return true;
0066 }
0067 
0068 KTextEditor::Command *KateCmd::queryCommand(const QString &cmd) const
0069 {
0070     // a command can be named ".*[\w\-]+" with the constrain that it must
0071     // contain at least one letter.
0072     int f = 0;
0073     bool b = false;
0074 
0075     // special case: '-' and '_' can be part of a command name, but if the
0076     // command is 's' (substitute), it should be considered the delimiter and
0077     // should not be counted as part of the command name
0078     if (cmd.length() >= 2 && cmd.at(0) == QLatin1Char('s') && (cmd.at(1) == QLatin1Char('-') || cmd.at(1) == QLatin1Char('_'))) {
0079         return m_dict.value(QStringLiteral("s"));
0080     }
0081 
0082     for (; f < cmd.length(); f++) {
0083         if (cmd[f].isLetter()) {
0084             b = true;
0085         }
0086         if (b && (!cmd[f].isLetterOrNumber() && cmd[f] != QLatin1Char('-') && cmd[f] != QLatin1Char('_'))) {
0087             break;
0088         }
0089     }
0090     return m_dict.value(cmd.left(f));
0091 }
0092 
0093 QList<KTextEditor::Command *> KateCmd::commands() const
0094 {
0095     return m_dict.values();
0096 }
0097 
0098 QStringList KateCmd::commandList() const
0099 {
0100     return m_cmds;
0101 }
0102 
0103 KateCmd *KateCmd::self()
0104 {
0105     return KTextEditor::EditorPrivate::self()->cmdManager();
0106 }
0107 
0108 void KateCmd::appendHistory(const QString &cmd)
0109 {
0110     if (!m_history.isEmpty()) { // this line should be backported to 3.x
0111         if (m_history.last() == cmd) {
0112             return;
0113         }
0114     }
0115 
0116     if (m_history.count() == CMD_HIST_LENGTH) {
0117         m_history.removeFirst();
0118     }
0119 
0120     m_history.append(cmd);
0121 }
0122 
0123 const QString KateCmd::fromHistory(int index) const
0124 {
0125     if (index < 0 || index > m_history.count() - 1) {
0126         return QString();
0127     }
0128     return m_history[index];
0129 }
0130 
0131 KCompletion *KateCmd::commandCompletionObject()
0132 {
0133     return &m_cmdCompletion;
0134 }
0135 // END KateCmd
0136 
0137 // BEGIN KateCmdShellCompletion
0138 /*
0139    A lot of the code in the below class is copied from
0140    kdelibs/kio/kio/kshellcompletion.cpp
0141    SPDX-FileCopyrightText: 2000 David Smith <dsmith@algonet.se>
0142    SPDX-FileCopyrightText: 2004 Anders Lund <anders@alweb.dk>
0143 */
0144 KateCmdShellCompletion::KateCmdShellCompletion()
0145     : KCompletion()
0146 {
0147     m_word_break_char = QLatin1Char(' ');
0148     m_quote_char1 = QLatin1Char('\"');
0149     m_quote_char2 = QLatin1Char('\'');
0150     m_escape_char = QLatin1Char('\\');
0151 }
0152 
0153 QString KateCmdShellCompletion::makeCompletion(const QString &text)
0154 {
0155     // Split text at the last unquoted space
0156     //
0157     splitText(text, m_text_start, m_text_compl);
0158 
0159     // Make completion on the last part of text
0160     //
0161     return KCompletion::makeCompletion(m_text_compl);
0162 }
0163 
0164 void KateCmdShellCompletion::postProcessMatch(QString *match) const
0165 {
0166     if (match->isNull()) {
0167         return;
0168     }
0169 
0170     match->prepend(m_text_start);
0171 }
0172 
0173 void KateCmdShellCompletion::postProcessMatches(QStringList *matches) const
0174 {
0175     for (QStringList::Iterator it = matches->begin(); it != matches->end(); it++) {
0176         if (!(*it).isNull()) {
0177             (*it).prepend(m_text_start);
0178         }
0179     }
0180 }
0181 
0182 void KateCmdShellCompletion::postProcessMatches(KCompletionMatches *matches) const
0183 {
0184     for (KCompletionMatches::Iterator it = matches->begin(); it != matches->end(); it++) {
0185         if (!(*it).value().isNull()) {
0186             (*it).value().prepend(m_text_start);
0187         }
0188     }
0189 }
0190 
0191 void KateCmdShellCompletion::splitText(const QString &text, QString &text_start, QString &text_compl) const
0192 {
0193     bool in_quote = false;
0194     bool escaped = false;
0195     QChar p_last_quote_char;
0196     int last_unquoted_space = -1;
0197 
0198     for (int pos = 0; pos < text.length(); pos++) {
0199         if (escaped) {
0200             escaped = false;
0201         } else if (in_quote && text[pos] == p_last_quote_char) {
0202             in_quote = false;
0203         } else if (!in_quote && text[pos] == m_quote_char1) {
0204             p_last_quote_char = m_quote_char1;
0205             in_quote = true;
0206         } else if (!in_quote && text[pos] == m_quote_char2) {
0207             p_last_quote_char = m_quote_char2;
0208             in_quote = true;
0209         } else if (text[pos] == m_escape_char) {
0210             escaped = true;
0211         } else if (!in_quote && text[pos] == m_word_break_char) {
0212             while (pos + 1 < text.length() && text[pos + 1] == m_word_break_char) {
0213                 pos++;
0214             }
0215 
0216             if (pos + 1 == text.length()) {
0217                 break;
0218             }
0219 
0220             last_unquoted_space = pos;
0221         }
0222     }
0223 
0224     text_start = text.left(last_unquoted_space + 1);
0225 
0226     // the last part without trailing blanks
0227     text_compl = text.mid(last_unquoted_space + 1);
0228 }
0229 
0230 // END KateCmdShellCompletion