From f053ec8c1498261db2d079bce20ef8b28d418982 Mon Sep 17 00:00:00 2001 From: Henrik Abelsson <henrik@abelsson.com> Date: Tue, 21 Jul 2009 10:37:00 +0200 Subject: [PATCH] Initial support for emacs like fill-paragraph. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge-request: 882 Reviewed-by: Thorbjørn Lindeijer <thorbjorn.lindeijer@nokia.com> --- src/plugins/texteditor/basetexteditor.cpp | 123 ++++++++++++++++++ src/plugins/texteditor/basetexteditor.h | 1 + .../texteditor/texteditoractionhandler.cpp | 7 + .../texteditor/texteditoractionhandler.h | 2 + src/plugins/texteditor/texteditorconstants.h | 3 +- 5 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 189b9a50537..b61236cdd95 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -3850,6 +3850,129 @@ void BaseTextEditor::format() cursor.endEditBlock(); } +void BaseTextEditor::rewrapParagraph() +{ + const int paragraphWidth = displaySettings().m_wrapColumn; + const QRegExp anyLettersOrNumbers = QRegExp("\\w"); + const int tabSize = tabSettings().m_tabSize; + + QTextCursor cursor = textCursor(); + cursor.beginEditBlock(); + + // Find start of paragraph. + + while (cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) { + QTextBlock block = cursor.block(); + QString text = block.text(); + + // If this block is empty, move marker back to previous and terminate. + if (!text.contains(anyLettersOrNumbers)) { + cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor); + break; + } + } + + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + + // Find indent level of current block. + + int indentLevel = 0; + QString text = cursor.block().text(); + + for (int i = 0; i < text.length(); i++) { + const QChar ch = text.at(i); + + if (ch == QLatin1Char(' ')) + indentLevel++; + else if (ch == QLatin1Char('\t')) + indentLevel += tabSize - (indentLevel % tabSize); + else + break; + } + + // If there is a common prefix, it should be kept and expanded to all lines. + // this allows nice reflowing of doxygen style comments. + QTextCursor nextBlock = cursor; + QString commonPrefix; + + if (nextBlock.movePosition(QTextCursor::NextBlock)) + { + QString nText = nextBlock.block().text(); + int maxLength = qMin(text.length(), nText.length()); + + for (int i = 0; i < maxLength; ++i) { + const QChar ch = text.at(i); + + if (ch != nText[i] || ch.isLetterOrNumber()) + break; + commonPrefix.append(ch); + } + } + + + // Find end of paragraph. + while (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor)) { + QString text = cursor.block().text(); + + if (!text.contains(anyLettersOrNumbers)) + break; + } + + + QString selectedText = cursor.selectedText(); + + // Preserve initial indent level.or common prefix. + QString spacing; + + if (commonPrefix.isEmpty()) { + spacing = tabSettings().indentationString(0, indentLevel); + } else { + spacing = commonPrefix; + indentLevel = commonPrefix.length(); + } + + int currentLength = indentLevel; + QString result; + result.append(spacing); + + // Remove existing instances of any common prefix from paragraph to + // reflow. + selectedText.remove(0, commonPrefix.length()); + commonPrefix.prepend(QChar::ParagraphSeparator); + selectedText.replace(commonPrefix, QLatin1String("\n")); + + // remove any repeated spaces, trim lines to PARAGRAPH_WIDTH width and + // keep the same indentation level as first line in paragraph. + QString currentWord; + + for (int i = 0; i < selectedText.length(); ++i) { + QChar ch = selectedText.at(i); + if (ch.isSpace()) { + if (!currentWord.isEmpty()) { + currentLength += currentWord.length() + 1; + + if (currentLength > paragraphWidth - indentLevel) { + currentLength = currentWord.length() + 1 + indentLevel; + result.append(QChar::ParagraphSeparator); + result.append(spacing); + } + + result.append(currentWord); + result.append(QLatin1String(" ")); + currentWord.clear(); + } + + continue; + } + + currentWord.append(ch); + } + result.append(QChar::ParagraphSeparator); + + cursor.insertText(result); + cursor.endEditBlock(); +} + void BaseTextEditor::unCommentSelection() { } diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 9fb764aae2f..cc8df867c5c 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -483,6 +483,7 @@ public: public slots: virtual void format(); + virtual void rewrapParagraph(); virtual void unCommentSelection(); virtual void setFontSettings(const TextEditor::FontSettings &); void setFontSettingsIfVisible(const TextEditor::FontSettings &); diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp index ff6933cabe1..abf81e0b576 100644 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -141,6 +141,12 @@ void TextEditorActionHandler::createActions() advancedMenu->addAction(command, Core::Constants::G_EDIT_FORMAT); connect(m_formatAction, SIGNAL(triggered(bool)), this, SLOT(formatAction())); + m_rewrapParagraphAction = new QAction(tr("&Rewrap Paragraph"), this); + command = am->registerAction(m_rewrapParagraphAction, TextEditor::Constants::REWRAP_PARAGRAPH, m_contextId); + //command->setDefaultKeySequence(QKeySequence(tr("Alt+Q"))); (No default key sequence for now.) + advancedMenu->addAction(command, Core::Constants::G_EDIT_FORMAT); + connect(m_rewrapParagraphAction, SIGNAL(triggered(bool)), this, SLOT(rewrapParagraphAction())); + m_visualizeWhitespaceAction = new QAction(tr("&Visualize Whitespace"), this); m_visualizeWhitespaceAction->setCheckable(true); @@ -398,6 +404,7 @@ FUNCTION2(copyAction, copy) FUNCTION2(cutAction, cut) FUNCTION2(pasteAction, paste) FUNCTION2(formatAction, format) +FUNCTION2(rewrapParagraphAction, rewrapParagraph) FUNCTION2(selectAllAction, selectAll) FUNCTION(cleanWhitespace) FUNCTION(unCommentSelection) diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h index c782ff9b838..a184ac6e442 100644 --- a/src/plugins/texteditor/texteditoractionhandler.h +++ b/src/plugins/texteditor/texteditoractionhandler.h @@ -91,6 +91,7 @@ private slots: void gotoAction(); void printAction(); void formatAction(); + void rewrapParagraphAction(); void setVisualizeWhitespace(bool); void cleanWhitespace(); void setTextWrapping(bool); @@ -125,6 +126,7 @@ private: QAction *m_gotoAction; QAction *m_printAction; QAction *m_formatAction; + QAction *m_rewrapParagraphAction; QAction *m_visualizeWhitespaceAction; QAction *m_cleanWhitespaceAction; QAction *m_textWrappingAction; diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index 18d19492987..8b83ea27a13 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -60,8 +60,9 @@ const char * const CUT_LINE = "TextEditor.CutLine"; const char * const DELETE_LINE = "TextEditor.DeleteLine"; const char * const DELETE_WORD = "TextEditor.DeleteWord"; const char * const SELECT_ENCODING = "TextEditor.SelectEncoding"; +const char * const REWRAP_PARAGRAPH = "TextEditor.RewrapParagraph"; const char * const GOTO_OPENING_PARENTHESIS = "TextEditor.GotoOpeningParenthesis"; -const char * const GOTO_CLOSING_PARENTHESIS = "TextEditor.GotoOpeningParenthesis"; +const char * const GOTO_CLOSING_PARENTHESIS = "TextEditor.GotoClosingParenthesis"; const char * const C_TEXTEDITOR_MIMETYPE_TEXT = "text/plain"; const char * const C_TEXTEDITOR_MIMETYPE_XML = "application/xml"; -- GitLab