void KeywordItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { KTextEditor::Document *document = view->document(); if ( !m_replacement.isEmpty() ) { QString replacement = m_replacement; replacement = replacement.replace('\n', '\n' + getIndendation(document->line(word.start().line()))); replacement = replacement.replace(QLatin1String("%INDENT%"), indentString(document)); int cursorPos = replacement.indexOf(QStringLiteral("%CURSOR%")); int selectionEnd = -1; if ( cursorPos != -1 ) { replacement.remove(QStringLiteral("%CURSOR%")); } else { cursorPos = replacement.indexOf(QStringLiteral("%SELECT%")); if ( cursorPos != -1 ) { replacement.remove(QStringLiteral("%SELECT%")); selectionEnd = replacement.indexOf(QStringLiteral("%ENDSELECT%"), cursorPos + 1); if ( selectionEnd == -1 ) { selectionEnd = replacement.length(); } replacement.remove(QStringLiteral("%ENDSELECT%")); } } document->replaceText(word, replacement); if ( cursorPos != -1 ) { if (view) { replacement = replacement.left(cursorPos); KTextEditor::Cursor newPos( word.start().line() + replacement.count('\n'), word.start().column() + replacement.length() - replacement.lastIndexOf('\n') - 1 ); view->setCursorPosition(newPos); if ( selectionEnd != -1 ) { ///TODO: maybe we want to support multi-line selections in the future? view->setSelection( KTextEditor::Range( newPos, KTextEditor::Cursor( newPos.line(), newPos.column() + selectionEnd - cursorPos ) ) ); } } } } else { document->replaceText(word, m_keyword + ' '); } }
void FunctionDeclarationCompletionItem::executed(KTextEditor::View* view, const KTextEditor::Range& word) { qCDebug(KDEV_PYTHON_CODECOMPLETION) << "FunctionDeclarationCompletionItem executed"; KTextEditor::Document* document = view->document(); auto resolvedDecl = Helper::resolveAliasDeclaration(declaration().data()); DUChainReadLocker lock; auto functionDecl = Helper::functionForCalled(resolvedDecl).declaration; lock.unlock(); if ( ! functionDecl && (! resolvedDecl || ! resolvedDecl->abstractType() || resolvedDecl->abstractType()->whichType() != AbstractType::TypeStructure) ) { qCritical(KDEV_PYTHON_CODECOMPLETION) << "ERROR: could not get declaration data, not executing completion item!"; return; } QString suffix = "()"; KTextEditor::Range checkPrefix(word.start().line(), 0, word.start().line(), word.start().column()); KTextEditor::Range checkSuffix(word.end().line(), word.end().column(), word.end().line(), document->lineLength(word.end().line())); if ( m_doNotCall || document->text(checkSuffix).trimmed().startsWith('(') || document->text(checkPrefix).trimmed().endsWith('@') || (functionDecl && Helper::findDecoratorByName(functionDecl, QLatin1String("property"))) ) { // don't insert brackets if they're already there, // the item is a decorator, or if it's an import item. suffix.clear(); } // place cursor behind bracktes by default int skip = 2; if ( functionDecl ) { bool needsArguments = false; int argumentCount = functionDecl->type<FunctionType>()->arguments().length(); if ( functionDecl->context()->type() == KDevelop::DUContext::Class ) { // it's a member function, so it has the implicit self // TODO static methods needsArguments = argumentCount > 1; } else { // it's a free function needsArguments = argumentCount > 0; } if ( needsArguments ) { // place cursor in brackets if there's parameters skip = 1; } } document->replaceText(word, declaration()->identifier().toString() + suffix); view->setCursorPosition( Cursor(word.end().line(), word.end().column() + skip) ); }
void NormalDeclarationCompletionItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { if( m_completionContext && m_completionContext->depth() != 0 ) return; //Do not replace any text when it is an argument-hint KTextEditor::Document* document = view->document(); QString newText; { KDevelop::DUChainReadLocker lock(KDevelop::DUChain::lock()); if(m_declaration) { newText = declarationName(); } else { qCDebug(LANGUAGE) << "Declaration disappeared"; return; } } document->replaceText(word, newText); KTextEditor::Range newRange = word; newRange.setEnd(KTextEditor::Cursor(newRange.end().line(), newRange.start().column() + newText.length())); executed(view, newRange); }
void ReplaceMatches::doReplaceNextMatch() { if ((!m_manager) || (m_cancelReplace) || (m_tree->topLevelItemCount() != 1)) { m_rootIndex = -1; emit replaceDone(); return; } // NOTE The document managers signal documentWillBeDeleted() must be connected to // cancelReplace(). A closed file could lead to a crash if it is not handled. // Open the file QTreeWidgetItem *rootItem = m_tree->topLevelItem(0)->child(m_rootIndex); if (!rootItem) { m_rootIndex = -1; emit replaceDone(); return; } if (!rootItem->data(0, ColumnRole).toString().isEmpty()) { // this is a search as you type replace rootItem = m_tree->topLevelItem(0); m_cancelReplace = true; // only one document... } if (rootItem->checkState(0) == Qt::Unchecked) { m_rootIndex++; emit replaceNextMatch(); return; } KTextEditor::Document *doc; QString docUrl = rootItem->data(0, FileUrlRole).toString(); QString docName = rootItem->data(0, FileNameRole).toString(); if (docUrl.isEmpty()) { doc = findNamed(rootItem->data(0, FileNameRole).toString()); } else { doc = m_manager->findUrl(QUrl::fromUserInput(docUrl)); if (!doc) { doc = m_manager->openUrl(QUrl::fromUserInput(rootItem->data(0, FileUrlRole).toString())); } } if (!doc) { m_rootIndex++; emit replaceNextMatch(); return; } QVector<KTextEditor::MovingRange*> rVector; QStringList rTexts; KTextEditor::MovingInterface* miface = qobject_cast<KTextEditor::MovingInterface*>(doc); int line; int column; int matchLen; int endLine; int endColumn; QTreeWidgetItem *item; QString matchLines; // lines might be modified so search the document again for (int i=0; i<rootItem->childCount(); i++) { item = rootItem->child(i); if (item->checkState(0) == Qt::Unchecked) continue; line = endLine= item->data(0, LineRole).toInt(); column = item->data(0, ColumnRole).toInt(); matchLen = item->data(0, MatchLenRole).toInt(); matchLines = doc->line(line).mid(column); while (matchLines.size() < matchLen) { if (endLine+1 >= doc->lines()) break; endLine++; matchLines+= QLatin1Char('\n') + doc->line(endLine); } QRegularExpressionMatch match = m_regExp.match(matchLines); if (match.capturedStart() != 0) { qDebug() << matchLines << "Does not match" << m_regExp.pattern(); continue; } QString replaceText = m_replaceText; replaceText.replace(QStringLiteral("\\\\"), QStringLiteral("¤Search&Replace¤")); // allow captures \0 .. \9 for (int j = qMin(9, match.lastCapturedIndex()); j >= 0; --j) { replaceText.replace(QString(QStringLiteral("\\%1")).arg(j), match.captured(j)); } // allow captures \{0} .. \{9999999}... for (int j = match.lastCapturedIndex(); j >= 0; --j) { replaceText.replace(QString(QStringLiteral("\\{%1}")).arg(j), match.captured(j)); } replaceText.replace(QStringLiteral("\\n"), QStringLiteral("\n")); replaceText.replace(QStringLiteral("\\t"), QStringLiteral("\t")); replaceText.replace(QStringLiteral("¤Search&Replace¤"), QStringLiteral("\\")); rTexts << replaceText; replaceText.replace(QLatin1Char('\n'), QStringLiteral("\\n")); replaceText.replace(QLatin1Char('\t'), QStringLiteral("\\t")); QString html = item->data(0, PreMatchRole).toString(); html += QStringLiteral("<i><s>") + item->data(0, MatchRole).toString() + QStringLiteral("</s></i> "); html += QStringLiteral("<b>") + replaceText + QStringLiteral("</b>"); html += item->data(0, PostMatchRole).toString(); item->setData(0, Qt::DisplayRole, i18n("Line: <b>%1</b>: %2",line+1, html)); endLine = line; endColumn = column+matchLen; while ((endLine < doc->lines()) && (endColumn > doc->line(endLine).size())) { endColumn -= doc->line(endLine).size(); endColumn--; // remove one for '\n' endLine++; } KTextEditor::Range range(line, column, endLine, endColumn); KTextEditor::MovingRange* mr = miface->newMovingRange(range); rVector.append(mr); } for (int i=0; i<rVector.size(); i++) { line = rVector[i]->start().line(); column = rVector[i]->start().column(); doc->replaceText(*rVector[i], rTexts[i]); emit matchReplaced(doc, line, column, rTexts[i].length()); } qDeleteAll(rVector); m_rootIndex++; emit replaceNextMatch(); }
void ImplementationItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { DUChainReadLocker lock(DUChain::lock()); KTextEditor::Document *document = view->document(); QString replText; if (m_declaration) { //TODO:respect custom code styles // get existing modifiers so we can respect the user's choice of public/protected and final QStringList modifiers = getMethodTokens(document->text(KTextEditor::Range(KTextEditor::Cursor::start(), word.start()))); // get range to replace KTextEditor::Range replaceRange(word); if (!modifiers.isEmpty()) { // TODO: is there no easy API to map QString Index to a KTextEditor::Cursor ?! QString methodText = document->text(KTextEditor::Range(KTextEditor::Cursor::start(), word.start())); methodText = methodText.left(methodText.lastIndexOf(modifiers.last(), -1, Qt::CaseInsensitive)); replaceRange.start() = KTextEditor::Cursor(methodText.count('\n'), methodText.length() - methodText.lastIndexOf('\n') - 1); } // get indendation QString indendation; { QString currentLine = document->line(replaceRange.start().line()); indendation = getIndendation(currentLine); if ( !currentLine.isEmpty() && currentLine != indendation ) { // since theres some non-whitespace in this line, skip to the enxt one replText += '\n' + indendation; } if (indendation.isEmpty()) { // use a minimal indendation // TODO: respect code style indendation = QStringLiteral(" "); replText += indendation; } } #if 0 //Disabled, because not everyone writes phpdoc for every function //TODO: move to a phpdoc helper // build phpdoc comment { QualifiedIdentifier parentClassIdentifier; if (DUContext* pctx = m_declaration->context()) { parentClassIdentifier = pctx->localScopeIdentifier(); } else { qCDebug(COMPLETION) << "completion item for implementation has no parent context!"; } replText += "/**\n" + indendation + " * "; // insert old comment: const QString indentationWithExtra = "\n" + indendation + " *"; replText += m_declaration->comment().replace('\n', indentationWithExtra.toAscii().constData()); replText += "\n" + indendation + " * @overload " + m_declaration->internalContext()->scopeIdentifier(true).toString(); replText += "\n" + indendation + " **/\n" + indendation; } #endif // write function signature // copy existing modifiers if (!modifiers.isEmpty()) { // the tokens are in a bad order and there's no reverse method or similar, so we can't simply join the tokens QStringList::const_iterator i = modifiers.constEnd() - 1; while (true) { replText += (*i) + ' '; if (i == modifiers.constBegin()) { break; } else { --i; } } } QString functionName; bool isConstructorOrDestructor = false; bool isInterface = false; if (ClassMemberDeclaration* member = dynamic_cast<ClassMemberDeclaration*>(m_declaration.data())) { // NOTE: it should _never_ be private - but that's the completionmodel / context / worker's job if (!modifiers.contains(QStringLiteral("public")) && !modifiers.contains(QStringLiteral("protected"))) { if (member->accessPolicy() == Declaration::Protected) { replText += QLatin1String("protected "); } else { replText += QLatin1String("public "); } } if (!modifiers.contains(QStringLiteral("static")) && member->isStatic()) { replText += QLatin1String("static "); } functionName = member->identifier().toString(); ClassMethodDeclaration* method = dynamic_cast<ClassMethodDeclaration*>(m_declaration.data()); if (method) { functionName = method->prettyName().str(); isConstructorOrDestructor = method->isConstructor() || method->isDestructor(); } if (member->context() && member->context()->owner()) { ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(member->context()->owner()); if (classDec) { isInterface = (classDec->classType() == ClassDeclarationData::Interface); } } } else { qCDebug(COMPLETION) << "completion item for implementation was not a classfunction declaration!"; functionName = m_declaration->identifier().toString(); } if (m_type == ImplementationItem::OverrideVar) { replText += "$" + functionName + " = "; } else { if (!modifiers.contains(QStringLiteral("function"))) { replText += QLatin1String("function "); } replText += functionName; { // get argument list QString arguments; createArgumentList(*this, arguments, 0, true); replText += arguments; } QString arguments; QVector<Declaration*> parameters; if (DUChainUtils::getArgumentContext(m_declaration.data())) parameters = DUChainUtils::getArgumentContext(m_declaration.data())->localDeclarations(); arguments = '('; bool first = true; foreach(Declaration* dec, parameters) { if (first) first = false; else arguments += QLatin1String(", "); arguments += '$' + dec->identifier().toString(); } arguments += ')'; bool voidReturnType = false; if (FunctionType::Ptr::dynamicCast(m_declaration->abstractType())) { AbstractType::Ptr retType = FunctionType::Ptr::staticCast(m_declaration->abstractType())->returnType(); if (retType->equals(new IntegralType(IntegralType::TypeVoid))) { voidReturnType = true; } } replText += QStringLiteral("\n%1{\n%1 ").arg(indendation); if (isInterface || m_type == ImplementationItem::Implement) { } else if (!isConstructorOrDestructor && !voidReturnType) { replText += QStringLiteral("$ret = parent::%2%3;\n%1 return $ret;").arg(indendation, functionName, arguments); } else { replText += QStringLiteral("parent::%1%2;").arg(functionName, arguments); } replText += QStringLiteral("\n%1}\n%1") .arg(indendation); } //TODO: properly place the cursor inside the {} part document->replaceText(replaceRange, replText); } else {
void ReplaceMatches::doReplaceNextMatch() { if ((!m_manager) || (m_cancelReplace)) { m_rootIndex = -1; emit replaceDone(); return; } // NOTE The document managers signal documentWillBeDeleted() must be connected to // cancelReplace(). A closed file could lead to a crash if it is not handled. // Open the file QTreeWidgetItem *rootItem = m_tree->topLevelItem(m_rootIndex); if (!rootItem) { m_rootIndex = -1; emit replaceDone(); return; } if (rootItem->checkState(0) == Qt::Unchecked) { m_rootIndex++; emit replaceNextMatch(); return; } KTextEditor::Document *doc = m_manager->findUrl(rootItem->data(0, Qt::UserRole).toString()); if (!doc) { doc = m_manager->openUrl(rootItem->data(0, Qt::UserRole).toString()); } if (!doc) { m_rootIndex++; emit replaceNextMatch(); return; } QVector<KTextEditor::MovingRange*> rVector; KTextEditor::MovingInterface* miface = qobject_cast<KTextEditor::MovingInterface*>(doc); int line; int column; int len; QTreeWidgetItem *item; // lines might be modified so search the document again for (int i=0; i<rootItem->childCount(); i++) { item = rootItem->child(i); if (item->checkState(0) == Qt::Unchecked) continue; line = item->data(1, Qt::UserRole).toInt(); column = item->data(2, Qt::UserRole).toInt(); len = item->data(3, Qt::UserRole).toInt(); if (m_regExp.indexIn(doc->line(line), column) != column) { kDebug() << "expression does not match"; continue; } QString html = item->data(1, Qt::ToolTipRole).toString(); html += "<i><s>" + item->data(2, Qt::ToolTipRole).toString() + "</s></i> "; html += "<b>" + m_replaceText + "</b>"; html += item->data(3, Qt::ToolTipRole).toString(); item->setData(0, Qt::DisplayRole, QString("Line: <b>%1</b>: %2").arg(line+1).arg(html)); KTextEditor::Range range(line, column, line, column+len); KTextEditor::MovingRange* mr = miface->newMovingRange(range); rVector.append(mr); } for (int i=0; i<rVector.size(); i++) { line = rVector[i]->start().line(); column = rVector[i]->start().column(); doc->replaceText(*rVector[i], m_replaceText); emit matchReplaced(doc, line, column, m_replaceText.length()); } qDeleteAll(rVector); m_rootIndex++; emit replaceNextMatch(); }
// Do one completion, searching in the desired direction, // if possible void KateWordCompletionView::complete( bool fw ) { KTextEditor::Range r = range(); int inc = fw ? 1 : -1; KTextEditor::Document *doc = m_view->document(); if ( d->dcRange.isValid() ) { //kDebug( 13040 )<<"CONTINUE "<<d->dcRange; // this is a repeted activation // if we are back to where we started, reset. if ( ( fw && d->directionalPos == -1 ) || ( !fw && d->directionalPos == 1 ) ) { const int spansColumns = d->liRange->end().column() - d->liRange->start().column(); if ( spansColumns > 0 ) doc->removeText( *d->liRange ); d->liRange->setRange( KTextEditor::Range::invalid() ); d->dcCursor = r.end(); d->directionalPos = 0; return; } if ( fw ) { const int spansColumns = d->liRange->end().column() - d->liRange->start().column(); d->dcCursor.setColumn( d->dcCursor.column() + spansColumns ); } d->directionalPos += inc; } else // new completion, reset all { //kDebug( 13040 )<<"RESET FOR NEW"; d->dcRange = r; d->liRange->setRange( KTextEditor::Range::invalid() ); d->dcCursor = r.start(); d->directionalPos = inc; d->liRange->setView( m_view ); connect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(slotCursorMoved()) ); } d->re.setPattern( "\\b" + doc->text( d->dcRange ) + "(\\w+)" ); int pos ( 0 ); QString ln = doc->line( d->dcCursor.line() ); while ( true ) { //kDebug( 13040 )<<"SEARCHING FOR "<<d->re.pattern()<<" "<<ln<<" at "<<d->dcCursor; pos = fw ? d->re.indexIn( ln, d->dcCursor.column() ) : d->re.lastIndexIn( ln, d->dcCursor.column() ); if ( pos > -1 ) // we matched a word { //kDebug( 13040 )<<"USABLE MATCH"; QString m = d->re.cap( 1 ); if ( m != doc->text( *d->liRange ) && (d->dcCursor.line() != d->dcRange.start().line() || pos != d->dcRange.start().column() ) ) { // we got good a match! replace text and return. d->isCompleting = true; KTextEditor::Range replaceRange(d->liRange->toRange()); if (!replaceRange.isValid()) { replaceRange.setRange(r.end(), r.end()); } doc->replaceText( replaceRange, m ); d->liRange->setRange( KTextEditor::Range( d->dcRange.end(), m.length() ) ); d->dcCursor.setColumn( pos ); // for next try d->isCompleting = false; return; } // equal to last one, continue else { //kDebug( 13040 )<<"SKIPPING, EQUAL MATCH"; d->dcCursor.setColumn( pos ); // for next try if ( fw ) d->dcCursor.setColumn( pos + m.length() ); else { if ( pos == 0 ) { if ( d->dcCursor.line() > 0 ) { int l = d->dcCursor.line() + inc; ln = doc->line( l ); d->dcCursor.setPosition( l, ln.length() ); } else { KNotification::beep(); return; } } else d->dcCursor.setColumn( d->dcCursor.column()-1 ); } } } else // no match { //kDebug( 13040 )<<"NO MATCH"; if ( (! fw && d->dcCursor.line() == 0 ) || ( fw && d->dcCursor.line() >= doc->lines() ) ) { KNotification::beep(); return; } int l = d->dcCursor.line() + inc; ln = doc->line( l ); d->dcCursor.setPosition( l, fw ? 0 : ln.length() ); } } // while true }