forked from qt-creator/qt-creator
TextEditor: Split FormatText into input and output structs
Remove output data from FormatText and rename it into FormatInput. Use Utils::expected_str<QString> for output data and alias it with FormatOutput. Change-Id: I2cf21d73d2e973eeeb4431ae30b56bb5987986f6 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -12,8 +12,9 @@
|
|||||||
|
|
||||||
#include <utils/async.h>
|
#include <utils/async.h>
|
||||||
#include <utils/differ.h>
|
#include <utils/differ.h>
|
||||||
#include <utils/qtcprocess.h>
|
#include <utils/expected.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/temporarydirectory.h>
|
#include <utils/temporarydirectory.h>
|
||||||
#include <utils/textutils.h>
|
#include <utils/textutils.h>
|
||||||
|
|
||||||
@@ -27,18 +28,17 @@ using namespace std::chrono_literals;
|
|||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
|
|
||||||
class FormatTask
|
struct FormatInput
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
Utils::FilePath filePath;
|
Utils::FilePath filePath;
|
||||||
QString sourceData;
|
QString sourceData;
|
||||||
TextEditor::Command command;
|
TextEditor::Command command;
|
||||||
int startPos = -1;
|
int startPos = -1;
|
||||||
int endPos = 0;
|
int endPos = 0;
|
||||||
QString formattedData = {};
|
|
||||||
QString error = {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using FormatOutput = expected_str<QString>;
|
||||||
|
|
||||||
void formatCurrentFile(const Command &command, int startPos, int endPos)
|
void formatCurrentFile(const Command &command, int startPos, int endPos)
|
||||||
{
|
{
|
||||||
if (TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget())
|
if (TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget())
|
||||||
@@ -52,89 +52,80 @@ static QString sourceData(TextEditorWidget *editor, int startPos, int endPos)
|
|||||||
: Utils::Text::textAt(editor->textCursor(), startPos, (endPos - startPos));
|
: Utils::Text::textAt(editor->textCursor(), startPos, (endPos - startPos));
|
||||||
}
|
}
|
||||||
|
|
||||||
static FormatTask format(FormatTask task)
|
static FormatOutput format(const FormatInput &input)
|
||||||
{
|
{
|
||||||
task.error.clear();
|
const FilePath executable = input.command.executable();
|
||||||
task.formattedData.clear();
|
|
||||||
|
|
||||||
const FilePath executable = task.command.executable();
|
|
||||||
if (executable.isEmpty())
|
if (executable.isEmpty())
|
||||||
return task;
|
return {};
|
||||||
|
|
||||||
switch (task.command.processing()) {
|
switch (input.command.processing()) {
|
||||||
case Command::FileProcessing: {
|
case Command::FileProcessing: {
|
||||||
// Save text to temporary file
|
// Save text to temporary file
|
||||||
Utils::TempFileSaver sourceFile(Utils::TemporaryDirectory::masterDirectoryPath()
|
Utils::TempFileSaver sourceFile(Utils::TemporaryDirectory::masterDirectoryPath()
|
||||||
+ "/qtc_beautifier_XXXXXXXX."
|
+ "/qtc_beautifier_XXXXXXXX." + input.filePath.suffix());
|
||||||
+ task.filePath.suffix());
|
|
||||||
sourceFile.setAutoRemove(true);
|
sourceFile.setAutoRemove(true);
|
||||||
sourceFile.write(task.sourceData.toUtf8());
|
sourceFile.write(input.sourceData.toUtf8());
|
||||||
if (!sourceFile.finalize()) {
|
if (!sourceFile.finalize()) {
|
||||||
task.error = Tr::tr("Cannot create temporary file \"%1\": %2.")
|
return Utils::make_unexpected(Tr::tr("Cannot create temporary file \"%1\": %2.")
|
||||||
.arg(sourceFile.filePath().toUserOutput(), sourceFile.errorString());
|
.arg(sourceFile.filePath().toUserOutput(), sourceFile.errorString()));
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format temporary file
|
// Format temporary file
|
||||||
QStringList options = task.command.options();
|
QStringList options = input.command.options();
|
||||||
options.replaceInStrings(QLatin1String("%file"), sourceFile.filePath().toString());
|
options.replaceInStrings(QLatin1String("%file"), sourceFile.filePath().toString());
|
||||||
Process process;
|
Process process;
|
||||||
process.setCommand({executable, options});
|
process.setCommand({executable, options});
|
||||||
process.runBlocking(5s);
|
process.runBlocking(5s);
|
||||||
if (process.result() != ProcessResult::FinishedWithSuccess) {
|
if (process.result() != ProcessResult::FinishedWithSuccess) {
|
||||||
task.error = Tr::tr("Failed to format: %1.").arg(process.exitMessage());
|
return Utils::make_unexpected(Tr::tr("Failed to format: %1.")
|
||||||
return task;
|
.arg(process.exitMessage()));
|
||||||
}
|
}
|
||||||
const QString output = process.cleanedStdErr();
|
const QString output = process.cleanedStdErr();
|
||||||
if (!output.isEmpty())
|
if (!output.isEmpty())
|
||||||
task.error = executable.toUserOutput() + ": " + output;
|
return Utils::make_unexpected(executable.toUserOutput() + ": " + output);
|
||||||
|
|
||||||
// Read text back
|
// Read text back
|
||||||
Utils::FileReader reader;
|
Utils::FileReader reader;
|
||||||
if (!reader.fetch(sourceFile.filePath(), QIODevice::Text)) {
|
if (!reader.fetch(sourceFile.filePath(), QIODevice::Text)) {
|
||||||
task.error = Tr::tr("Cannot read file \"%1\": %2.")
|
return Utils::make_unexpected(Tr::tr("Cannot read file \"%1\": %2.")
|
||||||
.arg(sourceFile.filePath().toUserOutput(), reader.errorString());
|
.arg(sourceFile.filePath().toUserOutput(), reader.errorString()));
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
task.formattedData = QString::fromUtf8(reader.data());
|
return QString::fromUtf8(reader.data());
|
||||||
}
|
}
|
||||||
return task;
|
|
||||||
|
|
||||||
case Command::PipeProcessing: {
|
case Command::PipeProcessing: {
|
||||||
Process process;
|
Process process;
|
||||||
QStringList options = task.command.options();
|
QStringList options = input.command.options();
|
||||||
options.replaceInStrings("%filename", task.filePath.fileName());
|
options.replaceInStrings("%filename", input.filePath.fileName());
|
||||||
options.replaceInStrings("%file", task.filePath.toString());
|
options.replaceInStrings("%file", input.filePath.toString());
|
||||||
process.setCommand({executable, options});
|
process.setCommand({executable, options});
|
||||||
process.setWriteData(task.sourceData.toUtf8());
|
process.setWriteData(input.sourceData.toUtf8());
|
||||||
process.start();
|
process.start();
|
||||||
if (!process.waitForFinished(5s)) {
|
if (!process.waitForFinished(5s)) {
|
||||||
task.error = Tr::tr("Cannot call %1 or some other error occurred. Timeout "
|
return Utils::make_unexpected(Tr::tr("Cannot call %1 or some other error occurred. "
|
||||||
"reached while formatting file %2.")
|
"Timeout reached while formatting file %2.")
|
||||||
.arg(executable.toUserOutput(), task.filePath.displayName());
|
.arg(executable.toUserOutput(), input.filePath.displayName()));
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
const QString errorText = process.readAllStandardError();
|
const QString errorText = process.readAllStandardError();
|
||||||
if (!errorText.isEmpty()) {
|
if (!errorText.isEmpty()) {
|
||||||
task.error = QString("%1: %2").arg(executable.toUserOutput(), errorText);
|
return Utils::make_unexpected(QString("%1: %2").arg(executable.toUserOutput(),
|
||||||
return task;
|
errorText));
|
||||||
}
|
}
|
||||||
|
|
||||||
task.formattedData = process.readAllStandardOutput();
|
QString formattedData = process.readAllStandardOutput();
|
||||||
|
|
||||||
if (task.command.pipeAddsNewline() && task.formattedData.endsWith('\n')) {
|
if (input.command.pipeAddsNewline() && formattedData.endsWith('\n')) {
|
||||||
task.formattedData.chop(1);
|
formattedData.chop(1);
|
||||||
if (task.formattedData.endsWith('\r'))
|
if (formattedData.endsWith('\r'))
|
||||||
task.formattedData.chop(1);
|
formattedData.chop(1);
|
||||||
}
|
}
|
||||||
if (task.command.returnsCRLF())
|
if (input.command.returnsCRLF())
|
||||||
task.formattedData.replace("\r\n", "\n");
|
formattedData.replace("\r\n", "\n");
|
||||||
|
|
||||||
return task;
|
return formattedData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,28 +259,26 @@ static void showError(const QString &error)
|
|||||||
* Checks the state of @a task and if the formatting was successful calls updateEditorText() with
|
* Checks the state of @a task and if the formatting was successful calls updateEditorText() with
|
||||||
* the respective members of @a task.
|
* the respective members of @a task.
|
||||||
*/
|
*/
|
||||||
static void checkAndApplyTask(const QPointer<QPlainTextEdit> &textEditor, const FormatTask &task)
|
static void checkAndApplyTask(const QPointer<QPlainTextEdit> &textEditor, const FormatInput &input,
|
||||||
|
const FormatOutput &output)
|
||||||
{
|
{
|
||||||
if (!task.error.isEmpty()) {
|
if (!output.has_value()) {
|
||||||
showError(task.error);
|
showError(output.error());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.formattedData.isEmpty()) {
|
if (output->isEmpty()) {
|
||||||
showError(Tr::tr("Could not format file %1.").arg(task.filePath.displayName()));
|
showError(Tr::tr("Could not format file %1.").arg(input.filePath.displayName()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!textEditor) {
|
if (!textEditor) {
|
||||||
showError(Tr::tr("File %1 was closed.").arg(task.filePath.displayName()));
|
showError(Tr::tr("File %1 was closed.").arg(input.filePath.displayName()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString formattedData = (task.startPos < 0)
|
const QString formattedData = (input.startPos < 0) ? *output
|
||||||
? task.formattedData
|
: textEditor->toPlainText().replace(input.startPos, (input.endPos - input.startPos), *output);
|
||||||
: QString(textEditor->toPlainText()).replace(
|
|
||||||
task.startPos, (task.endPos - task.startPos), task.formattedData);
|
|
||||||
|
|
||||||
updateEditorText(textEditor, formattedData);
|
updateEditorText(textEditor, formattedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,8 +296,8 @@ void formatEditor(TextEditorWidget *editor, const Command &command, int startPos
|
|||||||
const QString sd = sourceData(editor, startPos, endPos);
|
const QString sd = sourceData(editor, startPos, endPos);
|
||||||
if (sd.isEmpty())
|
if (sd.isEmpty())
|
||||||
return;
|
return;
|
||||||
checkAndApplyTask(editor,
|
const FormatInput input{editor->textDocument()->filePath(), sd, command, startPos, endPos};
|
||||||
format({editor->textDocument()->filePath(), sd, command, startPos, endPos}));
|
checkAndApplyTask(editor, input, format(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -322,19 +311,20 @@ void formatEditorAsync(TextEditorWidget *editor, const Command &command, int sta
|
|||||||
if (sd.isEmpty())
|
if (sd.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto watcher = new QFutureWatcher<FormatTask>;
|
auto watcher = new QFutureWatcher<FormatOutput>;
|
||||||
const TextDocument *doc = editor->textDocument();
|
const TextDocument *doc = editor->textDocument();
|
||||||
QObject::connect(doc, &TextDocument::contentsChanged, watcher, &QFutureWatcher<FormatTask>::cancel);
|
const FormatInput input{doc->filePath(), sd, command, startPos, endPos};
|
||||||
QObject::connect(watcher, &QFutureWatcherBase::finished,
|
QObject::connect(doc, &TextDocument::contentsChanged, watcher,
|
||||||
[watcher, editor = QPointer<QPlainTextEdit>(editor)] {
|
&QFutureWatcher<FormatOutput>::cancel);
|
||||||
|
QObject::connect(watcher, &QFutureWatcherBase::finished, watcher,
|
||||||
|
[watcher, editor = QPointer<QPlainTextEdit>(editor), input] {
|
||||||
if (watcher->isCanceled())
|
if (watcher->isCanceled())
|
||||||
showError(Tr::tr("File was modified."));
|
showError(Tr::tr("File was modified."));
|
||||||
else
|
else
|
||||||
checkAndApplyTask(editor, watcher->result());
|
checkAndApplyTask(editor, input, watcher->result());
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
});
|
});
|
||||||
watcher->setFuture(
|
watcher->setFuture(Utils::asyncRun(&format, input));
|
||||||
Utils::asyncRun(&format, FormatTask{doc->filePath(), sd, command, startPos, endPos}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace TextEditor
|
} // namespace TextEditor
|
||||||
|
Reference in New Issue
Block a user