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(); }
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; }
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; }
// 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; }
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; }
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; }
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); }
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; }
bool MatchingText::shouldInsertMatchingText(const QTextCursor &tc) { QTextDocument *doc = tc.document(); return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd())); }
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; }