Beispiel #1
0
    /**
     * @brief ComponentsMaker::makeField
     * @return
     */
    OptionalEntity ComponentsMaker::makeField(const Tokens &tokens)
    {
        Q_ASSERT(!tokens.isEmpty() && tokens[int(FieldGroupNames::Typename)]->isSingle() &&
                 !tokens[int(FieldGroupNames::Typename)]->token().isEmpty() &&
                 tokens[int(FieldGroupNames::Name)]->isSingle() &&
                 !tokens[int(FieldGroupNames::Name)]->token().isEmpty());

        Q_ASSERT(checkCommonState());

        // Make field with lhs keywords
        auto newField = std::make_shared<entity::Field>();
        newField->setName(tokens[int(FieldGroupNames::Name)]->token());
        if (!tokens[int(FieldGroupNames::LhsKeywords)]->token().isEmpty()) {
            auto keyword =
                utility::fieldKeywordFromString(tokens[int(FieldGroupNames::LhsKeywords)]->token());
            Q_ASSERT(keyword != entity::FieldKeyword::Invalid);
            newField->addKeyword(keyword);
        }

        // Make type
        Tokens typeTokens(int(TypeGroups::GroupsCount));
        std::copy(std::begin(tokens) + int(FieldGroupNames::ConstStatus),
                  std::begin(tokens) + int(FieldGroupNames::Name),        // do not include name
                  std::begin(typeTokens) + int(TypeGroups::ConstStatus)); // add first group offset
        auto optionalType = makeType(typeTokens);
        if (!optionalType.errorMessage.isEmpty())
            return {optionalType.errorMessage, nullptr};

        Q_ASSERT(optionalType.resultEntity);
        newField->setTypeId(optionalType.resultEntity->id());

        return {"", newField};
    }
Beispiel #2
0
static bool isInStringHelper(const QTextCursor &cursor, Token *retToken = 0)
{
    LanguageFeatures features;
    features.qtEnabled = false;
    features.qtKeywordsEnabled = false;
    features.qtMocRunEnabled = false;
    features.cxx11Enabled = true;
    features.c99Enabled = true;

    SimpleLexer tokenize;
    tokenize.setLanguageFeatures(features);

    const int prevState = BackwardsScanner::previousBlockState(cursor.block()) & 0xFF;
    const Tokens tokens = tokenize(cursor.block().text(), prevState);

    const unsigned pos = cursor.selectionEnd() - cursor.block().position();

    if (tokens.isEmpty() || pos <= tokens.first().utf16charsBegin())
        return false;

    if (pos >= tokens.last().utf16charsEnd()) {
        const Token tk = tokens.last();
        return tk.isStringLiteral() && prevState > 0;
    }

    Token tk = tokenAtPosition(tokens, pos);
    if (retToken)
        *retToken = tk;
    return tk.isStringLiteral() && pos > tk.utf16charsBegin();
}
Beispiel #3
0
    /**
     * @brief ComponentsMaker::makeMethod
     * @param tokens
     * @return
     */
    OptionalEntity ComponentsMaker::makeMethod(const Tokens &tokens)
    {
        Q_ASSERT(!tokens.isEmpty() &&
                 tokens[int(MethodsGroupsNames::ReturnType)]->isMulti() &&
                 tokens[int(MethodsGroupsNames::Name)]->isSingle());

        Q_ASSERT(checkCommonState());

        entity::SharedMethod newMethod = std::make_shared<entity::ClassMethod>();

        // Add Lhs
        auto lhsToken = tokens[int(MethodsGroupsNames::LhsKeywords)];
        Q_ASSERT(!lhsToken->isEmpty() && lhsToken->isSingle());
        for (auto &&w : lhsToken->token().split(QChar::Space, QString::SkipEmptyParts)) {
            entity::LhsIdentificator id = utility::methodLhsIdFromString(w);
            Q_ASSERT(id != entity::LhsIdentificator::None);
            newMethod->addLhsIdentificator(id);
        }


        // Add return type
        auto returnTypeToken = tokens[int(MethodsGroupsNames::ReturnType)];
        Q_ASSERT(returnTypeToken->isMulti() && !returnTypeToken->isEmpty());
        auto returnType = makeType(returnTypeToken->tokens());

        if (!returnType.errorMessage.isEmpty())
            return {returnType.errorMessage, nullptr};

        newMethod->setReturnTypeId(returnType.resultEntity->id());

        // Add name
        newMethod->setName(tokens[int(MethodsGroupsNames::Name)]->token());

        // Add arguments
        auto argumentsToken = tokens[int(MethodsGroupsNames::Arguments)];
        Q_ASSERT(argumentsToken->isEmpty() || argumentsToken->isMulti());
        if (argumentsToken->isMulti()) {
            auto argumentsTokens = argumentsToken->tokens();
            for (auto &&argumentToken : argumentsTokens) {
                Q_ASSERT(argumentToken->isMulti());

                auto argSubTokens = argumentToken->tokens();
                Q_ASSERT(argSubTokens.size() == int(Argument::GroupsCount));

                Q_ASSERT(argSubTokens[int(Argument::Name)]->isSingle());
                auto name = argSubTokens[int(Argument::Name)]->token();

                Q_ASSERT(argSubTokens[int(Argument::Type)]->isMulti());
                auto type = makeType(argSubTokens[int(Argument::Type)]->tokens());
                if (!type.errorMessage.isEmpty())
                    return {tr("Wrong type of argument: %1. Error: %2.").arg(
                                QString::number(argumentsTokens.indexOf(argumentToken)),
                                type.errorMessage
                            ), nullptr};

                Q_ASSERT(type.resultEntity);
                newMethod->addParameter(name, type.resultEntity->id());
            }
        }

        // Const
        auto constArgument = tokens[int(MethodsGroupsNames::Const)];
        Q_ASSERT(!constArgument->isEmpty() && constArgument->isSingle());

        const QString &token = constArgument->token();
        if (!token.isEmpty()) {
            if (token == "const")
                newMethod->setConstStatus(true);
            else
                return {tr("Wrong const status token: %1.").arg(constArgument->token()), nullptr};
        }

        // Rhs
        auto rhsArgument = tokens[int(MethodsGroupsNames::RhsKeywords)];
        Q_ASSERT(!rhsArgument->isEmpty() && rhsArgument->isSingle());

        const QString &rhsToken = rhsArgument->token();
        if (!rhsToken.isEmpty()) {
            entity::RhsIdentificator rhsId = utility::methodRhsIdFromString(rhsToken);
            if (rhsId != entity::RhsIdentificator::None)
                newMethod->setRhsIdentificator(rhsId);
            else
                return {tr("Wrong rhs keyword: %1").arg(rhsToken), nullptr};
        }

        return {"", newMethod};
    }
Beispiel #4
0
    OptionalEntity ComponentsMaker::makeProperty(const Tokens &tokens)
    {
        Q_ASSERT(!tokens.isEmpty() && tokens[int(PropGroupNames::Type)]->isSingle() &&
                 !tokens[int(PropGroupNames::Type)]->token().isEmpty() &&
                 tokens[int(PropGroupNames::Name)]->isSingle() &&
                 !tokens[int(PropGroupNames::Name)]->token().isEmpty());

        Q_ASSERT(checkCommonState());

        entity::SharedProperty newProperty = std::make_shared<entity::Property>();
        newProperty->setTypeSearcher(m_Model->globalDatabase());

        // Add name
        newProperty->setName(tokens[int(PropGroupNames::Name)]->token());

        // Add type
        // Not support namespaces for now, so check only in global database.
        // Work with namespaces and custom types in project will be hard due to Qt meta-stuff.
        // And also required more detail work on current code generation functionality. TODO: implement!
        const entity::SharedScope &globasScope = m_Model->globalDatabase()->scope(common::ID::globalScopeID());
        if (!globasScope)
            return {tr("Cannot find global scope."), nullptr};

        const auto &typeName = tokens[int(PropGroupNames::Type)]->token();
        auto type = globasScope->type(typeName);
        if (!type)
            return {tr("Wrong type: %1.").arg(typeName), nullptr};
        newProperty->setTypeId(type->id());

        // Member (supports only "m_" prefix for now)
        const auto &member = tokens[int(PropGroupNames::Member)]->token();
        if (!member.isEmpty()) {
            const bool hasPrefix = member.startsWith("m_");
            const auto name(hasPrefix ? QString(member).remove(0, 2) : member);
            const auto prefix(hasPrefix ? "m_" : "");
            newProperty->addField(name).field()->setPrefix(prefix);
            newProperty->setMember(true);
        }

        // Common methods
        addCommon(tokens, PropGroupNames::Getter,   &entity::Property::addGetter,   newProperty);
        addCommon(tokens, PropGroupNames::Setter,   &entity::Property::addSetter,   newProperty);
        addCommon(tokens, PropGroupNames::Resetter, &entity::Property::addResetter, newProperty);
        addCommon(tokens, PropGroupNames::Notifier, &entity::Property::addNotifier, newProperty);

        // Revision
        Q_ASSERT(tokens[int(PropGroupNames::Revision)]->isSingle());
        const auto &revision = tokens[int(PropGroupNames::Revision)]->token();
        if (!revision.isEmpty()) {
            bool ok = false;
            int rev = revision.toInt(&ok);

            if (ok)
                newProperty->setRevision(rev);
            else
                return {tr("Wrong revision: %1.").arg(revision), nullptr};
        }

        // Add designable and scriptable
        addDS(tokens, PropGroupNames::Designable, &entity::Property::addDesignableGetter,
              &entity::Property::setDesignable, newProperty);
        addDS(tokens, PropGroupNames::Scriptable, &entity::Property::addScriptableGetter,
              &entity::Property::setScriptable, newProperty);

        // Add stroed, user, const and final options
        addExtra(tokens, PropGroupNames::Stored,   &entity::Property::setStored,   newProperty);
        addExtra(tokens, PropGroupNames::User,     &entity::Property::setUser,     newProperty);
        addExtra(tokens, PropGroupNames::Constant, &entity::Property::setConstant, newProperty);
        addExtra(tokens, PropGroupNames::Final,    &entity::Property::setFinal,    newProperty);

        return {"", newProperty};
    }
Beispiel #5
0
    /**
     * @brief ComponentsMaker::makeType
     * @param tokens
     * @return
     */
    OptionalEntity ComponentsMaker::makeType(const Tokens &tokens)
    {
        Q_ASSERT(!tokens.isEmpty() && tokens[int(TypeGroups::Typename)]->isSingle() &&
                 !tokens[int(TypeGroups::Typename)]->token().isEmpty());

        // Check common type
        const QString &typeName = tokens[int(TypeGroups::Typename)]->token();
        entity::SharedType type;
        if (!tokens[int(TypeGroups::Namespaces)]->token().isEmpty()) {
            // TODO: should be already splitted i.e. is not single
            Q_ASSERT(tokens[int(TypeGroups::Namespaces)]->isSingle());
            auto names = tokens[int(TypeGroups::Namespaces)]->token()
                         .split("::", QString::SkipEmptyParts);
            auto scope = m_Model->globalDatabase()->chainScopeSearch(names);
            if (!scope)
                scope = m_Model->currentProject()->database()->chainScopeSearch(names);

            if (scope)
                type = scope->type(typeName);
        } else {
            // First of all check in all scopes of global database
            const entity::ScopesList &scopes = m_Model->globalDatabase()->scopes();
            range::find_if(scopes, [&](auto scope){ type = scope->type(typeName); return !!type; });

            // If not found, try to check project database
            if (!type) {
                auto db = m_Model->currentProject()->database();
                range::find_if(db->scopes(), [&](auto scope){ type = scope->type(typeName); return !!type; });
            }
        }

        if (!type)
            return {tr("Wrong type: %1.").arg(typeName), nullptr};

        // Check extra stuff
        entity::SharedExtendedType extType = std::make_shared<entity::ExtendedType>();
        extType->setTypeId(type->id());
        extType->setScopeId(m_Scope->id());
        Q_ASSERT(tokens[int(TypeGroups::ConstStatus)]->isSingle());
        extType->setConstStatus(!tokens[int(TypeGroups::ConstStatus)]->token().isEmpty());

        Q_ASSERT(tokens[int(TypeGroups::PLC)]->isSingle());
        if (!tokens[int(TypeGroups::PLC)]->token().isEmpty()) {
            QString plc = tokens[int(TypeGroups::PLC)]->token();
            plc.remove(QChar::Space);

            if (plc.startsWith("const")) {
                extType->setConstStatus(true);
                plc.remove(0, 4);
            }

            while (!plc.isEmpty()) {
                if (plc.startsWith("const")) {
                    plc.remove(0, 5);
                } else if (plc.startsWith("*const")) {
                    extType->addPointerStatus(true);
                    plc.remove(0, 6);
                } else if (plc.startsWith("*")) {
                    extType->addPointerStatus();
                    plc.remove(0, 1);
                } else if (plc.startsWith("&")) {
                    extType->addLinkStatus();
                    plc.remove(0, 1);
                } else {
                    break;
                }
            }
        }

        // TODO: should be already splitted too
        Q_ASSERT(tokens[int(TypeGroups::TemplateArgs)]->isSingle());
        if (!tokens[int(TypeGroups::TemplateArgs)]->token().isEmpty()) {
            QStringList arguments = tokens[int(TypeGroups::TemplateArgs)]->token()
                                    .remove(QChar::Space)
                                    .split(",", QString::SkipEmptyParts);
            entity::ScopesList scopes = m_Model->currentProject()->database()->scopes();
            for (auto && s : m_Model->globalDatabase()->scopes())
                scopes << s;

            // TODO: add namespaces, * and const
            for (auto &&name : arguments) {
                entity::SharedType t;
                range::find_if(scopes, [&](auto &&sc){ t = sc->type(name); return !!t; });
                if (t)
                    extType->addTemplateParameter(t->id());
                else
                    return {tr("Template parameter \"%1\" not found.").arg(name), nullptr};
            }
        }

        if (extType->isConst() || !extType->templateParameters().isEmpty() || !extType->pl().isEmpty()) {
            const entity::TypesList &types = m_Scope->types();
            auto it = range::find_if(types, [=](const entity::SharedType &type) {
                                                  return extType->isEqual(*type, false);
                                              });
            if (it == cend(types))
                m_Model->addExistsType(m_Model->currentProject()->name(), m_Scope->id(), extType);

            return {"", extType};
         } else {
            return {"", type};
         }
    }
Beispiel #6
0
void Editor::triggerAutoComplete()
{
    if( !d->autoCompleteEnabled ) return;

    // tokenize the expression (don't worry, this is very fast)
    // faster now that it uses flex. ;)
    int para = 0, curPos = 0;
    getCursorPosition( &para, &curPos );
    QString subtext = text().left( curPos );
    Tokens tokens = Evaluator::scan( subtext );
    if(!tokens.valid())
    {
        kdWarning() << "invalid tokens.\n";
        return;
    }

    if(tokens.isEmpty() || subtext.endsWith(" "))
        return;

    Token lastToken = tokens[ tokens.count()-1 ];

    // last token must be an identifier
    if( !lastToken.isIdentifier() )
        return;

    QString id = lastToken.text();
    if( id.isEmpty() )
        return;

    // find matches in function names
    QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
    QStringList choices;

    for( unsigned i=0; i<fnames.count(); i++ )
        if( fnames[i].startsWith( id, false ) )
        {
            QString str = fnames[i];

            ::Function* f = FunctionManager::instance()->function( str );
            if( f && !f->description.isEmpty() )
    str.append( ':' ).append( f->description );

            choices.append( str );
        }

    choices.sort();

    // find matches in variables names
    QStringList vchoices;
    QStringList values = NumeralModel::instance()->valueNames();

    for(QStringList::ConstIterator it = values.begin(); it != values.end(); ++it)
        if( (*it).startsWith( id, false ) )
        {
            QString choice = NumeralModel::description(*it);
            if(choice.isEmpty())
    choice = NumeralModel::instance()->value(*it).toString();

            vchoices.append( QString("%1:%2").arg( *it, choice ) );
        }

    vchoices.sort();
    choices += vchoices;

    // no match, don't bother with completion
    if( !choices.count() ) return;

    // one match, complete it for the user
    if( choices.count()==1 )
    {
        QString str = QStringList::split( ':', choices[0] )[0];

        // single perfect match, no need to give choices.
        if(str == id.lower())
            return;

        str = str.remove( 0, id.length() );
        int para = 0, curPos = 0;
        getCursorPosition( &para, &curPos );
        blockSignals( true );
        insert( str );
        setSelection( 0, curPos, 0, curPos+str.length() );
        blockSignals( false );
        return;
    }

    // present the user with completion choices
    d->completion->showCompletion( choices );
}
Beispiel #7
0
void CellEditor::Private::updateActiveSubRegion(const Tokens &tokens)
{
    // Index of the token, at which the text cursor is positioned.
    // For sub-regions it is the start range.
    currentToken = 0;

    if (tokens.isEmpty()) {
        selection->setActiveSubRegion(0, 0); // also set the active element
        return;
    }

    const int cursorPosition = textEdit->textCursor().position() - 1; // without '='
    kDebug() << "cursorPosition:" << cursorPosition << "textLength:" << textEdit->toPlainText().length() - 1;

    uint rangeCounter = 0; // counts the ranges in the sub-region
    uint currentRange = 0; // range index denoting the current range
    int regionStart = 0; // range index denoting the sub-region start
    uint regionEnd = 0; // range index denoting the sub-region end
    enum { Anywhere, InRegion, BeyondCursor } state = Anywhere;

    Token token;
    Token::Type type;
    // Search the current range the text cursor is positioned to.
    // Determine the subregion start and end, in which the range is located.
    for (int i = 0; i < tokens.count(); ++i) {
        token = tokens[i];
        type = token.type();

        // If not in a subregion, we may already quit the loop here.
        if (state == Anywhere) {
            // Already beyond the cursor position?
            if (token.pos() > cursorPosition) {
                state = BeyondCursor;
                break; // for loop
            }
        } else if (state == InRegion) {
            // Loop to the end of the subregion.
            if (type == Token::Cell || type == Token::Range) {
                regionEnd = rangeCounter++;
                continue; // keep going until the referenced region ends
            }
            if (type == Token::Operator) {
                if (tokens[i].asOperator() == Token::Semicolon) {
                    continue; // keep going until the referenced region ends
                }
            }
            state = Anywhere;
            continue;
        }

        // Can the token be replaced by a reference?
        switch (type) {
        case Token::Cell:
        case Token::Range:
            if (state == Anywhere) {
                currentToken = i;
                regionStart = rangeCounter;
                state = InRegion;
            }
            regionEnd = rangeCounter; // length = 1
            currentRange = ++rangeCounter; // point behind the last
            continue;
        case Token::Unknown:
        case Token::Boolean:
        case Token::Integer:
        case Token::Float:
        case Token::String:
        case Token::Error:
            // Set the active sub-region start to the next range but
            // with a length of 0, which results in inserting a new range
            // to the selection on calling Selection::initialize() or
            // Selection::update().
            currentToken = i;
            regionStart = rangeCounter; // position of the next range
            regionEnd = rangeCounter - 1; // length = 0
            currentRange = rangeCounter;
            continue;
        case Token::Operator:
        case Token::Identifier:
            continue;
        }
    }

    // Cursor not reached? I.e. the cursor is placed at the last token's end.
    if (state == Anywhere) {
        token = tokens.last();
        type = token.type();
        // Check the last token.
        // It was processed, but maybe a reference can be placed behind it.
        // Check, if the token can be replaced by a reference.
        switch (type) {
        case Token::Operator:
            // Possible to place a reference behind the operator?
            switch (token.asOperator()) {
            case Token::Plus:
            case Token::Minus:
            case Token::Asterisk:
            case Token::Slash:
            case Token::Caret:
            case Token::LeftPar:
            case Token::Semicolon:
            case Token::Equal:
            case Token::NotEqual:
            case Token::Less:
            case Token::Greater:
            case Token::LessEqual:
            case Token::GreaterEqual:
            case Token::Intersect:
            case Token::Union:
                // Append new references by pointing behind the last.
                currentToken = tokens.count();
                regionStart = rangeCounter;
                regionEnd = rangeCounter - 1; // length = 0
                currentRange = rangeCounter;
                break;
            case Token::InvalidOp:
            case Token::RightPar:
            case Token::Comma:
            case Token::Ampersand:
            case Token::Percent:
            case Token::CurlyBra:
            case Token::CurlyKet:
            case Token::Pipe:
                // reference cannot be placed behind
                break;
            }
            break;
        case Token::Unknown:
        case Token::Boolean:
        case Token::Integer:
        case Token::Float:
        case Token::String:
        case Token::Identifier:
        case Token::Error:
            // currentToken = tokens.count() - 1; // already set
            // Set the active sub-region start to the end of the selection
            // with a length of 0, which results in appending a new range
            // to the selection on calling Selection::initialize() or
            // Selection::update().
            regionStart = rangeCounter;
            regionEnd = rangeCounter - 1; // length = 0
            currentRange = rangeCounter;
            break;
        case Token::Cell:
        case Token::Range:
            // currentToken = tokens.count() - 1; // already set
            // Set the last range as active one. It is not a sub-region,
            // otherwise the state would have been InRegion.
            regionStart = rangeCounter - 1;
            regionEnd = rangeCounter - 1; // length = 1
            currentRange = rangeCounter; // point behind the last
            break;
        }
    }

    const int regionLength = regionEnd - regionStart + 1;
    kDebug() << "currentRange:" << currentRange << "regionStart:" << regionStart
    << "regionEnd:" << regionEnd << "regionLength:" << regionLength;

    selection->setActiveSubRegion(regionStart, regionLength, currentRange);
}