File indexing completed on 2024-04-28 11:20:45

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com>
0004 */
0005 
0006 #include "completionobject.h"
0007 using namespace Cantor;
0008 
0009 #include <QStringList>
0010 #include <QTimer>
0011 #include <QDebug>
0012 
0013 #include "session.h"
0014 
0015 class Cantor::CompletionObjectPrivate
0016 {
0017   public:
0018     QStringList completions;
0019     QString line;
0020     QString command;
0021     QString identifier;
0022     QString completion;
0023     int position;
0024     Session* session;
0025     bool parenCompletion;
0026 };
0027 
0028 CompletionObject::CompletionObject(Session* session) :
0029     d(new CompletionObjectPrivate)
0030 {
0031     setParent(session);
0032     d->position = -1;
0033     d->session=session;
0034 
0035     connect(this, &CompletionObject::fetchingDone, this, &CompletionObject::findCompletion);
0036     connect(this, &CompletionObject::fetchingTypeDone, this, &CompletionObject::completeLineWithType);
0037 
0038     setCompletionMode(KCompletion::CompletionShell);
0039 }
0040 
0041 CompletionObject::~CompletionObject()
0042 {
0043     delete d;
0044 }
0045 
0046 QString CompletionObject::command() const
0047 {
0048     return d->command;
0049 }
0050 
0051 Session* CompletionObject::session() const
0052 {
0053     return d->session;
0054 }
0055 
0056 QStringList CompletionObject::completions() const
0057 {
0058     return d->completions;
0059 }
0060 
0061 QString CompletionObject::identifier() const
0062 {
0063     return d->identifier;
0064 }
0065 
0066 QString CompletionObject::completion() const
0067 {
0068     return d->completion;
0069 }
0070 
0071 void CompletionObject::setLine(const QString& line, int index)
0072 {
0073     d->parenCompletion = false;
0074     d->line = line;
0075     if (index < 0)
0076         index = line.length();
0077     if (index > 1 && line[index-1] == QLatin1Char('(')) {
0078         --index;                   // move before the parenthesis
0079         d->parenCompletion = true; // but remember it was there
0080     }
0081     int cmd_index = locateIdentifier(line, index-1);
0082     if (cmd_index < 0)
0083         cmd_index = index;
0084     d->position=cmd_index;
0085     d->command=line.mid(cmd_index, index-cmd_index);
0086 
0087     //start a delayed fetch
0088     QTimer::singleShot(0, this, &CompletionObject::fetchCompletions);
0089 }
0090 
0091 void CompletionObject::updateLine(const QString& line, int index)
0092 {
0093     d->line = line;
0094     if (index < 0)
0095     index = line.length();
0096     int cmd_index = locateIdentifier(line, index-1);
0097     if (cmd_index < 0)
0098     cmd_index = index;
0099     d->command=line.mid(cmd_index, index-cmd_index);
0100 
0101     // start a delayed fetch
0102     // For some backends this is a lot of unnecessary work...
0103     QTimer::singleShot(0, this, &CompletionObject::fetchCompletions);
0104 }
0105 
0106 void CompletionObject::completeLine(const QString& comp, CompletionObject::LineCompletionMode mode)
0107 {
0108     d->identifier = comp;
0109     if (comp.isEmpty()) {
0110     int index = d->position + d->command.length();
0111     emit lineDone(d->line, index);
0112     } else if (mode == PreliminaryCompletion) {
0113     completeUnknownLine();
0114     } else /* mode == FinalCompletion */ {
0115     QTimer::singleShot(0, this, &CompletionObject::fetchIdentifierType);
0116     }
0117 }
0118 
0119 void CompletionObject::fetchIdentifierType()
0120 {
0121     emit fetchingTypeDone(UnknownType);
0122 }
0123 
0124 
0125 void CompletionObject::setCompletions(const QStringList& completions)
0126 {
0127     d->completions=completions;
0128     this->setItems(completions);
0129 }
0130 
0131 void CompletionObject::setCommand(const QString& cmd)
0132 {
0133     d->command=cmd;
0134 }
0135 
0136 int CompletionObject::locateIdentifier(const QString& cmd, int index) const
0137 {
0138     if (index < 0)
0139     return -1;
0140 
0141     int i;
0142     for (i=index; i>=0 && mayIdentifierContain(cmd[i]); --i)
0143     {}
0144 
0145     if (i==index || !mayIdentifierBeginWith(cmd[i+1]))
0146     return -1;
0147     return i+1;
0148 }
0149 
0150 bool CompletionObject::mayIdentifierContain(QChar c) const
0151 {
0152     return c.isLetter() || c.isDigit() || c == QLatin1Char('_');
0153 }
0154 
0155 bool CompletionObject::mayIdentifierBeginWith(QChar c) const
0156 {
0157     return c.isLetter() || c == QLatin1Char('_');
0158 }
0159 
0160 void CompletionObject::findCompletion()
0161 {
0162     if (d->parenCompletion) {
0163     disconnect(this, SIGNAL(fetchingTypeDone(IdentifierType)), nullptr, nullptr);
0164     connect(this, &CompletionObject::fetchingTypeDone, this, &CompletionObject::handleParenCompletionWithType);
0165     d->identifier = d->command;
0166     fetchIdentifierType();
0167     return;
0168     }
0169     d->completion = makeCompletion(command());
0170     emit done();
0171 }
0172 
0173 void CompletionObject::handleParenCompletionWithType(IdentifierType type)
0174 {
0175     disconnect(this, SIGNAL(fetchingTypeDone(IdentifierType)), nullptr, nullptr);
0176     connect(this, &CompletionObject::fetchingTypeDone, this, &CompletionObject::completeLineWithType);
0177 
0178     if (type == FunctionWithArguments || type == FunctionWithoutArguments) {
0179     d->completion = d->command;
0180     emit done();
0181     }
0182 }
0183 
0184 void CompletionObject::completeLineWithType(IdentifierType type)
0185 {
0186     switch(type) {
0187     case VariableType:
0188     completeVariableLine();
0189     break;
0190     case FunctionWithArguments:
0191     case FunctionWithoutArguments:
0192     completeFunctionLine(type);
0193     break;
0194     case KeywordType:
0195     completeKeywordLine();
0196     break;
0197     case UnknownType:
0198     completeUnknownLine();
0199     break;
0200     }
0201 }
0202 
0203 void CompletionObject::completeFunctionLine(IdentifierType type)
0204 {
0205     QString newline;
0206     int newindex;
0207 
0208     QString func = d->identifier;
0209     int after_command =  d->position + d->command.length();
0210     QString part1 = d->line.left(d->position) + func;
0211     int index = d->position + func.length() + 1;
0212     if (after_command < d->line.length() && d->line.at(after_command) == QLatin1Char('(')) {
0213     QString part2 = d->line.mid(after_command+1);
0214     int i;
0215     // search for next non-space position
0216     for (i = after_command+1;
0217          i < d->line.length() && d->line.at(i).isSpace();
0218          ++i) {}
0219     if (type == FunctionWithArguments) {
0220         if (i < d->line.length()) {
0221         newline = part1+QLatin1Char('(')+part2;
0222         newindex = index;
0223         } else {
0224         newline = part1+QLatin1String("()")+part2;
0225         newindex = index;
0226         }
0227     } else /*type == FunctionWithoutArguments*/ {
0228         if (i < d->line.length() && d->line.at(i) == QLatin1Char(')')) {
0229         newline = part1+QLatin1Char('(')+part2;
0230         newindex = index+i-after_command;
0231         } else {
0232         newline = part1+QLatin1String("()")+part2;
0233         newindex = index+1;
0234         }
0235     }
0236     } else {
0237     QString part2 = d->line.mid(after_command);
0238     if (type == FunctionWithArguments) {
0239         newline = part1+QLatin1String("()")+part2;
0240         newindex = index;
0241     } else /*type == FunctionWithoutArguments*/ {
0242         newline = part1+QLatin1String("()")+part2;
0243         newindex = index+1;
0244     }
0245     }
0246     emit lineDone(newline, newindex);
0247 }
0248 
0249 void CompletionObject::completeKeywordLine()
0250 {
0251     QString keyword = d->identifier;
0252     int after_command = d->position + d->command.length();
0253     int newindex = d->position + keyword.length() + 1;
0254     QString part1 = d->line.left(d->position) + keyword;
0255     QString part2 = d->line.mid(after_command);
0256     if (after_command < d->line.length() && d->line.at(after_command) == QLatin1Char(' '))
0257     emit lineDone(part1+part2, newindex);
0258     else
0259     emit lineDone(part1+QLatin1Char(' ')+part2, newindex);
0260 }
0261 
0262 void CompletionObject::completeVariableLine()
0263 {
0264     QString var = d->identifier;
0265     int after_command = d->position + d->command.length();
0266     QString newline = d->line.left(d->position) + var + d->line.mid(after_command);
0267     int newindex = d->position + var.length();
0268     emit lineDone(newline, newindex);
0269 }
0270 
0271 void CompletionObject::completeUnknownLine()
0272 {
0273     // identifiers of unknown type are completed like variables
0274     completeVariableLine();
0275 }
0276 
0277