Scope *CanonicalSymbol::getScopeAndExpression(const QTextCursor &cursor, QString *code)
{
    if (!m_document)
        return 0;

    QTextCursor tc = cursor;
    int line, column;
    TextEditor::Convenience::convertPosition(cursor.document(), tc.position(), &line, &column);
    ++column; // 1-based line and 1-based column

    int pos = tc.position();
    QTextDocument *textDocument = cursor.document();
    if (!CppTools::isValidIdentifierChar(textDocument->characterAt(pos)))
        if (!(pos > 0 && CppTools::isValidIdentifierChar(textDocument->characterAt(pos - 1))))
            return 0;

    while (CppTools::isValidIdentifierChar(textDocument->characterAt(pos)))
        ++pos;
    tc.setPosition(pos);

    ExpressionUnderCursor expressionUnderCursor;
    *code = expressionUnderCursor(tc);
    return m_document->scopeAt(line, column);
}
void TestChangeTrackedDelete::testPartialListItemDelete()
{
    TextTool *textTool = new TextTool(new MockCanvas);
    KoTextEditor *textEditor = textTool->textEditor();
    QVERIFY(textEditor);
    QTextDocument *document = textEditor->document();
    KTextDocumentLayout *layout = qobject_cast<KTextDocumentLayout*>(document->documentLayout());
    QTextCursor *cursor = textEditor->cursor();
    insertSampleList(document);

    cursor->setPosition(46);
    cursor->setPosition(62, QTextCursor::KeepAnchor);
    ChangeTrackedDeleteCommand *delCommand = new ChangeTrackedDeleteCommand(ChangeTrackedDeleteCommand::NextChar, textTool);
    textEditor->addCommand(delCommand);
    QCOMPARE(document->characterAt(46).unicode(), (ushort)(QChar::ObjectReplacementCharacter));

    // This is wierd. Without this loop present the succeeding call to inlineTextObject returs NULL. Why ??????
    for (int i=0; i<document->characterCount(); i++) {
        cursor->setPosition(i);
    }

    cursor->setPosition(47);
    KDeleteChangeMarker *testMarker = dynamic_cast<KDeleteChangeMarker*>(layout->inlineTextObjectManager()->inlineTextObject(*cursor));
    QTextDocumentFragment deleteData =  KTextDocument(document).changeTracker()->elementById(testMarker->changeId())->deleteData();

    QTextDocument deleteDocument;
    QTextCursor deleteCursor(&deleteDocument);

    deleteCursor.insertFragment(deleteData);
    bool listFound = false;

    for (int i=0; i < deleteDocument.characterCount(); i++) {
        deleteCursor.setPosition(i);
        if (deleteCursor.currentList()) {
            listFound = true;
            continue;
        }
    }

    QVERIFY(listFound == true);
    QTextList *deletedList = deleteCursor.currentList();
    bool deletedListStatus = deletedList->format().boolProperty(KDeleteChangeMarker::DeletedList);
    QVERIFY (deletedListStatus == false);
    bool deletedListItemStatus;
    deletedListItemStatus  = deletedList->item(0).blockFormat().boolProperty(KDeleteChangeMarker::DeletedListItem);
    QVERIFY(deletedListItemStatus == false);
    delete textTool;
}
void Document::deleteTrailingSpaces()
{
    QTextCursor cursor (textDocument());
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::EndOfBlock);
    QTextDocument * doc = textDocument();

    while( !cursor.atEnd() ) {
        while( (cursor.block().length() > 1) && doc->characterAt(cursor.position() - 1).isSpace())
            cursor.deletePreviousChar();

        cursor.movePosition(QTextCursor::NextBlock);
        cursor.movePosition(QTextCursor::EndOfBlock);
    }
    cursor.endEditBlock();
}
Beispiel #4
0
int AutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
{
    if (!m_autoInsertBrackets)
        return 0;

    QTextDocument *doc = cursor.document();
    if (doc->characterAt(cursor.position() - 1) != QLatin1Char('{'))
        return 0;

    if (!contextAllowsAutoBrackets(cursor))
        return 0;

    // verify that we indeed do have an extra opening brace in the document
    QTextBlock block = cursor.block();
    const QString textFromCusror = block.text().mid(cursor.positionInBlock()).trimmed();
    int braceDepth = TextDocumentLayout::braceDepth(doc->lastBlock());

    if (braceDepth <= 0 && (textFromCusror.isEmpty() || textFromCusror.at(0) != QLatin1Char('}')))
        return 0; // braces are all balanced or worse, no need to do anything and separator inserted not between '{' and '}'

    // we have an extra brace , let's see if we should close it

    /* verify that the next block is not further intended compared to the current block.
       This covers the following case:

            if (condition) {|
                statement;
    */
    if (isNextBlockIndented(block))
        return 0;

    const QString &textToInsert = insertParagraphSeparator(cursor);
    int pos = cursor.position();
    cursor.insertBlock();
    cursor.insertText(textToInsert);
    cursor.setPosition(pos);

    // if we actually insert a separator, allow it to be overwritten if
    // user types it
    if (!textToInsert.isEmpty())
        m_allowSkippingOfBlockEnd = true;

    return 1;
}
Beispiel #5
0
unsigned QMLRewriter::calculateIndentDepth(const SourceLocation &position) const
{
    QTextDocument *doc = m_textModifier->textDocument();
    QTextCursor tc(doc);
    tc.setPosition(position.offset);
    const int lineOffset = tc.block().position();
    unsigned indentDepth = 0;

    forever {
        const QChar ch = doc->characterAt(lineOffset + indentDepth);

        if (ch.isNull() || !ch.isSpace())
            break;
        else
            ++indentDepth;
    }

    return indentDepth;
}
Beispiel #6
0
// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void QMLRewriter::includeLeadingEmptyLine(int &start) const
{
    QTextDocument *doc = textModifier()->textDocument();

    if (start == 0)
        return;

    if (doc->characterAt(start - 1) != QChar::ParagraphSeparator)
        return;

    QTextCursor tc(doc);
    tc.setPosition(start);
    const int blockNr = tc.blockNumber();
    if (blockNr == 0)
        return;

    const QTextBlock prevBlock = tc.block().previous();
    const QString trimmedPrevBlockText = prevBlock.text().trimmed();
    if (!trimmedPrevBlockText.isEmpty())
        return;

    start = prevBlock.position();
}
void TestChangeTrackedDelete::testDeleteSelection()
{
    TextTool *textTool = new TextTool(new MockCanvas);
    KoTextEditor *textEditor = textTool->textEditor();
    QVERIFY(textEditor);
    QTextDocument *document = textEditor->document();
    KTextDocumentLayout *layout = qobject_cast<KTextDocumentLayout*>(document->documentLayout());
    QTextCursor *cursor = textEditor->cursor();
    cursor->insertText("Hello World");
    cursor->setPosition(2);
    cursor->setPosition(8, QTextCursor::KeepAnchor);
    ChangeTrackedDeleteCommand *delCommand = new ChangeTrackedDeleteCommand(ChangeTrackedDeleteCommand::NextChar, textTool);
    textEditor->addCommand(delCommand);
    QCOMPARE(document->characterAt(2).unicode(), (ushort)(QChar::ObjectReplacementCharacter));
    // This is wierd. Without this loop present the succeeding call to inlineTextObject returs NULL. Why ??????
    for (int i=0; i<document->characterCount(); i++) {
        cursor->setPosition(i);
    }
    cursor->setPosition(3);
    KDeleteChangeMarker *testMarker = dynamic_cast<KDeleteChangeMarker*>(layout->inlineTextObjectManager()->inlineTextObject(*cursor));
    QTextDocumentFragment deleteData =  KTextDocument(document).changeTracker()->elementById(testMarker->changeId())->deleteData();
    QCOMPARE(deleteData.toPlainText(), QString("llo Wo"));
    delete textTool;
}
QString AutoCompleter::autoComplete(QTextCursor &cursor, const QString &textToInsert) const
{
    const bool checkBlockEnd = m_allowSkippingOfBlockEnd;
    m_allowSkippingOfBlockEnd = false; // consume blockEnd.

    if (m_surroundWithEnabled && cursor.hasSelection()) {
        if (textToInsert == QLatin1String("("))
            return cursor.selectedText() + QLatin1Char(')');
        if (textToInsert == QLatin1String("{")) {
            //If the text span multiple lines, insert on different lines
            QString str = cursor.selectedText();
            if (str.contains(QChar::ParagraphSeparator)) {
                //Also, try to simulate auto-indent
                str = (str.startsWith(QChar::ParagraphSeparator) ? QString() : QString(QChar::ParagraphSeparator)) +
                      str;
                if (str.endsWith(QChar::ParagraphSeparator))
                    str += QLatin1Char('}') + QString(QChar::ParagraphSeparator);
                else
                    str += QString(QChar::ParagraphSeparator) + QLatin1Char('}');
            } else {
                str += QLatin1Char('}');
            }
            return str;
        }
        if (textToInsert == QLatin1String("["))
            return cursor.selectedText() + QLatin1Char(']');
        if (textToInsert == QLatin1String("\""))
            return cursor.selectedText() + QLatin1Char('"');
        if (textToInsert == QLatin1String("'"))
            return cursor.selectedText() + QLatin1Char('\'');
    }

    if (!m_autoParenthesesEnabled)
        return QString();

    if (!contextAllowsAutoParentheses(cursor, textToInsert))
        return QString();

    QTextDocument *doc = cursor.document();
    const QString text = textToInsert;
    const QChar lookAhead = doc->characterAt(cursor.selectionEnd());

    const QChar character = textToInsert.at(0);
    const QString parentheses = QLatin1String("()");
    const QString brackets = QLatin1String("[]");
    if (parentheses.contains(character) || brackets.contains(character)) {
        QTextCursor tmp= cursor;
        bool foundBlockStart = TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
        int blockStart = foundBlockStart ? tmp.position() : 0;
        tmp = cursor;
        bool foundBlockEnd = TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
        int blockEnd = foundBlockEnd ? tmp.position() : (cursor.document()->characterCount() - 1);
        const QChar openChar = parentheses.contains(character) ? QLatin1Char('(') : QLatin1Char('[');
        const QChar closeChar = parentheses.contains(character) ? QLatin1Char(')') : QLatin1Char(']');

        int errors = 0;
        int stillopen = 0;
        countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
        int errorsBeforeInsertion = errors + stillopen;
        errors = 0;
        stillopen = 0;
        countBrackets(cursor, blockStart, cursor.position(), openChar, closeChar, &errors, &stillopen);
        countBracket(openChar, closeChar, character, &errors, &stillopen);
        countBrackets(cursor, cursor.position(), blockEnd, openChar, closeChar, &errors, &stillopen);
        int errorsAfterInsertion = errors + stillopen;
        if (errorsAfterInsertion < errorsBeforeInsertion)
            return QString(); // insertion fixes parentheses or bracket errors, do not auto complete
    }

    int skippedChars = 0;
    const QString autoText = insertMatchingBrace(cursor, text, lookAhead, &skippedChars);

    if (checkBlockEnd && textToInsert.at(0) == QLatin1Char('}')) {
        if (textToInsert.length() > 1)
            qWarning() << "*** handle event compression";

        int startPos = cursor.selectionEnd(), pos = startPos;
        while (doc->characterAt(pos).isSpace())
            ++pos;

        if (doc->characterAt(pos) == QLatin1Char('}'))
            skippedChars += (pos - startPos) + 1;
    }

    if (skippedChars) {
        const int pos = cursor.position();
        cursor.setPosition(pos + skippedChars);
        cursor.setPosition(pos, QTextCursor::KeepAnchor);
    }

    return autoText;
}
bool CppDocumentationCommentHelper::handleKeyPressEvent(QKeyEvent *e) const
{
    if (!m_settings.m_enableDoxygen && !m_settings.m_leadingAsterisks)
        return false;

    if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        QTextCursor cursor = m_editorWidget->textCursor();
        if (!m_editorWidget->autoCompleter()->isInComment(cursor))
            return false;

        // We are interested on two particular cases:
        //   1) The cursor is right after a /**, /*!, /// or ///! and the user pressed enter.
        //      If Doxygen is enabled we need to generate an entire comment block.
        //   2) The cursor is already in the middle of a multi-line comment and the user pressed
        //      enter. If leading asterisk(s) is set we need to write a comment continuation
        //      with those.

        if (m_settings.m_enableDoxygen && cursor.positionInBlock() >= 3) {
            const int pos = cursor.position();
            if (isStartOfDoxygenComment(cursor)) {
                QTextDocument *textDocument = m_editorWidget->document();
                DoxygenGenerator::DocumentationStyle style = doxygenStyle(cursor, textDocument);

                // Check if we're already in a CppStyle Doxygen comment => continuation
                // Needs special handling since CppStyle does not have start and end markers
                if ((style == DoxygenGenerator::CppStyleA || style == DoxygenGenerator::CppStyleB)
                        && isCppStyleContinuation(cursor)) {
                    return handleDoxygenCppStyleContinuation(cursor, e);
                }

                DoxygenGenerator doxygen;
                doxygen.setStyle(style);
                doxygen.setAddLeadingAsterisks(m_settings.m_leadingAsterisks);
                doxygen.setGenerateBrief(m_settings.m_generateBrief);
                doxygen.setStartComment(false);

                // Move until we reach any possibly meaningful content.
                while (textDocument->characterAt(cursor.position()).isSpace()
                       && cursor.movePosition(QTextCursor::NextCharacter)) {
                }

                if (!cursor.atEnd()) {
                    const QString &comment = doxygen.generate(cursor);
                    if (!comment.isEmpty()) {
                        cursor.beginEditBlock();
                        cursor.setPosition(pos);
                        cursor.insertText(comment);
                        cursor.setPosition(pos - 3, QTextCursor::KeepAnchor);
                        m_editorWidget->textDocument()->autoIndent(cursor);
                        cursor.endEditBlock();
                        e->accept();
                        return true;
                    }
                }

            }
        } // right after first doxygen comment

        return handleDoxygenContinuation(cursor,
                                         e,
                                         m_editorWidget->document(),
                                         m_settings.m_enableDoxygen,
                                         m_settings.m_leadingAsterisks);
    }

    return false;
}
bool ScCodeEditor::insertMatchingTokens( const QString & text )
{
    if (text.isEmpty())
        return false;

    QTextCursor cursor = textCursor();
    QTextDocument *document = cursor.document();
    int cursorPosition = cursor.position();

    QChar token = text[0];
    QChar matchingToken;

    static QString openingTokens("([{'\"");
    static QString closingTokens(")]}'\"");

    bool isOpeningToken, isClosingToken;

    int idx;
    if ( (idx = openingTokens.indexOf(token)) != -1 ) {
        matchingToken = closingTokens[idx];
        isOpeningToken = true;
        isClosingToken = token == matchingToken;
    }
    else if ( (idx = closingTokens.indexOf(token)) != -1 ) {
        matchingToken = openingTokens[idx];
        isClosingToken = true;
        isOpeningToken = token == matchingToken;
    }
    else
        return false;

    cursor.beginEditBlock();

    if (mInsertMatchingTokens)
    {
        if (cursor.hasSelection()) {
            if (isOpeningToken) {
                int start = cursor.selectionStart();
                int end = cursor.selectionEnd();
                cursor.setPosition(start);
                cursor.insertText(token);
                cursor.setPosition(end + 1);
                cursor.insertText(matchingToken);
            }
            else
                cursor.insertText(token);
        }
        else {
            if (isClosingToken && document->characterAt(cursorPosition) == token) {
                cursor.movePosition(QTextCursor::NextCharacter);
            }
            else if (isOpeningToken) {
                cursor.insertText(token);
                cursor.insertText(matchingToken);
                cursor.movePosition(QTextCursor::PreviousCharacter);
            }
            else
                cursor.insertText(token);
        }
    }
    else
        cursor.insertText(token);

    cursor.endEditBlock();

    cursor.setVerticalMovementX(-1);
    setTextCursor(cursor);

    if (isClosingToken)
        indent(cursor, JoinEditBlock);

    ensureCursorVisible();

    return true;
}
Beispiel #11
0
void TestChangeTrackedDelete::testTableDelete()
{
    TextTool *textTool = new TextTool(new MockCanvas);
    KoTextEditor *textEditor = textTool->textEditor();
    QVERIFY(textEditor);
    QTextDocument *document = textEditor->document();
    KTextDocumentLayout *layout = qobject_cast<KTextDocumentLayout*>(document->documentLayout());
    QTextCursor *cursor = textEditor->cursor();
    insertSampleTable(document);

    cursor->setPosition(13);
    cursor->setPosition(102, QTextCursor::KeepAnchor);
    ChangeTrackedDeleteCommand *delCommand = new ChangeTrackedDeleteCommand(ChangeTrackedDeleteCommand::NextChar, textTool);
    textEditor->addCommand(delCommand);
    QCOMPARE(document->characterAt(13).unicode(), (ushort)(QChar::ObjectReplacementCharacter));

    // This is wierd. Without this loop present the succeeding call to inlineTextObject returs NULL. Why ??????
    for (int i=0; i<document->characterCount(); i++) {
        cursor->setPosition(i);
    }

    cursor->setPosition(14);
    KDeleteChangeMarker *testMarker = dynamic_cast<KDeleteChangeMarker*>(layout->inlineTextObjectManager()->inlineTextObject(*cursor));
    QTextDocumentFragment deleteData =  KTextDocument(document).changeTracker()->elementById(testMarker->changeId())->deleteData();

    QTextDocument deleteDocument;
    QTextCursor deleteCursor(&deleteDocument);

    deleteCursor.insertFragment(deleteData);
    bool tableFound = false;

    for (int i=0; i < deleteDocument.characterCount(); i++) {
        deleteCursor.setPosition(i);
        if (deleteCursor.currentTable()) {
            tableFound = true;
            break;
        }
    }
    QVERIFY(tableFound == true);
    QTextTable *table = deleteCursor.currentTable();
    QVERIFY(table->rows() == 3);
    QVERIFY(table->columns() == 3);

    tableFound = false;
    for (int i=0; i < document->characterCount(); i++) {
        deleteCursor.setPosition(i);
        if (deleteCursor.currentTable()) {
            tableFound = true;
            break;
        }
    }
    QVERIFY(tableFound == false);

    delCommand->undo();
    tableFound = false;
    for (int i=0; i < document->characterCount(); i++) {
        deleteCursor.setPosition(i);
        if (deleteCursor.currentTable()) {
            tableFound = true;
            break;
        }
    }
    QVERIFY(tableFound == true);
    delete textTool;
}
Beispiel #12
0
void TextEditor::validateAndHighlightBrackets(const QChar &openStr, const QChar &closeStr)
{
    QTextDocument * doc = document();

    if(!brackets.contains(openStr)) {
        brackets[openStr] = QTextCursor();
    }
    if(!brackets.contains(closeStr)) {
        brackets[closeStr] = QTextCursor();
    }

    QTextCursor bracketBeginCursor = brackets[openStr];
    QTextCursor bracketEndCursor = brackets[closeStr];

    if (!bracketBeginCursor.isNull() || !bracketEndCursor.isNull()) {
        bracketBeginCursor.setCharFormat(QTextCharFormat());
        bracketEndCursor.setCharFormat(QTextCharFormat());
        bracketBeginCursor = bracketEndCursor = QTextCursor();
    }

    QTextCursor cursor = textCursor();

    int position = cursor.position();

    bool forward;
    QChar begin, end;

    if (doc->characterAt(position) == openStr || doc->characterAt(position - 1) == openStr ||
        doc->characterAt(position - 1) == closeStr || doc->characterAt(position) == closeStr) {

        forward = doc->characterAt(position) == openStr || doc->characterAt(position - 1) == openStr;

        if (forward) {
            if(doc->characterAt(position) == openStr) {
                position++;
            } else {
                cursor.setPosition(position - 1);
            }
            begin = openStr;
            end = closeStr;
        } else {
            if(doc->characterAt(position) == closeStr) {
                cursor.setPosition(position + 1);
                position -= 1;
            } else {
                position -= 2;
            }
            begin = closeStr;
            end = openStr;
        }

        bracketBeginCursor = QTextCursor(cursor);
        bracketBeginCursor.movePosition(forward ? QTextCursor::NextCharacter : QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);

        QTextCharFormat format = bracketMismatchFormat;

        int braceDepth = 1;

        QChar c;
        while (!(c = doc->characterAt(position)).isNull()) {
            if (c == begin) {
                braceDepth++;
            } else if (c == end) {
                braceDepth--;

                if (!braceDepth) {
                    bracketEndCursor = QTextCursor(doc);
                    bracketEndCursor.setPosition(position);
                    bracketEndCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
                    bracketEndCursor.setCharFormat(bracketMatchFormat);

                    format = bracketMatchFormat;

                    break;
                }
            }
            forward ? position++ : position--;
        }
        bracketBeginCursor.setCharFormat(format);
    }
    brackets[openStr] = bracketBeginCursor;
    brackets[closeStr] = bracketEndCursor;
}
Beispiel #13
0
void TextEditor::smartText(QKeyEvent *e)
{
    bool parentEvent = true;
    QTextDocument * doc = document();
    if(e->key() == Qt::Key_Tab) {
        e->accept();
        insertSmartTab();
        return;
    }

    // Smart indent
    if (e->key() == Qt::Key_Return) {
        e->accept();
        autoIndentNewLine();
        return;
    }

    QTextCursor cursor = textCursor();
    int position = cursor.position();

    if(cursor.selectedText() != "") {
        position = cursor.selectionStart();
    }

    if(e->key() == Qt::Key_ParenRight && doc->characterAt(position) == ')') {
        cursor.setPosition(position + 1);
        setTextCursor(cursor);
        parentEvent = false;
    }

    if (e->key() == Qt::Key_ParenLeft) {
        cursor.insertText(")");
        cursor.setPosition(position);
        setTextCursor(cursor);
    }

    if(e->key() == Qt::Key_BracketRight && doc->characterAt(position) == ']') {
        cursor.setPosition(position + 1);
        setTextCursor(cursor);
        parentEvent = false;
    }

    if (e->key() == Qt::Key_BracketLeft) {
        cursor.insertText("]");
        cursor.setPosition(position);
        setTextCursor(cursor);
    }

    if(e->key() == Qt::Key_BraceRight && doc->characterAt(position) == '}') {
        cursor.setPosition(position + 1);
        setTextCursor(cursor);
        parentEvent = false;
    }

    if(e->key() == Qt::Key_BraceLeft) {
        cursor.insertText("}");
        cursor.setPosition(position);
        setTextCursor(cursor);
    }

    if(e->key() == Qt::Key_QuoteDbl && doc->characterAt(position) == '"') {
        cursor.setPosition(position + 1);
        setTextCursor(cursor);
        parentEvent = false;
    } else if (e->key() == Qt::Key_QuoteDbl) {
        cursor.insertText("\"");
        cursor.setPosition(position);
        setTextCursor(cursor);
    }

    if(e->key() == Qt::Key_Apostrophe && doc->characterAt(position) == '\'') {
        cursor.setPosition(position + 1);
        setTextCursor(cursor);
        parentEvent = false;
    } else if (e->key() == Qt::Key_Apostrophe) {
        cursor.insertText("'");
        cursor.setPosition(position);
        setTextCursor(cursor);
    }

    if(parentEvent) {
        QPlainTextEdit::keyPressEvent(e);
    }
}
QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, const QTextCursor &end,
                                                    const QRect &clip)
{
    if (begin.isNull() || end.isNull() || begin.position() > end.position())
        return QPainterPath();

    QPointF offset = m_editor->contentOffset();
    QRect viewportRect = rect();
    QTextDocument *document = m_editor->document();

    if (m_editor->blockBoundingGeometry(begin.block()).translated(offset).top() > clip.bottom() + 10
        || m_editor->blockBoundingGeometry(end.block()).translated(offset).bottom() < clip.top() - 10
        )
        return QPainterPath(); // nothing of the selection is visible


    QTextBlock block = begin.block();

    if (block.blockNumber() < m_editor->firstVisibleBlock().blockNumber() - 4)
        block = m_editor->document()->findBlockByNumber(m_editor->firstVisibleBlock().blockNumber() - 4);

    bool inSelection = false;

    QVector<QRectF> selection;

    if (begin.position() == end.position()) {
        // special case empty selections
        const QRectF blockGeometry = m_editor->blockBoundingGeometry(block);
        QTextLayout *blockLayout = block.layout();
        int pos = begin.position() - begin.block().position();
        QTextLine line = blockLayout->lineForTextPosition(pos);
        QRectF lineRect = line.naturalTextRect();
        int x = line.cursorToX(pos);
        lineRect.setLeft(x - m_borderWidth);
        lineRect.setRight(x + m_borderWidth);
        selection += lineRect.translated(blockGeometry.topLeft());
    } else {
        for (; block.isValid() && block.blockNumber() <= end.blockNumber(); block = block.next()) {
            if (! block.isVisible())
                continue;

            const QRectF blockGeometry = m_editor->blockBoundingGeometry(block);
            QTextLayout *blockLayout = block.layout();

            QTextLine line = blockLayout->lineAt(0);
            bool firstOrLastBlock = false;

            int beginChar = 0;
            if (!inSelection) {
                if (block == begin.block()) {
                    beginChar = begin.positionInBlock();
                    line = blockLayout->lineForTextPosition(beginChar);
                    firstOrLastBlock = true;
                }
                inSelection = true;
            } else {
//                while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace())
//                    ++beginChar;
//                if (beginChar == block.length())
//                    beginChar = 0;
            }

            int lastLine = blockLayout->lineCount()-1;
            int endChar = -1;
            if (block == end.block()) {
                endChar = end.positionInBlock();
                lastLine = blockLayout->lineForTextPosition(endChar).lineNumber();
                inSelection = false;
                firstOrLastBlock = true;
            } else {
                endChar = block.length();
                while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace())
                    --endChar;
            }

            QRectF lineRect = line.naturalTextRect();
            if (beginChar < endChar) {
                lineRect.setLeft(line.cursorToX(beginChar));
                if (line.lineNumber() == lastLine)
                    lineRect.setRight(line.cursorToX(endChar));
                selection += lineRect.translated(blockGeometry.topLeft());

                for (int lineIndex = line.lineNumber()+1; lineIndex <= lastLine; ++lineIndex) {
                    line = blockLayout->lineAt(lineIndex);
                    lineRect = line.naturalTextRect();
                    if (lineIndex == lastLine)
                        lineRect.setRight(line.cursorToX(endChar));
                    selection += lineRect.translated(blockGeometry.topLeft());
                }
            } else { // empty lines
                const int emptyLineSelectionSize = 16;
                if (!firstOrLastBlock && !selection.isEmpty()) { // middle
                    lineRect.setLeft(selection.last().left());
                } else if (inSelection) { // first line
                    lineRect.setLeft(line.cursorToX(beginChar));
                } else { // last line
                    if (endChar == 0)
                        break;
                    lineRect.setLeft(line.cursorToX(endChar) - emptyLineSelectionSize);
                }
                lineRect.setRight(lineRect.left() + emptyLineSelectionSize);
                selection += lineRect.translated(blockGeometry.topLeft());
            }

            if (!inSelection)
                break;

            if (blockGeometry.translated(offset).y() > 2*viewportRect.height())
                break;
        }
    }


    if (selection.isEmpty())
        return QPainterPath();

    QVector<QPointF> points;

    const int margin = m_borderWidth/2;
    const int extra = 0;

    const QRectF &firstSelection = selection.at(0);
    points += (firstSelection.topLeft() + firstSelection.topRight()) / 2 + QPointF(0, -margin);
    points += firstSelection.topRight() + QPointF(margin+1, -margin);
    points += firstSelection.bottomRight() + QPointF(margin+1, 0);

    const int count = selection.count();
    for (int i = 1; i < count-1; ++i) {
#define MAX3(a,b,c) qMax(a, qMax(b,c))
        qreal x = MAX3(selection.at(i-1).right(),
                       selection.at(i).right(),
                       selection.at(i+1).right()) + margin;

        points += QPointF(x+1, selection.at(i).top());
        points += QPointF(x+1, selection.at(i).bottom());
    }

    const QRectF &lastSelection = selection.at(count-1);
    points += lastSelection.topRight() + QPointF(margin+1, 0);
    points += lastSelection.bottomRight() + QPointF(margin+1, margin+extra);
    points += lastSelection.bottomLeft() + QPointF(-margin, margin+extra);
    points += lastSelection.topLeft() + QPointF(-margin, 0);

    for (int i = count-2; i > 0; --i) {
#define MIN3(a,b,c) qMin(a, qMin(b,c))
        qreal x = MIN3(selection.at(i-1).left(),
                       selection.at(i).left(),
                       selection.at(i+1).left()) - margin;

        points += QPointF(x, selection.at(i).bottom()+extra);
        points += QPointF(x, selection.at(i).top());
    }

    points += firstSelection.bottomLeft() + QPointF(-margin, extra);
    points += firstSelection.topLeft() + QPointF(-margin, -margin);


    QPainterPath path;
    const int corner = 4;
    path.moveTo(points.at(0));
    points += points.at(0);
    QPointF previous = points.at(0);
    for (int i = 1; i < points.size(); ++i) {
        QPointF point = points.at(i);
        if (point.y() == previous.y() && qAbs(point.x() - previous.x()) > 2*corner) {
            QPointF tmp = QPointF(previous.x() + corner * ((point.x() > previous.x())?1:-1), previous.y());
            path.quadTo(previous, tmp);
            previous = tmp;
            i--;
            continue;
        } else if (point.x() == previous.x() && qAbs(point.y() - previous.y()) > 2*corner) {
            QPointF tmp = QPointF(previous.x(), previous.y() + corner * ((point.y() > previous.y())?1:-1));
            path.quadTo(previous, tmp);
            previous = tmp;
            i--;
            continue;
        }


        QPointF target = (previous + point) / 2;
        path.quadTo(previous, target);
        previous = points.at(i);
    }
    path.closeSubpath();
    path.translate(offset);
    return path;
}
bool trySplitComment(TextEditor::TextEditorWidget *editorWidget,
                     const CPlusPlus::Snapshot &snapshot)
{
    const CommentsSettings &settings = CppToolsSettings::instance()->commentsSettings();
    if (!settings.m_enableDoxygen && !settings.m_leadingAsterisks)
        return false;

    QTextCursor cursor = editorWidget->textCursor();
    if (!CPlusPlus::MatchingText::isInCommentHelper(cursor))
        return false;

    // We are interested on two particular cases:
    //   1) The cursor is right after a /**, /*!, /// or ///! and the user pressed enter.
    //      If Doxygen is enabled we need to generate an entire comment block.
    //   2) The cursor is already in the middle of a multi-line comment and the user pressed
    //      enter. If leading asterisk(s) is set we need to write a comment continuation
    //      with those.

    if (settings.m_enableDoxygen && cursor.positionInBlock() >= 3) {
        const int pos = cursor.position();
        if (isStartOfDoxygenComment(cursor)) {
            QTextDocument *textDocument = editorWidget->document();
            DoxygenGenerator::DocumentationStyle style = doxygenStyle(cursor, textDocument);

            // Check if we're already in a CppStyle Doxygen comment => continuation
            // Needs special handling since CppStyle does not have start and end markers
            if ((style == DoxygenGenerator::CppStyleA || style == DoxygenGenerator::CppStyleB)
                    && isCppStyleContinuation(cursor)) {
                return handleDoxygenCppStyleContinuation(cursor);
            }

            DoxygenGenerator doxygen;
            doxygen.setStyle(style);
            doxygen.setAddLeadingAsterisks(settings.m_leadingAsterisks);
            doxygen.setGenerateBrief(settings.m_generateBrief);
            doxygen.setStartComment(false);

            // Move until we reach any possibly meaningful content.
            while (textDocument->characterAt(cursor.position()).isSpace()
                   && cursor.movePosition(QTextCursor::NextCharacter)) {
            }

            if (!cursor.atEnd()) {
                const QString &comment = doxygen.generate(cursor,
                                                          snapshot,
                                                          editorWidget->textDocument()->filePath());
                if (!comment.isEmpty()) {
                    cursor.beginEditBlock();
                    cursor.setPosition(pos);
                    cursor.insertText(comment);
                    cursor.setPosition(pos - 3, QTextCursor::KeepAnchor);
                    editorWidget->textDocument()->autoIndent(cursor);
                    cursor.endEditBlock();
                    return true;
                }
            }
        }
    } // right after first doxygen comment

    return handleDoxygenContinuation(cursor,
                                     editorWidget,
                                     settings.m_enableDoxygen,
                                     settings.m_leadingAsterisks);
}
Beispiel #16
0
QString MatchingText::insertMatchingBrace(const QTextCursor &cursor, const QString &textToProcess,
                                          QChar la, int *skippedChars) const
{
    QTextCursor tc = cursor;
    QTextDocument *doc = tc.document();
    QString text = textToProcess;

    const QString blockText = tc.block().text().mid(tc.positionInBlock());
    const int length = qMin(blockText.length(), textToProcess.length());

    const QChar previousChar = doc->characterAt(tc.selectionEnd() - 1);

    bool escape = false;

    if (! text.isEmpty() && (text.at(0) == QLatin1Char('"') ||
                             text.at(0) == QLatin1Char('\''))) {
        if (previousChar == QLatin1Char('\\')) {
            int escapeCount = 0;
            int index = tc.selectionEnd() - 1;
            do {
                ++escapeCount;
                --index;
            } while (doc->characterAt(index) == QLatin1Char('\\'));

            if ((escapeCount % 2) != 0)
                escape = true;
        }
    }

    if (! escape) {
        for (int i = 0; i < length; ++i) {
            const QChar ch1 = blockText.at(i);
            const QChar ch2 = textToProcess.at(i);

            if (ch1 != ch2)
                break;
            else if (! shouldOverrideChar(ch1))
                break;

            ++*skippedChars;
        }
    }

    if (*skippedChars != 0) {
        tc.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, *skippedChars);
        text = textToProcess.mid(*skippedChars);
    }

    if (text.isEmpty() || !shouldInsertMatchingText(la))
        return QString();

    BackwardsScanner tk(tc, MAX_NUM_LINES, textToProcess.left(*skippedChars));
    const int startToken = tk.startToken();
    int index = startToken;

    const Token &token = tk[index - 1];

    if (text.at(0) == QLatin1Char('"') && (token.is(T_STRING_LITERAL) || token.is(T_WIDE_STRING_LITERAL))) {
        if (text.length() != 1)
            qWarning() << Q_FUNC_INFO << "handle event compression";

        if (isCompleteStringLiteral(tk, index - 1))
            return QLatin1String("\"");

        return QString();
    } else if (text.at(0) == QLatin1Char('\'') && (token.is(T_CHAR_LITERAL) || token.is(T_WIDE_CHAR_LITERAL))) {
        if (text.length() != 1)
            qWarning() << Q_FUNC_INFO << "handle event compression";

        if (isCompleteCharLiteral(tk, index - 1))
            return QLatin1String("'");

        return QString();
    }

    QString result;

    foreach (const QChar &ch, text) {
        if      (ch == QLatin1Char('('))  result += ')';
        else if (ch == QLatin1Char('['))  result += ']';
        else if (ch == QLatin1Char('"'))  result += '"';
        else if (ch == QLatin1Char('\'')) result += '\'';
    }

    return result;
}
Beispiel #17
0
bool MatchingText::shouldInsertMatchingText(const QTextCursor &tc)
{
    QTextDocument *doc = tc.document();
    return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd()));
}
Beispiel #18
0
bool AutoCompleter::autoBackspace(QTextCursor &cursor)
{
    m_allowSkippingOfBlockEnd = false;

    if (!m_autoInsertBrackets)
        return false;

    int pos = cursor.position();
    if (pos == 0)
        return false;
    QTextCursor c = cursor;
    c.setPosition(pos - 1);

    QTextDocument *doc = cursor.document();
    const QChar lookAhead = doc->characterAt(pos);
    const QChar lookBehind = doc->characterAt(pos - 1);
    const QChar lookFurtherBehind = doc->characterAt(pos - 2);

    const QChar character = lookBehind;
    if (character == QLatin1Char('(')
            || character == QLatin1Char('[')
            || character == QLatin1Char('{')) {
        QTextCursor tmp = cursor;
        TextBlockUserData::findPreviousBlockOpenParenthesis(&tmp);
        int blockStart = tmp.isNull() ? 0 : tmp.position();
        tmp = cursor;
        TextBlockUserData::findNextBlockClosingParenthesis(&tmp);
        int blockEnd = tmp.isNull() ? (cursor.document()->characterCount()-1) : tmp.position();
        QChar openChar = character;
        QChar closeChar = charType(character, CharType::CloseChar);

        int errors = 0;
        int stillopen = 0;
        countBrackets(cursor, blockStart, blockEnd, openChar, closeChar, &errors, &stillopen);
        int errorsBeforeDeletion = errors + stillopen;
        errors = 0;
        stillopen = 0;
        countBrackets(cursor, blockStart, pos - 1, openChar, closeChar, &errors, &stillopen);
        countBrackets(cursor, pos, blockEnd, openChar, closeChar, &errors, &stillopen);
        int errorsAfterDeletion = errors + stillopen;

        if (errorsAfterDeletion < errorsBeforeDeletion)
            return false; // insertion fixes parentheses or bracket errors, do not auto complete
    }

    // ### this code needs to be generalized
    if    ((lookBehind == QLatin1Char('(') && lookAhead == QLatin1Char(')'))
        || (lookBehind == QLatin1Char('[') && lookAhead == QLatin1Char(']'))
        || (lookBehind == QLatin1Char('{') && lookAhead == QLatin1Char('}'))
        || (lookBehind == QLatin1Char('"') && lookAhead == QLatin1Char('"')
            && lookFurtherBehind != QLatin1Char('\\'))
        || (lookBehind == QLatin1Char('\'') && lookAhead == QLatin1Char('\'')
            && lookFurtherBehind != QLatin1Char('\\'))) {
        if (! isInComment(c)) {
            cursor.beginEditBlock();
            cursor.deleteChar();
            cursor.deletePreviousChar();
            cursor.endEditBlock();
            return true;
        }
    }
    return false;
}