From edab564cf0c051ad0774dc5a59751d3c5781b263 Mon Sep 17 00:00:00 2001
From: Nikolai Kosjar <nikolai.kosjar@qt.io>
Date: Mon, 4 Sep 2017 15:18:42 +0200
Subject: [PATCH] Beautifier: ClangFormat: Add action "Disable Formatting for
 Selected Text"

Change-Id: I0786dfdc0679bbdf1cf1157067bd7f572ac7d108
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Reviewed-by: Lorenz Haas <lorenz.haas@histomatics.de>
---
 dist/clangformat/README.md                    |  1 +
 doc/src/editors/creator-beautifier.qdoc       |  4 ++
 src/plugins/beautifier/beautifierplugin.cpp   |  6 +++
 src/plugins/beautifier/beautifierplugin.h     |  1 +
 .../beautifier/clangformat/clangformat.cpp    | 45 +++++++++++++++++++
 .../beautifier/clangformat/clangformat.h      |  2 +
 .../clangformat/clangformatconstants.h        |  1 +
 7 files changed, 60 insertions(+)

diff --git a/dist/clangformat/README.md b/dist/clangformat/README.md
index ae714aad9ee..aa3584e4621 100644
--- a/dist/clangformat/README.md
+++ b/dist/clangformat/README.md
@@ -58,6 +58,7 @@ For Windows:
      In Menu: Tools > Options > Environment > Keyboard
       * ClangFormat / FormatFile - e.g. Alt+C, F
       * ClangFormat / FormatSelectedText - e.g. Alt+C, S
+      * ClangFormat / DisableFormattingSelectedText - e.g. Alt+C, D
 
 Due to several issues outlined below the FormatFile action might be of limited
 use.
diff --git a/doc/src/editors/creator-beautifier.qdoc b/doc/src/editors/creator-beautifier.qdoc
index c47f9fb5121..e93d222594a 100644
--- a/doc/src/editors/creator-beautifier.qdoc
+++ b/doc/src/editors/creator-beautifier.qdoc
@@ -178,4 +178,8 @@
     select it when no text is selected, the whole file is formatted by default.
     To disable this behavior, deselect the
     \uicontrol {Format entire file if no text was selected} check box.
+
+    ClangFormat provides additionally the \uicontrol {Disable Formatting for
+    Selected Text} command. If you select it, the selected lines will be
+    wrapped within \c {// clang-format off} and \c {// clang-format on}.
 */
diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp
index 0dcfbf7c0ef..947233606c1 100644
--- a/src/plugins/beautifier/beautifierplugin.cpp
+++ b/src/plugins/beautifier/beautifierplugin.cpp
@@ -496,6 +496,12 @@ QString BeautifierPlugin::msgFormatSelectedText()
     return tr("Format &Selected Text");
 }
 
+QString BeautifierPlugin::msgDisableFormattingSelectedText()
+{
+    //: Menu entry
+    return tr("&Disable Formatting for Selected Text");
+}
+
 QString BeautifierPlugin::msgCommandPromptDialogTitle(const QString &command)
 {
     //: File dialog title for path chooser when choosing binary
diff --git a/src/plugins/beautifier/beautifierplugin.h b/src/plugins/beautifier/beautifierplugin.h
index 14d6674ab8a..32189bcb19d 100644
--- a/src/plugins/beautifier/beautifierplugin.h
+++ b/src/plugins/beautifier/beautifierplugin.h
@@ -80,6 +80,7 @@ public:
     static QString msgCannotGetConfigurationFile(const QString &command);
     static QString msgFormatCurrentFile();
     static QString msgFormatSelectedText();
+    static QString msgDisableFormattingSelectedText();
     static QString msgCommandPromptDialogTitle(const QString &command);
     static void showError(const QString &error);
 
diff --git a/src/plugins/beautifier/clangformat/clangformat.cpp b/src/plugins/beautifier/clangformat/clangformat.cpp
index 13f7caab24f..b677a59fbcc 100644
--- a/src/plugins/beautifier/clangformat/clangformat.cpp
+++ b/src/plugins/beautifier/clangformat/clangformat.cpp
@@ -48,6 +48,7 @@
 
 #include <QAction>
 #include <QMenu>
+#include <QTextBlock>
 
 namespace Beautifier {
 namespace Internal {
@@ -88,6 +89,14 @@ bool ClangFormat::initialize()
     menu->addAction(cmd);
     connect(m_formatRange, &QAction::triggered, this, &ClangFormat::formatSelectedText);
 
+    m_disableFormattingSelectedText
+        = new QAction(BeautifierPlugin::msgDisableFormattingSelectedText(), this);
+    cmd = Core::ActionManager::registerAction(
+        m_disableFormattingSelectedText, Constants::ClangFormat::ACTION_DISABLEFORMATTINGSELECTED);
+    menu->addAction(cmd);
+    connect(m_disableFormattingSelectedText, &QAction::triggered,
+            this, &ClangFormat::disableFormattingSelectedText);
+
     Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu);
 
     connect(m_settings, &ClangFormatSettings::supportedMimeTypesChanged,
@@ -130,6 +139,42 @@ void ClangFormat::formatSelectedText()
     }
 }
 
+void ClangFormat::disableFormattingSelectedText()
+{
+    TextEditor::TextEditorWidget *widget = TextEditor::TextEditorWidget::currentTextEditorWidget();
+    if (!widget)
+        return;
+
+    const QTextCursor tc = widget->textCursor();
+    if (!tc.hasSelection())
+        return;
+
+    // Insert start marker
+    const QTextBlock selectionStartBlock = tc.document()->findBlock(tc.selectionStart());
+    QTextCursor insertCursor(tc.document());
+    insertCursor.beginEditBlock();
+    insertCursor.setPosition(selectionStartBlock.position());
+    insertCursor.insertText("// clang-format off\n");
+    const int positionToRestore = tc.position();
+
+    // Insert end marker
+    QTextBlock selectionEndBlock = tc.document()->findBlock(tc.selectionEnd());
+    insertCursor.setPosition(selectionEndBlock.position() + selectionEndBlock.length() - 1);
+    insertCursor.insertText("\n// clang-format on");
+    insertCursor.endEditBlock();
+
+    // Reset the cursor position in order to clear the selection.
+    QTextCursor restoreCursor(tc.document());
+    restoreCursor.setPosition(positionToRestore);
+    widget->setTextCursor(restoreCursor);
+
+    // The indentation of these markers might be undesired, so reformat.
+    // This is not optimal because two undo steps will be needed to remove the markers.
+    const int reformatTextLength = insertCursor.position() - selectionStartBlock.position();
+    m_beautifierPlugin->formatCurrentFile(command(selectionStartBlock.position(),
+                                                  reformatTextLength));
+}
+
 Command ClangFormat::command() const
 {
     Command command;
diff --git a/src/plugins/beautifier/clangformat/clangformat.h b/src/plugins/beautifier/clangformat/clangformat.h
index 86037fac0cc..bb5e5bf7881 100644
--- a/src/plugins/beautifier/clangformat/clangformat.h
+++ b/src/plugins/beautifier/clangformat/clangformat.h
@@ -55,9 +55,11 @@ public:
 private:
     void formatFile();
     void formatSelectedText();
+    void disableFormattingSelectedText();
     BeautifierPlugin *m_beautifierPlugin;
     QAction *m_formatFile = nullptr;
     QAction *m_formatRange = nullptr;
+    QAction *m_disableFormattingSelectedText = nullptr;
     ClangFormatSettings *m_settings;
     Command command(int offset, int length) const;
 };
diff --git a/src/plugins/beautifier/clangformat/clangformatconstants.h b/src/plugins/beautifier/clangformat/clangformatconstants.h
index 837d3323069..51f3981fc5d 100644
--- a/src/plugins/beautifier/clangformat/clangformatconstants.h
+++ b/src/plugins/beautifier/clangformat/clangformatconstants.h
@@ -34,6 +34,7 @@ namespace ClangFormat {
 const char DISPLAY_NAME[]          = QT_TRANSLATE_NOOP("Beautifier::Internal::ClangFormat::ClangFormat", "ClangFormat");
 const char ACTION_FORMATFILE[]     = "ClangFormat.FormatFile";
 const char ACTION_FORMATSELECTED[] = "ClangFormat.FormatSelectedText";
+const char ACTION_DISABLEFORMATTINGSELECTED[] = "ClangFormat.DisableFormattingSelectedText";
 const char MENU_ID[]               = "ClangFormat.Menu";
 const char OPTION_ID[]             = "ClangFormat";
 const char SETTINGS_NAME[]         = "clangformat";
-- 
GitLab