From 5c658ac96854a4ffd5c546f5391cd4b64b4b75cd Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Wed, 29 Aug 2018 14:36:47 +0200 Subject: [PATCH] Beautifier: Move formatting tools to TextEditor Formatting is moved from Beautifier plugin to formattexteditor.h/.cpp. Diff and Differ classes are extracted from DiffEditor to Utils to prevent extra TextEditor dependencies. This change will make possible to use formatCurrentFile and similar functions not only from Beautifier code. Change-Id: Ic5ca668afe88f4e9376d49e6bd3594807172b0dd Reviewed-by: Eike Ziller --- .../diffeditor => libs/utils}/differ.cpp | 4 +- .../diffeditor => libs/utils}/differ.h | 10 +- src/libs/utils/utils-lib.pri | 6 +- src/libs/utils/utils.qbs | 2 + .../artisticstyle/artisticstyle.cpp | 5 +- .../beautifier/artisticstyle/artisticstyle.h | 4 +- src/plugins/beautifier/beautifier.pro | 2 - src/plugins/beautifier/beautifier.qbs | 2 - .../beautifier/beautifierabstracttool.h | 4 +- src/plugins/beautifier/beautifierplugin.cpp | 339 +--------------- src/plugins/beautifier/beautifierplugin.h | 5 +- .../beautifier/clangformat/clangformat.cpp | 17 +- .../beautifier/clangformat/clangformat.h | 4 +- .../beautifier/uncrustify/uncrustify.cpp | 10 +- .../beautifier/uncrustify/uncrustify.h | 4 +- src/plugins/diffeditor/diffeditor.pro | 2 - src/plugins/diffeditor/diffeditor.qbs | 2 - src/plugins/diffeditor/diffeditorplugin.cpp | 3 +- src/plugins/diffeditor/diffutils.cpp | 8 +- src/plugins/diffeditor/diffutils.h | 8 +- .../{beautifier => texteditor}/command.cpp | 6 +- .../{beautifier => texteditor}/command.h | 10 +- src/plugins/texteditor/formattexteditor.cpp | 364 ++++++++++++++++++ src/plugins/texteditor/formattexteditor.h | 67 ++++ src/plugins/texteditor/texteditor.pro | 8 +- src/plugins/texteditor/texteditor.qbs | 4 + 26 files changed, 503 insertions(+), 397 deletions(-) rename src/{plugins/diffeditor => libs/utils}/differ.cpp (99%) rename src/{plugins/diffeditor => libs/utils}/differ.h (97%) rename src/plugins/{beautifier => texteditor}/command.cpp (95%) rename src/plugins/{beautifier => texteditor}/command.h (94%) create mode 100644 src/plugins/texteditor/formattexteditor.cpp create mode 100644 src/plugins/texteditor/formattexteditor.h diff --git a/src/plugins/diffeditor/differ.cpp b/src/libs/utils/differ.cpp similarity index 99% rename from src/plugins/diffeditor/differ.cpp rename to src/libs/utils/differ.cpp index b810eeff62d..cff9dd21dbf 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 #include -namespace DiffEditor { +namespace Utils { static int commonPrefix(const QString &text1, const QString &text2) { @@ -1555,4 +1555,4 @@ QList Differ::cleanupSemanticsLossless(const QList &diffList) return newDiffList; } -} // namespace DiffEditor +} // namespace Utils diff --git a/src/plugins/diffeditor/differ.h b/src/libs/utils/differ.h similarity index 97% rename from src/plugins/diffeditor/differ.h rename to src/libs/utils/differ.h index 14012cc10f9..11eebfe5270 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 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 41d8745baac..3369e0e1618 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 2ada52c17ee..40f7a21ca42 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 40e5cb20cbf..4a7ee4ec549 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp @@ -44,12 +44,15 @@ #include #include #include +#include #include #include #include #include +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 2dbcca4bec1..59278144896 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 6dcdbf6e695..65021fd8be2 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 35c5a17fc81..1a71cb00989 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 9253a6d9107..813f37ac7fb 100644 --- a/src/plugins/beautifier/beautifierabstracttool.h +++ b/src/plugins/beautifier/beautifierabstracttool.h @@ -25,7 +25,7 @@ #pragma once -#include "command.h" +#include #include @@ -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 fa973c717ae..3883a790446 100644 --- a/src/plugins/beautifier/beautifierplugin.cpp +++ b/src/plugins/beautifier/beautifierplugin.cpp @@ -42,9 +42,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -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 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 &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 m_generalSettings; - QHash m_autoFormatConnections; ArtisticStyle::ArtisticStyle artisticStyleBeautifier; ClangFormat::ClangFormat clangFormatBeautifier; @@ -304,221 +175,17 @@ 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 editors = Core::DocumentModel::editorsForDocument(document); if (editors.isEmpty()) return; if (TextEditorWidget* widget = qobject_cast(editors.first()->widget())) - formatEditor(widget, command); + TextEditor::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 *watcher = new QFutureWatcher; - const TextDocument *doc = editor->textDocument(); - connect(doc, &TextDocument::contentsChanged, watcher, &QFutureWatcher::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; - } - - 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 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 foldedBlocks; - QTextBlock block = editor->document()->firstBlock(); - while (block.isValid()) { - if (const TextBlockUserData *userdata = static_cast(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) { Core::MessageManager::write(tr("Error in Beautifier: %1").arg(error.trimmed())); diff --git a/src/plugins/beautifier/beautifierplugin.h b/src/plugins/beautifier/beautifierplugin.h index 4b55eab80c1..c35ef2ab9a3 100644 --- a/src/plugins/beautifier/beautifierplugin.h +++ b/src/plugins/beautifier/beautifierplugin.h @@ -25,9 +25,8 @@ #pragma once -#include "command.h" - #include +#include 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 ce9d69c9d3b..ee2e9ad7700 100644 --- a/src/plugins/beautifier/clangformat/clangformat.cpp +++ b/src/plugins/beautifier/clangformat/clangformat.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,8 @@ #include #include +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 2da33b81906..4d69d2e9e5d 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 acba89331bf..daeffdf2dfa 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp @@ -44,12 +44,15 @@ #include #include #include +#include #include #include #include #include +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 3bdeffacc04..e35434fc811 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 5c8c9044956..235f4e9dcde 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 8d0a1a1820e..9c6a41c3773 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 4411c84c679..6d3ba8858d8 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 #include @@ -50,10 +49,12 @@ #include #include +#include #include #include 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 e2a1831251d..e233173629f 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 +#include #include #include #include #include +using namespace Utils; + namespace DiffEditor { static QList assemblyRows(const QList &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 &leftDiffList, - const QList &rightDiffList) + const QList &rightDiffList) { int i = 0; int j = 0; diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h index d057a38e2fe..31956e62968 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 &leftDiffList, - const QList &rightDiffList); + static ChunkData calculateOriginalData(const QList &leftDiffList, + const QList &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 similarity index 95% rename from src/plugins/beautifier/command.cpp rename to src/plugins/texteditor/command.cpp index 6263ae9b52c..b709334590e 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 similarity index 94% rename from src/plugins/beautifier/command.h rename to src/plugins/texteditor/command.h index f81880fb3d2..dcb2f6f23af 100644 --- a/src/plugins/beautifier/command.h +++ b/src/plugins/texteditor/command.h @@ -25,13 +25,14 @@ #pragma once +#include "texteditor_global.h" + #include #include -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 00000000000..d38c863cca6 --- /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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 = 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 foldedBlocks; + QTextBlock block = editor->document()->firstBlock(); + while (block.isValid()) { + if (const TextBlockUserData *userdata = static_cast(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; + const TextDocument *doc = editor->textDocument(); + QObject::connect(doc, &TextDocument::contentsChanged, watcher, &QFutureWatcher::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 00000000000..9e8ab266115 --- /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 +#include + +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 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 508acb78a72..9b698b73409 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 81bb85c0a3d..052a8f13fbe 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",