summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/utils/differ.cpp (renamed from src/plugins/diffeditor/differ.cpp)4
-rw-r--r--src/libs/utils/differ.h (renamed from src/plugins/diffeditor/differ.h)10
-rw-r--r--src/libs/utils/utils-lib.pri6
-rw-r--r--src/libs/utils/utils.qbs2
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyle.cpp5
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyle.h4
-rw-r--r--src/plugins/beautifier/beautifier.pro2
-rw-r--r--src/plugins/beautifier/beautifier.qbs2
-rw-r--r--src/plugins/beautifier/beautifierabstracttool.h4
-rw-r--r--src/plugins/beautifier/beautifierplugin.cpp339
-rw-r--r--src/plugins/beautifier/beautifierplugin.h5
-rw-r--r--src/plugins/beautifier/clangformat/clangformat.cpp17
-rw-r--r--src/plugins/beautifier/clangformat/clangformat.h4
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustify.cpp10
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustify.h4
-rw-r--r--src/plugins/diffeditor/diffeditor.pro2
-rw-r--r--src/plugins/diffeditor/diffeditor.qbs2
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.cpp3
-rw-r--r--src/plugins/diffeditor/diffutils.cpp8
-rw-r--r--src/plugins/diffeditor/diffutils.h8
-rw-r--r--src/plugins/texteditor/command.cpp (renamed from src/plugins/beautifier/command.cpp)6
-rw-r--r--src/plugins/texteditor/command.h (renamed from src/plugins/beautifier/command.h)10
-rw-r--r--src/plugins/texteditor/formattexteditor.cpp364
-rw-r--r--src/plugins/texteditor/formattexteditor.h67
-rw-r--r--src/plugins/texteditor/texteditor.pro8
-rw-r--r--src/plugins/texteditor/texteditor.qbs4
26 files changed, 503 insertions, 397 deletions
diff --git a/src/plugins/diffeditor/differ.cpp b/src/libs/utils/differ.cpp
index b810eeff62..cff9dd21db 100644
--- a/src/plugins/diffeditor/differ.cpp
+++ b/src/libs/utils/differ.cpp
@@ -41,7 +41,7 @@ publication by Neil Fraser: http://neil.fraser.name/writing/diff/
#include <QCoreApplication>
#include <QFutureInterfaceBase>
-namespace DiffEditor {
+namespace Utils {
static int commonPrefix(const QString &text1, const QString &text2)
{
@@ -1555,4 +1555,4 @@ QList<Diff> Differ::cleanupSemanticsLossless(const QList<Diff> &diffList)
return newDiffList;
}
-} // namespace DiffEditor
+} // namespace Utils
diff --git a/src/plugins/diffeditor/differ.h b/src/libs/utils/differ.h
index 14012cc10f..11eebfe527 100644
--- a/src/plugins/diffeditor/differ.h
+++ b/src/libs/utils/differ.h
@@ -25,7 +25,7 @@
#pragma once
-#include "diffeditor_global.h"
+#include "utils_global.h"
#include <QString>
QT_BEGIN_NAMESPACE
@@ -34,9 +34,9 @@ class QMap;
class QFutureInterfaceBase;
QT_END_NAMESPACE
-namespace DiffEditor {
+namespace Utils {
-class DIFFEDITOR_EXPORT Diff
+class QTCREATOR_UTILS_EXPORT Diff
{
public:
enum Command {
@@ -54,7 +54,7 @@ public:
static QString commandString(Command com);
};
-class DIFFEDITOR_EXPORT Differ
+class QTCREATOR_UTILS_EXPORT Differ
{
public:
enum DiffMode
@@ -114,4 +114,4 @@ private:
QFutureInterfaceBase *m_jobController = nullptr;
};
-} // namespace DiffEditor
+} // namespace Utils
diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri
index 41d8745baa..3369e0e161 100644
--- a/src/libs/utils/utils-lib.pri
+++ b/src/libs/utils/utils-lib.pri
@@ -121,7 +121,8 @@ SOURCES += \
$$PWD/url.cpp \
$$PWD/filecrumblabel.cpp \
$$PWD/fixedsizeclicklabel.cpp \
- $$PWD/removefiledialog.cpp
+ $$PWD/removefiledialog.cpp \
+ $$PWD/differ.cpp
win32:SOURCES += $$PWD/consoleprocess_win.cpp
else:SOURCES += $$PWD/consoleprocess_unix.cpp
@@ -258,7 +259,8 @@ HEADERS += \
$$PWD/linecolumn.h \
$$PWD/link.h \
$$PWD/fixedsizeclicklabel.h \
- $$PWD/removefiledialog.h
+ $$PWD/removefiledialog.h \
+ $$PWD/differ.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/newclasswidget.ui \
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 2ada52c17e..40f7a21ca4 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -75,6 +75,8 @@ Project {
"detailsbutton.h",
"detailswidget.cpp",
"detailswidget.h",
+ "differ.cpp",
+ "differ.h",
"dropsupport.cpp",
"dropsupport.h",
"elfreader.cpp",
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
index 40e5cb20cb..4a7ee4ec54 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
+++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp
@@ -44,12 +44,15 @@
#include <cppeditor/cppeditorconstants.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/project.h>
+#include <texteditor/formattexteditor.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <QAction>
#include <QMenu>
+using namespace TextEditor;
+
namespace Beautifier {
namespace Internal {
namespace ArtisticStyle {
@@ -101,7 +104,7 @@ void ArtisticStyle::formatFile()
BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile(
tr(Constants::ArtisticStyle::DISPLAY_NAME)));
} else {
- BeautifierPlugin::formatCurrentFile(command(cfgFileName));
+ formatCurrentFile(command(cfgFileName));
}
}
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.h b/src/plugins/beautifier/artisticstyle/artisticstyle.h
index 2dbcca4bec..5927814489 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstyle.h
+++ b/src/plugins/beautifier/artisticstyle/artisticstyle.h
@@ -46,7 +46,7 @@ public:
bool initialize() override;
QString id() const override;
void updateActions(Core::IEditor *editor) override;
- Command command() const override;
+ TextEditor::Command command() const override;
bool isApplicable(const Core::IDocument *document) const override;
private:
@@ -54,7 +54,7 @@ private:
QAction *m_formatFile = nullptr;
ArtisticStyleSettings *m_settings;
QString configurationFile() const;
- Command command(const QString &cfgFile) const;
+ TextEditor::Command command(const QString &cfgFile) const;
};
} // namespace ArtisticStyle
diff --git a/src/plugins/beautifier/beautifier.pro b/src/plugins/beautifier/beautifier.pro
index 6dcdbf6e69..65021fd8be 100644
--- a/src/plugins/beautifier/beautifier.pro
+++ b/src/plugins/beautifier/beautifier.pro
@@ -5,7 +5,6 @@ HEADERS += \
beautifierabstracttool.h \
beautifierconstants.h \
beautifierplugin.h \
- command.h \
configurationdialog.h \
configurationeditor.h \
configurationpanel.h \
@@ -27,7 +26,6 @@ HEADERS += \
SOURCES += \
abstractsettings.cpp \
beautifierplugin.cpp \
- command.cpp \
configurationdialog.cpp \
configurationeditor.cpp \
configurationpanel.cpp \
diff --git a/src/plugins/beautifier/beautifier.qbs b/src/plugins/beautifier/beautifier.qbs
index 35c5a17fc8..1a71cb0098 100644
--- a/src/plugins/beautifier/beautifier.qbs
+++ b/src/plugins/beautifier/beautifier.qbs
@@ -20,8 +20,6 @@ QtcPlugin {
"beautifierconstants.h",
"beautifierplugin.cpp",
"beautifierplugin.h",
- "command.cpp",
- "command.h",
"configurationdialog.cpp",
"configurationdialog.h",
"configurationdialog.ui",
diff --git a/src/plugins/beautifier/beautifierabstracttool.h b/src/plugins/beautifier/beautifierabstracttool.h
index 9253a6d910..813f37ac7f 100644
--- a/src/plugins/beautifier/beautifierabstracttool.h
+++ b/src/plugins/beautifier/beautifierabstracttool.h
@@ -25,7 +25,7 @@
#pragma once
-#include "command.h"
+#include <texteditor/command.h>
#include <QObject>
@@ -53,7 +53,7 @@ public:
*
* @note The received command may be invalid.
*/
- virtual Command command() const = 0;
+ virtual TextEditor::Command command() const = 0;
virtual bool isApplicable(const Core::IDocument *document) const = 0;
};
diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp
index fa973c717a..3883a79044 100644
--- a/src/plugins/beautifier/beautifierplugin.cpp
+++ b/src/plugins/beautifier/beautifierplugin.cpp
@@ -42,9 +42,9 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/messagemanager.h>
#include <cppeditor/cppeditorconstants.h>
-#include <diffeditor/differ.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projecttree.h>
+#include <texteditor/formattexteditor.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditor.h>
@@ -72,127 +72,6 @@ using namespace TextEditor;
namespace Beautifier {
namespace Internal {
-struct FormatTask
-{
- FormatTask(QPlainTextEdit *_editor, const QString &_filePath, const QString &_sourceData,
- const Command &_command, int _startPos = -1, int _endPos = 0) :
- editor(_editor),
- filePath(_filePath),
- sourceData(_sourceData),
- command(_command),
- startPos(_startPos),
- endPos(_endPos) {}
-
- QPointer<QPlainTextEdit> editor;
- QString filePath;
- QString sourceData;
- Command command;
- int startPos = -1;
- int endPos = 0;
- QString formattedData;
- QString error;
-};
-
-FormatTask format(FormatTask task)
-{
- task.error.clear();
- task.formattedData.clear();
-
- const QString executable = task.command.executable();
- if (executable.isEmpty())
- return task;
-
- switch (task.command.processing()) {
- case Command::FileProcessing: {
- // Save text to temporary file
- const QFileInfo fi(task.filePath);
- Utils::TempFileSaver sourceFile(Utils::TemporaryDirectory::masterDirectoryPath()
- + "/qtc_beautifier_XXXXXXXX."
- + fi.suffix());
- sourceFile.setAutoRemove(true);
- sourceFile.write(task.sourceData.toUtf8());
- if (!sourceFile.finalize()) {
- task.error = BeautifierPlugin::tr("Cannot create temporary file \"%1\": %2.")
- .arg(sourceFile.fileName()).arg(sourceFile.errorString());
- return task;
- }
-
- // Format temporary file
- QStringList options = task.command.options();
- options.replaceInStrings(QLatin1String("%file"), sourceFile.fileName());
- Utils::SynchronousProcess process;
- process.setTimeoutS(5);
- Utils::SynchronousProcessResponse response = process.runBlocking(executable, options);
- if (response.result != Utils::SynchronousProcessResponse::Finished) {
- task.error = BeautifierPlugin::tr("Failed to format: %1.").arg(response.exitMessage(executable, 5));
- return task;
- }
- const QString output = response.stdErr();
- if (!output.isEmpty())
- task.error = executable + QLatin1String(": ") + output;
-
- // Read text back
- Utils::FileReader reader;
- if (!reader.fetch(sourceFile.fileName(), QIODevice::Text)) {
- task.error = BeautifierPlugin::tr("Cannot read file \"%1\": %2.")
- .arg(sourceFile.fileName()).arg(reader.errorString());
- return task;
- }
- task.formattedData = QString::fromUtf8(reader.data());
- }
- return task;
-
- case Command::PipeProcessing: {
- QProcess process;
- QStringList options = task.command.options();
- options.replaceInStrings("%filename", QFileInfo(task.filePath).fileName());
- options.replaceInStrings("%file", task.filePath);
- process.start(executable, options);
- if (!process.waitForStarted(3000)) {
- task.error = BeautifierPlugin::tr("Cannot call %1 or some other error occurred.")
- .arg(executable);
- return task;
- }
- process.write(task.sourceData.toUtf8());
- process.closeWriteChannel();
- if (!process.waitForFinished(5000) && process.state() == QProcess::Running) {
- process.kill();
- task.error = BeautifierPlugin::tr("Cannot call %1 or some other error occurred. Timeout "
- "reached while formatting file %2.")
- .arg(executable).arg(task.filePath);
- return task;
- }
- const QByteArray errorText = process.readAllStandardError();
- if (!errorText.isEmpty()) {
- task.error = QString::fromLatin1("%1: %2").arg(executable)
- .arg(QString::fromUtf8(errorText));
- return task;
- }
-
- task.formattedData = QString::fromUtf8(process.readAllStandardOutput());
-
- if (task.command.pipeAddsNewline() && task.formattedData.endsWith('\n')) {
- task.formattedData.chop(1);
- if (task.formattedData.endsWith('\r'))
- task.formattedData.chop(1);
- }
- if (task.command.returnsCRLF())
- task.formattedData.replace("\r\n", "\n");
-
- return task;
- }
- }
-
- return task;
-}
-
-QString sourceData(TextEditorWidget *editor, int startPos, int endPos)
-{
- return (startPos < 0)
- ? editor->toPlainText()
- : Utils::Text::textAt(editor->textCursor(), startPos, (endPos - startPos));
-}
-
bool isAutoFormatApplicable(const Core::IDocument *document,
const QList<Utils::MimeType> &allowedMimeTypes)
{
@@ -215,17 +94,9 @@ public:
void updateActions(Core::IEditor *editor = nullptr);
- void formatEditor(TextEditor::TextEditorWidget *editor, const Command &command,
- int startPos = -1, int endPos = 0);
- void formatEditorAsync(TextEditor::TextEditorWidget *editor, const Command &command,
- int startPos = -1, int endPos = 0);
- void checkAndApplyTask(const FormatTask &task);
- void updateEditorText(QPlainTextEdit *editor, const QString &text);
-
void autoFormatOnSave(Core::IDocument *document);
QSharedPointer<GeneralSettings> m_generalSettings;
- QHash<QObject*, QMetaObject::Connection> m_autoFormatConnections;
ArtisticStyle::ArtisticStyle artisticStyleBeautifier;
ClangFormat::ClangFormat clangFormatBeautifier;
@@ -304,219 +175,15 @@ void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document)
if (tool != m_tools.constEnd()) {
if (!(*tool)->isApplicable(document))
return;
- const Command command = (*tool)->command();
+ const TextEditor::Command command = (*tool)->command();
if (!command.isValid())
return;
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(document);
if (editors.isEmpty())
return;
if (TextEditorWidget* widget = qobject_cast<TextEditorWidget *>(editors.first()->widget()))
- formatEditor(widget, command);
- }
-}
-
-void BeautifierPlugin::formatCurrentFile(const Command &command, int startPos, int endPos)
-{
- QTC_ASSERT(dd, return);
- if (TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget())
- dd->formatEditorAsync(editor, command, startPos, endPos);
-}
-
-/**
- * Formats the text of @a editor using @a command. @a startPos and @a endPos specifies the range of
- * the editor's text that will be formatted. If @a startPos is negative the editor's entire text is
- * formatted.
- *
- * @pre @a endPos must be greater than or equal to @a startPos
- */
-void BeautifierPluginPrivate::formatEditor(TextEditorWidget *editor, const Command &command,
- int startPos, int endPos)
-{
- QTC_ASSERT(startPos <= endPos, return);
-
- const QString sd = sourceData(editor, startPos, endPos);
- if (sd.isEmpty())
- return;
- checkAndApplyTask(format(FormatTask(editor, editor->textDocument()->filePath().toString(), sd,
- command, startPos, endPos)));
-}
-
-/**
- * Behaves like formatEditor except that the formatting is done asynchronously.
- */
-void BeautifierPluginPrivate::formatEditorAsync(TextEditorWidget *editor, const Command &command,
- int startPos, int endPos)
-{
- QTC_ASSERT(startPos <= endPos, return);
-
- const QString sd = sourceData(editor, startPos, endPos);
- if (sd.isEmpty())
- return;
-
- QFutureWatcher<FormatTask> *watcher = new QFutureWatcher<FormatTask>;
- const TextDocument *doc = editor->textDocument();
- connect(doc, &TextDocument::contentsChanged, watcher, &QFutureWatcher<FormatTask>::cancel);
- connect(watcher, &QFutureWatcherBase::finished, [this, watcher] {
- if (watcher->isCanceled())
- BeautifierPlugin::showError(BeautifierPlugin::tr("File was modified."));
- else
- checkAndApplyTask(watcher->result());
- watcher->deleteLater();
- });
- watcher->setFuture(Utils::runAsync(&format, FormatTask(editor, doc->filePath().toString(), sd,
- command, startPos, endPos)));
-}
-
-/**
- * Checks the state of @a task and if the formatting was successful calls updateEditorText() with
- * the respective members of @a task.
- */
-void BeautifierPluginPrivate::checkAndApplyTask(const FormatTask &task)
-{
- if (!task.error.isEmpty()) {
- BeautifierPlugin::showError(task.error);
- return;
- }
-
- if (task.formattedData.isEmpty()) {
- BeautifierPlugin::showError(BeautifierPlugin::tr("Could not format file %1.").arg(task.filePath));
- return;
- }
-
- QPlainTextEdit *textEditor = task.editor;
- if (!textEditor) {
- BeautifierPlugin::showError(BeautifierPlugin::tr("File %1 was closed.").arg(task.filePath));
- return;
+ TextEditor::formatEditor(widget, command);
}
-
- const QString formattedData = (task.startPos < 0)
- ? task.formattedData
- : QString(textEditor->toPlainText()).replace(
- task.startPos, (task.endPos - task.startPos), task.formattedData);
-
- updateEditorText(textEditor, formattedData);
-}
-
-/**
- * Sets the text of @a editor to @a text. Instead of replacing the entire text, however, only the
- * actually changed parts are updated while preserving the cursor position, the folded
- * blocks, and the scroll bar position.
- */
-void BeautifierPluginPrivate::updateEditorText(QPlainTextEdit *editor, const QString &text)
-{
- const QString editorText = editor->toPlainText();
- if (editorText == text)
- return;
-
- // Calculate diff
- DiffEditor::Differ differ;
- const QList<DiffEditor::Diff> diff = differ.diff(editorText, text);
-
- // Since QTextCursor does not work properly with folded blocks, all blocks must be unfolded.
- // To restore the current state at the end, keep track of which block is folded.
- QList<int> foldedBlocks;
- QTextBlock block = editor->document()->firstBlock();
- while (block.isValid()) {
- if (const TextBlockUserData *userdata = static_cast<TextBlockUserData *>(block.userData())) {
- if (userdata->folded()) {
- foldedBlocks << block.blockNumber();
- TextDocumentLayout::doFoldOrUnfold(block, true);
- }
- }
- block = block.next();
- }
- editor->update();
-
- // Save the current viewport position of the cursor to ensure the same vertical position after
- // the formatted text has set to the editor.
- int absoluteVerticalCursorOffset = editor->cursorRect().y();
-
- // Update changed lines and keep track of the cursor position
- QTextCursor cursor = editor->textCursor();
- int charactersInfrontOfCursor = cursor.position();
- int newCursorPos = charactersInfrontOfCursor;
- cursor.beginEditBlock();
- cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
- for (const DiffEditor::Diff &d : diff) {
- switch (d.command) {
- case DiffEditor::Diff::Insert:
- {
- // Adjust cursor position if we do work in front of the cursor.
- if (charactersInfrontOfCursor > 0) {
- const int size = d.text.size();
- charactersInfrontOfCursor += size;
- newCursorPos += size;
- }
- // Adjust folded blocks, if a new block is added.
- if (d.text.contains('\n')) {
- const int newLineCount = d.text.count('\n');
- const int number = cursor.blockNumber();
- const int total = foldedBlocks.size();
- for (int i = 0; i < total; ++i) {
- if (foldedBlocks.at(i) > number)
- foldedBlocks[i] += newLineCount;
- }
- }
- cursor.insertText(d.text);
- break;
- }
-
- case DiffEditor::Diff::Delete:
- {
- // Adjust cursor position if we do work in front of the cursor.
- if (charactersInfrontOfCursor > 0) {
- const int size = d.text.size();
- charactersInfrontOfCursor -= size;
- newCursorPos -= size;
- // Cursor was inside the deleted text, so adjust the new cursor position
- if (charactersInfrontOfCursor < 0)
- newCursorPos -= charactersInfrontOfCursor;
- }
- // Adjust folded blocks, if at least one block is being deleted.
- if (d.text.contains('\n')) {
- const int newLineCount = d.text.count('\n');
- const int number = cursor.blockNumber();
- for (int i = 0, total = foldedBlocks.size(); i < total; ++i) {
- if (foldedBlocks.at(i) > number) {
- foldedBlocks[i] -= newLineCount;
- if (foldedBlocks[i] < number) {
- foldedBlocks.removeAt(i);
- --i;
- --total;
- }
- }
- }
- }
- cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, d.text.size());
- cursor.removeSelectedText();
- break;
- }
-
- case DiffEditor::Diff::Equal:
- // Adjust cursor position
- charactersInfrontOfCursor -= d.text.size();
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, d.text.size());
- break;
- }
- }
- cursor.endEditBlock();
- cursor.setPosition(newCursorPos);
- editor->setTextCursor(cursor);
-
- // Adjust vertical scrollbar
- absoluteVerticalCursorOffset = editor->cursorRect().y() - absoluteVerticalCursorOffset;
- const double fontHeight = QFontMetrics(editor->document()->defaultFont()).height();
- editor->verticalScrollBar()->setValue(editor->verticalScrollBar()->value()
- + absoluteVerticalCursorOffset / fontHeight);
- // Restore folded blocks
- const QTextDocument *doc = editor->document();
- for (int blockId : foldedBlocks) {
- const QTextBlock block = doc->findBlockByNumber(qMax(0, blockId));
- if (block.isValid())
- TextDocumentLayout::doFoldOrUnfold(block, false);
- }
-
- editor->document()->setModified(true);
}
void BeautifierPlugin::showError(const QString &error)
diff --git a/src/plugins/beautifier/beautifierplugin.h b/src/plugins/beautifier/beautifierplugin.h
index 4b55eab80c..c35ef2ab9a 100644
--- a/src/plugins/beautifier/beautifierplugin.h
+++ b/src/plugins/beautifier/beautifierplugin.h
@@ -25,9 +25,8 @@
#pragma once
-#include "command.h"
-
#include <extensionsystem/iplugin.h>
+#include <texteditor/command.h>
namespace Beautifier {
namespace Internal {
@@ -38,8 +37,6 @@ class BeautifierPlugin : public ExtensionSystem::IPlugin
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Beautifier.json")
public:
- static void formatCurrentFile(const Command &command, int startPos = -1, int endPos = 0);
-
static QString msgCannotGetConfigurationFile(const QString &command);
static QString msgFormatCurrentFile();
static QString msgFormatSelectedText();
diff --git a/src/plugins/beautifier/clangformat/clangformat.cpp b/src/plugins/beautifier/clangformat/clangformat.cpp
index ce9d69c9d3..ee2e9ad770 100644
--- a/src/plugins/beautifier/clangformat/clangformat.cpp
+++ b/src/plugins/beautifier/clangformat/clangformat.cpp
@@ -42,6 +42,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
#include <cppeditor/cppeditorconstants.h>
+#include <texteditor/formattexteditor.h>
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
@@ -50,6 +51,8 @@
#include <QMenu>
#include <QTextBlock>
+using namespace TextEditor;
+
namespace Beautifier {
namespace Internal {
namespace ClangFormat {
@@ -114,13 +117,12 @@ void ClangFormat::updateActions(Core::IEditor *editor)
void ClangFormat::formatFile()
{
- BeautifierPlugin::formatCurrentFile(command());
+ formatCurrentFile(command());
}
void ClangFormat::formatAtCursor()
{
- const TextEditor::TextEditorWidget *widget
- = TextEditor::TextEditorWidget::currentTextEditorWidget();
+ const TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget();
if (!widget)
return;
@@ -128,7 +130,7 @@ void ClangFormat::formatAtCursor()
if (tc.hasSelection()) {
const int offset = tc.selectionStart();
const int length = tc.selectionEnd() - offset;
- BeautifierPlugin::formatCurrentFile(command(offset, length));
+ formatCurrentFile(command(offset, length));
} else {
// Pretend that the current line was selected.
// Note that clang-format will extend the range to the next bigger
@@ -136,13 +138,13 @@ void ClangFormat::formatAtCursor()
const QTextBlock block = tc.block();
const int offset = block.position();
const int length = block.length();
- BeautifierPlugin::formatCurrentFile(command(offset, length));
+ formatCurrentFile(command(offset, length));
}
}
void ClangFormat::disableFormattingSelectedText()
{
- TextEditor::TextEditorWidget *widget = TextEditor::TextEditorWidget::currentTextEditorWidget();
+ TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget();
if (!widget)
return;
@@ -172,8 +174,7 @@ void ClangFormat::disableFormattingSelectedText()
// 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();
- BeautifierPlugin::formatCurrentFile(command(selectionStartBlock.position(),
- reformatTextLength));
+ formatCurrentFile(command(selectionStartBlock.position(), reformatTextLength));
}
Command ClangFormat::command() const
diff --git a/src/plugins/beautifier/clangformat/clangformat.h b/src/plugins/beautifier/clangformat/clangformat.h
index 2da33b8190..4d69d2e9e5 100644
--- a/src/plugins/beautifier/clangformat/clangformat.h
+++ b/src/plugins/beautifier/clangformat/clangformat.h
@@ -46,7 +46,7 @@ public:
QString id() const override;
bool initialize() override;
void updateActions(Core::IEditor *editor) override;
- Command command() const override;
+ TextEditor::Command command() const override;
bool isApplicable(const Core::IDocument *document) const override;
private:
@@ -57,7 +57,7 @@ private:
QAction *m_formatRange = nullptr;
QAction *m_disableFormattingSelectedText = nullptr;
ClangFormatSettings *m_settings;
- Command command(int offset, int length) const;
+ TextEditor::Command command(int offset, int length) const;
};
} // namespace ClangFormat
diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp
index acba89331b..daeffdf2df 100644
--- a/src/plugins/beautifier/uncrustify/uncrustify.cpp
+++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp
@@ -44,12 +44,15 @@
#include <cppeditor/cppeditorconstants.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/project.h>
+#include <texteditor/formattexteditor.h>
#include <texteditor/texteditor.h>
#include <utils/fileutils.h>
#include <QAction>
#include <QMenu>
+using namespace TextEditor;
+
namespace Beautifier {
namespace Internal {
namespace Uncrustify {
@@ -111,7 +114,7 @@ void Uncrustify::formatFile()
BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile(
tr(Constants::Uncrustify::DISPLAY_NAME)));
} else {
- BeautifierPlugin::formatCurrentFile(command(cfgFileName));
+ formatCurrentFile(command(cfgFileName));
}
}
@@ -124,8 +127,7 @@ void Uncrustify::formatSelectedText()
return;
}
- const TextEditor::TextEditorWidget *widget
- = TextEditor::TextEditorWidget::currentTextEditorWidget();
+ const TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget();
if (!widget)
return;
@@ -141,7 +143,7 @@ void Uncrustify::formatSelectedText()
if (tc.positionInBlock() > 0)
tc.movePosition(QTextCursor::EndOfLine);
const int endPos = tc.position();
- BeautifierPlugin::formatCurrentFile(command(cfgFileName, true), startPos, endPos);
+ formatCurrentFile(command(cfgFileName, true), startPos, endPos);
} else if (m_settings->formatEntireFileFallback()) {
formatFile();
}
diff --git a/src/plugins/beautifier/uncrustify/uncrustify.h b/src/plugins/beautifier/uncrustify/uncrustify.h
index 3bdeffacc0..e35434fc81 100644
--- a/src/plugins/beautifier/uncrustify/uncrustify.h
+++ b/src/plugins/beautifier/uncrustify/uncrustify.h
@@ -45,7 +45,7 @@ public:
bool initialize() override;
QString id() const override;
void updateActions(Core::IEditor *editor) override;
- Command command() const override;
+ TextEditor::Command command() const override;
bool isApplicable(const Core::IDocument *document) const override;
private:
@@ -55,7 +55,7 @@ private:
QAction *m_formatRange = nullptr;
UncrustifySettings *m_settings;
QString configurationFile() const;
- Command command(const QString &cfgFile, bool fragment = false) const;
+ TextEditor::Command command(const QString &cfgFile, bool fragment = false) const;
};
} // namespace Uncrustify
diff --git a/src/plugins/diffeditor/diffeditor.pro b/src/plugins/diffeditor/diffeditor.pro
index 5c8c904495..235f4e9dcd 100644
--- a/src/plugins/diffeditor/diffeditor.pro
+++ b/src/plugins/diffeditor/diffeditor.pro
@@ -11,7 +11,6 @@ HEADERS += \
diffeditorfactory.h \
diffeditorplugin.h \
diffeditorwidgetcontroller.h \
- differ.h \
diffutils.h \
diffview.h \
selectabletexteditorwidget.h \
@@ -27,7 +26,6 @@ SOURCES += \
diffeditorfactory.cpp \
diffeditorplugin.cpp \
diffeditorwidgetcontroller.cpp \
- differ.cpp \
diffutils.cpp \
diffview.cpp \
selectabletexteditorwidget.cpp \
diff --git a/src/plugins/diffeditor/diffeditor.qbs b/src/plugins/diffeditor/diffeditor.qbs
index 8d0a1a1820..9c6a41c377 100644
--- a/src/plugins/diffeditor/diffeditor.qbs
+++ b/src/plugins/diffeditor/diffeditor.qbs
@@ -32,8 +32,6 @@ QtcPlugin {
"diffeditorplugin.h",
"diffeditorwidgetcontroller.cpp",
"diffeditorwidgetcontroller.h",
- "differ.cpp",
- "differ.h",
"diffutils.cpp",
"diffutils.h",
"diffview.cpp",
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index 4411c84c67..6d3ba8858d 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -29,7 +29,6 @@
#include "diffeditorcontroller.h"
#include "diffeditordocument.h"
#include "diffeditorfactory.h"
-#include "differ.h"
#include <QAction>
#include <QFileDialog>
@@ -50,10 +49,12 @@
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
+#include <utils/differ.h>
#include <utils/mapreduce.h>
#include <utils/qtcassert.h>
using namespace Core;
+using namespace Utils;
namespace DiffEditor {
namespace Internal {
diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp
index e2a1831251..e233173629 100644
--- a/src/plugins/diffeditor/diffutils.cpp
+++ b/src/plugins/diffeditor/diffutils.cpp
@@ -24,15 +24,17 @@
****************************************************************************/
#include "diffutils.h"
-#include "differ.h"
-#include "texteditor/fontsettings.h"
+#include <texteditor/fontsettings.h>
+#include <utils/differ.h>
#include <QFutureInterfaceBase>
#include <QRegularExpression>
#include <QStringList>
#include <QTextStream>
+using namespace Utils;
+
namespace DiffEditor {
static QList<TextLineData> assemblyRows(const QList<TextLineData> &lines,
@@ -103,7 +105,7 @@ static void handleDifference(const QString &text,
* The number of equalities on both lists must be the same.
*/
ChunkData DiffUtils::calculateOriginalData(const QList<Diff> &leftDiffList,
- const QList<Diff> &rightDiffList)
+ const QList<Diff> &rightDiffList)
{
int i = 0;
int j = 0;
diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h
index d057a38e2f..31956e6296 100644
--- a/src/plugins/diffeditor/diffutils.h
+++ b/src/plugins/diffeditor/diffutils.h
@@ -36,9 +36,9 @@ QT_END_NAMESPACE
namespace TextEditor { class FontSettings; }
-namespace DiffEditor {
+namespace Utils { class Diff; }
-class Diff;
+namespace DiffEditor {
class DIFFEDITOR_EXPORT DiffFileInfo {
public:
@@ -128,8 +128,8 @@ public:
GitFormat = AddLevel | 0x2, // Add line 'diff ..' as git does
};
- static ChunkData calculateOriginalData(const QList<Diff> &leftDiffList,
- const QList<Diff> &rightDiffList);
+ static ChunkData calculateOriginalData(const QList<Utils::Diff> &leftDiffList,
+ const QList<Utils::Diff> &rightDiffList);
static FileData calculateContextData(const ChunkData &originalData,
int contextLineCount,
int joinChunkThreshold = 1);
diff --git a/src/plugins/beautifier/command.cpp b/src/plugins/texteditor/command.cpp
index 6263ae9b52..b709334590 100644
--- a/src/plugins/beautifier/command.cpp
+++ b/src/plugins/texteditor/command.cpp
@@ -25,8 +25,7 @@
#include "command.h"
-namespace Beautifier {
-namespace Internal {
+namespace TextEditor {
bool Command::isValid() const
{
@@ -83,5 +82,4 @@ void Command::setReturnsCRLF(bool returnsCRLF)
m_returnsCRLF = returnsCRLF;
}
-} // namespace Internal
-} // namespace Beautifier
+} // namespace TextEditor
diff --git a/src/plugins/beautifier/command.h b/src/plugins/texteditor/command.h
index f81880fb3d..dcb2f6f23a 100644
--- a/src/plugins/beautifier/command.h
+++ b/src/plugins/texteditor/command.h
@@ -25,13 +25,14 @@
#pragma once
+#include "texteditor_global.h"
+
#include <QString>
#include <QStringList>
-namespace Beautifier {
-namespace Internal {
+namespace TextEditor {
-class Command
+class TEXTEDITOR_EXPORT Command
{
public:
enum Processing {
@@ -64,5 +65,4 @@ private:
bool m_returnsCRLF = false;
};
-} // namespace Internal
-} // namespace Beautifier
+} // namespace TextEditor
diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp
new file mode 100644
index 0000000000..d38c863cca
--- /dev/null
+++ b/src/plugins/texteditor/formattexteditor.cpp
@@ -0,0 +1,364 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "formattexteditor.h"
+
+#include "textdocument.h"
+#include "textdocumentlayout.h"
+#include "texteditor.h"
+
+#include <coreplugin/messagemanager.h>
+
+#include <utils/differ.h>
+#include <utils/runextensions.h>
+#include <utils/synchronousprocess.h>
+#include <utils/temporarydirectory.h>
+#include <utils/textutils.h>
+#include <utils/qtcassert.h>
+
+#include <QFileInfo>
+#include <QFutureWatcher>
+#include <QScrollBar>
+#include <QTextBlock>
+
+using namespace Utils;
+
+namespace TextEditor {
+
+void formatCurrentFile(const Command &command, int startPos, int endPos)
+{
+ if (TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget())
+ formatEditorAsync(editor, command, startPos, endPos);
+}
+
+static QString sourceData(TextEditorWidget *editor, int startPos, int endPos)
+{
+ return (startPos < 0)
+ ? editor->toPlainText()
+ : Utils::Text::textAt(editor->textCursor(), startPos, (endPos - startPos));
+}
+
+static FormatTask format(FormatTask task)
+{
+ task.error.clear();
+ task.formattedData.clear();
+
+ const QString executable = task.command.executable();
+ if (executable.isEmpty())
+ return task;
+
+ switch (task.command.processing()) {
+ case Command::FileProcessing: {
+ // Save text to temporary file
+ const QFileInfo fi(task.filePath);
+ Utils::TempFileSaver sourceFile(Utils::TemporaryDirectory::masterDirectoryPath()
+ + "/qtc_beautifier_XXXXXXXX."
+ + fi.suffix());
+ sourceFile.setAutoRemove(true);
+ sourceFile.write(task.sourceData.toUtf8());
+ if (!sourceFile.finalize()) {
+ task.error = QString(QT_TRANSLATE_NOOP("TextEditor",
+ "Cannot create temporary file \"%1\": %2."))
+ .arg(sourceFile.fileName(), sourceFile.errorString());
+ return task;
+ }
+
+ // Format temporary file
+ QStringList options = task.command.options();
+ options.replaceInStrings(QLatin1String("%file"), sourceFile.fileName());
+ Utils::SynchronousProcess process;
+ process.setTimeoutS(5);
+ Utils::SynchronousProcessResponse response = process.runBlocking(executable, options);
+ if (response.result != Utils::SynchronousProcessResponse::Finished) {
+ task.error = QString(QT_TRANSLATE_NOOP("TextEditor", "Failed to format: %1."))
+ .arg(response.exitMessage(executable, 5));
+ return task;
+ }
+ const QString output = response.stdErr();
+ if (!output.isEmpty())
+ task.error = executable + QLatin1String(": ") + output;
+
+ // Read text back
+ Utils::FileReader reader;
+ if (!reader.fetch(sourceFile.fileName(), QIODevice::Text)) {
+ task.error = QString(QT_TRANSLATE_NOOP("TextEditor", "Cannot read file \"%1\": %2."))
+ .arg(sourceFile.fileName(), reader.errorString());
+ return task;
+ }
+ task.formattedData = QString::fromUtf8(reader.data());
+ }
+ return task;
+
+ case Command::PipeProcessing: {
+ QProcess process;
+ QStringList options = task.command.options();
+ options.replaceInStrings("%filename", QFileInfo(task.filePath).fileName());
+ options.replaceInStrings("%file", task.filePath);
+ process.start(executable, options);
+ if (!process.waitForStarted(3000)) {
+ task.error = QString(QT_TRANSLATE_NOOP("TextEditor",
+ "Cannot call %1 or some other error occurred."))
+ .arg(executable);
+ return task;
+ }
+ process.write(task.sourceData.toUtf8());
+ process.closeWriteChannel();
+ if (!process.waitForFinished(5000) && process.state() == QProcess::Running) {
+ process.kill();
+ task.error = QString(QT_TRANSLATE_NOOP("TextEditor",
+ "Cannot call %1 or some other error occurred. Timeout "
+ "reached while formatting file %2."))
+ .arg(executable, task.filePath);
+ return task;
+ }
+ const QByteArray errorText = process.readAllStandardError();
+ if (!errorText.isEmpty()) {
+ task.error = QString::fromLatin1("%1: %2").arg(executable,
+ QString::fromUtf8(errorText));
+ return task;
+ }
+
+ task.formattedData = QString::fromUtf8(process.readAllStandardOutput());
+
+ if (task.command.pipeAddsNewline() && task.formattedData.endsWith('\n')) {
+ task.formattedData.chop(1);
+ if (task.formattedData.endsWith('\r'))
+ task.formattedData.chop(1);
+ }
+ if (task.command.returnsCRLF())
+ task.formattedData.replace("\r\n", "\n");
+
+ return task;
+ }
+ }
+
+ return task;
+}
+
+/**
+ * Sets the text of @a editor to @a text. Instead of replacing the entire text, however, only the
+ * actually changed parts are updated while preserving the cursor position, the folded
+ * blocks, and the scroll bar position.
+ */
+static void updateEditorText(QPlainTextEdit *editor, const QString &text)
+{
+ const QString editorText = editor->toPlainText();
+ if (editorText == text)
+ return;
+
+ // Calculate diff
+ Differ differ;
+ const QList<Diff> diff = differ.diff(editorText, text);
+
+ // Since QTextCursor does not work properly with folded blocks, all blocks must be unfolded.
+ // To restore the current state at the end, keep track of which block is folded.
+ QList<int> foldedBlocks;
+ QTextBlock block = editor->document()->firstBlock();
+ while (block.isValid()) {
+ if (const TextBlockUserData *userdata = static_cast<TextBlockUserData *>(block.userData())) {
+ if (userdata->folded()) {
+ foldedBlocks << block.blockNumber();
+ TextDocumentLayout::doFoldOrUnfold(block, true);
+ }
+ }
+ block = block.next();
+ }
+ editor->update();
+
+ // Save the current viewport position of the cursor to ensure the same vertical position after
+ // the formatted text has set to the editor.
+ int absoluteVerticalCursorOffset = editor->cursorRect().y();
+
+ // Update changed lines and keep track of the cursor position
+ QTextCursor cursor = editor->textCursor();
+ int charactersInfrontOfCursor = cursor.position();
+ int newCursorPos = charactersInfrontOfCursor;
+ cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
+ for (const Diff &d : diff) {
+ switch (d.command) {
+ case Diff::Insert:
+ {
+ // Adjust cursor position if we do work in front of the cursor.
+ if (charactersInfrontOfCursor > 0) {
+ const int size = d.text.size();
+ charactersInfrontOfCursor += size;
+ newCursorPos += size;
+ }
+ // Adjust folded blocks, if a new block is added.
+ if (d.text.contains('\n')) {
+ const int newLineCount = d.text.count('\n');
+ const int number = cursor.blockNumber();
+ const int total = foldedBlocks.size();
+ for (int i = 0; i < total; ++i) {
+ if (foldedBlocks.at(i) > number)
+ foldedBlocks[i] += newLineCount;
+ }
+ }
+ cursor.insertText(d.text);
+ break;
+ }
+
+ case Diff::Delete:
+ {
+ // Adjust cursor position if we do work in front of the cursor.
+ if (charactersInfrontOfCursor > 0) {
+ const int size = d.text.size();
+ charactersInfrontOfCursor -= size;
+ newCursorPos -= size;
+ // Cursor was inside the deleted text, so adjust the new cursor position
+ if (charactersInfrontOfCursor < 0)
+ newCursorPos -= charactersInfrontOfCursor;
+ }
+ // Adjust folded blocks, if at least one block is being deleted.
+ if (d.text.contains('\n')) {
+ const int newLineCount = d.text.count('\n');
+ const int number = cursor.blockNumber();
+ for (int i = 0, total = foldedBlocks.size(); i < total; ++i) {
+ if (foldedBlocks.at(i) > number) {
+ foldedBlocks[i] -= newLineCount;
+ if (foldedBlocks[i] < number) {
+ foldedBlocks.removeAt(i);
+ --i;
+ --total;
+ }
+ }
+ }
+ }
+ cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, d.text.size());
+ cursor.removeSelectedText();
+ break;
+ }
+
+ case Diff::Equal:
+ // Adjust cursor position
+ charactersInfrontOfCursor -= d.text.size();
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, d.text.size());
+ break;
+ }
+ }
+ cursor.endEditBlock();
+ cursor.setPosition(newCursorPos);
+ editor->setTextCursor(cursor);
+
+ // Adjust vertical scrollbar
+ absoluteVerticalCursorOffset = editor->cursorRect().y() - absoluteVerticalCursorOffset;
+ const double fontHeight = QFontMetrics(editor->document()->defaultFont()).height();
+ editor->verticalScrollBar()->setValue(editor->verticalScrollBar()->value()
+ + absoluteVerticalCursorOffset / fontHeight);
+ // Restore folded blocks
+ const QTextDocument *doc = editor->document();
+ for (int blockId : foldedBlocks) {
+ const QTextBlock block = doc->findBlockByNumber(qMax(0, blockId));
+ if (block.isValid())
+ TextDocumentLayout::doFoldOrUnfold(block, false);
+ }
+
+ editor->document()->setModified(true);
+}
+
+static void showError(const QString &error)
+{
+ Core::MessageManager::write(
+ QString(QT_TRANSLATE_NOOP("TextEditor", "Error in text formatting: %1"))
+ .arg(error.trimmed()));
+}
+
+/**
+ * Checks the state of @a task and if the formatting was successful calls updateEditorText() with
+ * the respective members of @a task.
+ */
+void checkAndApplyTask(const FormatTask &task)
+{
+ if (!task.error.isEmpty()) {
+ showError(task.error);
+ return;
+ }
+
+ if (task.formattedData.isEmpty()) {
+ showError(QString(QT_TRANSLATE_NOOP("TextEditor", "Could not format file %1.")).arg(
+ task.filePath));
+ return;
+ }
+
+ QPlainTextEdit *textEditor = task.editor;
+ if (!textEditor) {
+ showError(QString(QT_TRANSLATE_NOOP("TextEditor", "File %1 was closed.")).arg(
+ task.filePath));
+ return;
+ }
+
+ const QString formattedData = (task.startPos < 0)
+ ? task.formattedData
+ : QString(textEditor->toPlainText()).replace(
+ task.startPos, (task.endPos - task.startPos), task.formattedData);
+
+ updateEditorText(textEditor, formattedData);
+}
+
+/**
+ * Formats the text of @a editor using @a command. @a startPos and @a endPos specifies the range of
+ * the editor's text that will be formatted. If @a startPos is negative the editor's entire text is
+ * formatted.
+ *
+ * @pre @a endPos must be greater than or equal to @a startPos
+ */
+void formatEditor(TextEditorWidget *editor, const Command &command, int startPos, int endPos)
+{
+ QTC_ASSERT(startPos <= endPos, return);
+
+ const QString sd = sourceData(editor, startPos, endPos);
+ if (sd.isEmpty())
+ return;
+ checkAndApplyTask(format(FormatTask(editor, editor->textDocument()->filePath().toString(), sd,
+ command, startPos, endPos)));
+}
+
+/**
+ * Behaves like formatEditor except that the formatting is done asynchronously.
+ */
+void formatEditorAsync(TextEditorWidget *editor, const Command &command, int startPos, int endPos)
+{
+ QTC_ASSERT(startPos <= endPos, return);
+
+ const QString sd = sourceData(editor, startPos, endPos);
+ if (sd.isEmpty())
+ return;
+
+ auto *watcher = new QFutureWatcher<FormatTask>;
+ const TextDocument *doc = editor->textDocument();
+ QObject::connect(doc, &TextDocument::contentsChanged, watcher, &QFutureWatcher<FormatTask>::cancel);
+ QObject::connect(watcher, &QFutureWatcherBase::finished, [watcher] {
+ if (watcher->isCanceled())
+ showError(QString(QT_TRANSLATE_NOOP("TextEditor", "File was modified.")));
+ else
+ checkAndApplyTask(watcher->result());
+ watcher->deleteLater();
+ });
+ watcher->setFuture(Utils::runAsync(&format, FormatTask(editor, doc->filePath().toString(), sd,
+ command, startPos, endPos)));
+}
+
+} // namespace TextEditor
diff --git a/src/plugins/texteditor/formattexteditor.h b/src/plugins/texteditor/formattexteditor.h
new file mode 100644
index 0000000000..9e8ab26611
--- /dev/null
+++ b/src/plugins/texteditor/formattexteditor.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "texteditor_global.h"
+
+#include "command.h"
+
+#include <QPlainTextEdit>
+#include <QPointer>
+
+namespace TextEditor {
+
+class TextEditorWidget;
+
+class TEXTEDITOR_EXPORT FormatTask
+{
+public:
+ FormatTask(QPlainTextEdit *_editor, const QString &_filePath, const QString &_sourceData,
+ const Command &_command, int _startPos = -1, int _endPos = 0) :
+ editor(_editor),
+ filePath(_filePath),
+ sourceData(_sourceData),
+ command(_command),
+ startPos(_startPos),
+ endPos(_endPos) {}
+
+ QPointer<QPlainTextEdit> editor;
+ QString filePath;
+ QString sourceData;
+ TextEditor::Command command;
+ int startPos = -1;
+ int endPos = 0;
+ QString formattedData;
+ QString error;
+};
+
+TEXTEDITOR_EXPORT void formatCurrentFile(const TextEditor::Command &command, int startPos = -1, int endPos = 0);
+TEXTEDITOR_EXPORT void formatEditor(TextEditorWidget *editor, const TextEditor::Command &command,
+ int startPos = -1, int endPos = 0);
+TEXTEDITOR_EXPORT void formatEditorAsync(TextEditorWidget *editor, const TextEditor::Command &command,
+ int startPos = -1, int endPos = 0);
+
+} // namespace TextEditor
diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro
index 508acb78a7..9b698b7340 100644
--- a/src/plugins/texteditor/texteditor.pro
+++ b/src/plugins/texteditor/texteditor.pro
@@ -101,7 +101,9 @@ SOURCES += texteditorplugin.cpp \
codeassist/keywordscompletionassist.cpp \
completionsettingspage.cpp \
commentssettings.cpp \
- marginsettings.cpp
+ marginsettings.cpp \
+ formattexteditor.cpp \
+ command.cpp
HEADERS += texteditorplugin.h \
plaintexteditorfactory.h \
@@ -214,7 +216,9 @@ HEADERS += texteditorplugin.h \
blockrange.h \
completionsettingspage.h \
commentssettings.h \
- textstyles.h
+ textstyles.h \
+ formattexteditor.h \
+ command.h
FORMS += \
displaysettingspage.ui \
diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs
index 81bb85c0a3..052a8f13fb 100644
--- a/src/plugins/texteditor/texteditor.qbs
+++ b/src/plugins/texteditor/texteditor.qbs
@@ -51,6 +51,8 @@ Project {
"colorschemeedit.cpp",
"colorschemeedit.h",
"colorschemeedit.ui",
+ "command.cpp",
+ "command.h",
"commentssettings.cpp",
"commentssettings.h",
"completionsettings.cpp",
@@ -76,6 +78,8 @@ Project {
"fontsettingspage.cpp",
"fontsettingspage.h",
"fontsettingspage.ui",
+ "formattexteditor.cpp",
+ "formattexteditor.h",
"helpitem.cpp",
"helpitem.h",
"highlighterutils.cpp",