Example #1
0
void ModelTest::testCompletionRangeSecondLine()
{
    KTextEditor::Document* doc = KTextEditor::Editor::instance()->createDocument(0);
    doc->setText("body{color:red;}\nbody{font-w:normal;}");
                //                  01234567890123456789
    KTextEditor::View* view = doc->createView(0);
    CodeCompletionModel* model = new CodeCompletionModel(doc);

    KTextEditor::Cursor position(1, 9);
    KTextEditor::Range range = model->completionRange(view, position);
    kDebug() << range << doc->text(range);
    QCOMPARE(range, KTextEditor::Range(1, 5, 1, 11));
    QCOMPARE(doc->text(range), QString("font-w"));
    delete doc;
}
Example #2
0
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) );
}
// Scan throughout the entire document for possible completions,
// ignoring any dublets
const QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const
{
  KTextEditor::Document *doc = view->document();
  QString match_str = doc->text(range);
  QString s, m;
  QSet<QString> seen;
  QStringList l;

  int i( 0 );
  int pos( 0 );

  QRegExp re( "\\b(" + match_str + "\\w{1,})" );

  while( i < doc->lines() )
  {
      s = doc->line( i );
      pos = 0;
      while ( pos >= 0 )
      {
        pos = re.indexIn( s, pos );
        if ( pos >= 0 )
        {
          // typing in the middle of a word
          if ( ! ( i == range.start().line() && pos == range.start().column() ) )
          {
            m = re.cap( 1 );
            if ( ! seen.contains( m ) ) {
              seen.insert( m );
              l << m;
            }
          }
          pos += re.matchedLength();
        }
      }
    i++;
  }

  // Global completion
  // int db_area = KDebug::registerArea("ktuan-debug");
  QMap<QString, QStringList>::const_iterator ci = doc_word_list.constBegin();
  while (ci != doc_word_list.constEnd()) {
  if (ci.key() != doc->url().prettyUrl()) {
    QStringList list = ci.value();
    foreach (QString word, list) {
      // kDebug(db_area) << "complete word " << word;
      if (word.startsWith(match_str) && !seen.contains(word)) {
        // kDebug(db_area) << "Global completion";
        seen.insert(word);
        l << word;
      }
    }
  }
  ++ci;
  }
Example #4
0
// Scan throughout the entire document for possible completions,
// ignoring any dublets
const QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const
{
  QStringList l;

  int i( 0 );
  int pos( 0 );
  KTextEditor::Document *doc = view->document();
  QRegExp re( "\\b(" + doc->text( range ) + "\\w{1,})" );
  QString s, m;
  QSet<QString> seen;

  while( i < doc->lines() )
  {
    s = doc->line( i );
    pos = 0;
    while ( pos >= 0 )
    {
      pos = re.indexIn( s, pos );
      if ( pos >= 0 )
      {
        // typing in the middle of a word
        if ( ! ( i == range.start().line() && pos == range.start().column() ) )
        {
          m = re.cap( 1 );
          if ( ! seen.contains( m ) ) {
            seen.insert( m );
            l << m;
          }
        }
        pos += re.matchedLength();
      }
    }
    i++;
  }
  return l;
}
bool ParseJob::contentsAvailableFromEditor()
{
    KTextEditor::Document* doc = EditorIntegrator::documentForUrl(HashedString(d->document.str()));
    if (!doc)
        return false;

    finaliseChangedRanges();

    if (d->revisionToken == -1) {
        SmartInterface* iface = qobject_cast<SmartInterface*>(doc);
        if (iface) {
            QMutexLocker smartLock(iface->smartMutex());
            //Here we save the revision
            d->revisionToken = iface->currentRevision();
            iface->useRevision(d->revisionToken);

            // You must have called contentsAvailableFromEditor, it sets state

            d->contentsFromEditor = doc->text();
        }
    }

    return true;
}
Example #6
0
// Scan throughout the entire document for possible completions,
// ignoring any dublets
const QStringList KateNewCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const
{
  KTextEditor::Document *doc = view->document();
  QString match_str = doc->text(range);
  QString s, m;
  QSet<QString> seen;
  QStringList l;

  int i( 0 );
  int pos( 0 );

  QRegExp class_rek("([A-Z]\\w*<( |\\w|,|<|>)+>)");
  if (match_str.startsWith("new ")) {
    QString class_name = match_str.mid(4);
    for (i = 0; i < doc->lines(); ++i) {
      QString s = doc->line(i);
      QString m;
      pos = 0;
      while (pos >= 0) {
        pos = class_rek.indexIn(s, pos);
        if ( pos >= 0 )
        {
          // typing in the middle of a word
          if ( ! ( i == range.start().line() && pos >= range.start().column() && pos <= range.end().column()) )
          {
            m = class_rek.cap( 1 );
            if ( ! seen.contains( m ) && m.startsWith(class_name)) {
              seen.insert( m );
              m = "new " + m;
              l << m;
            }
          }
          pos += class_rek.matchedLength();
        }
      }
    }
  }

  // convert yieldXXX and Ent::load('XXX') to genXXX and getXXX
  if (match_str.startsWith("gen") || match_str.startsWith("get")) {
    QString x = match_str.mid(3);
    class_rek = QRegExp("(yield|Ent::load\\(\\\'|Ent::load\\(\\\")([A-Z]\\w*)");
    for (i = 0; i < doc->lines(); ++i) {
      QString s = doc->line(i);
      QString m;
      pos = 0;
      while (pos >= 0) {
        pos = class_rek.indexIn(s, pos);
        if ( pos >= 0 )
        {
          // typing in the middle of a word
          if ( ! ( i == range.start().line() && pos >= range.start().column() && pos <= range.end().column()) )
          {
            m = class_rek.cap( 2 );
            if ( ! seen.contains( m ) && m.startsWith(x)) {
              seen.insert( m );
              l << ("gen" + m);
              l << ("get" + m);
            }
          }
          pos += class_rek.matchedLength();
        }
      }
    }
  }

  return l;
}
Example #7
0
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 {
Example #8
0
// 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
}