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