1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "externaltool.h"
27 #include "externaltoolmanager.h"
28
29 #include "icore.h"
30 #include "idocument.h"
31 #include "messagemanager.h"
32 #include "documentmanager.h"
33 #include "editormanager/editormanager.h"
34 #include "editormanager/ieditor.h"
35
36 #include <app/app_version.h>
37
38 #include <utils/algorithm.h>
39 #include <utils/fileutils.h>
40 #include <utils/macroexpander.h>
41 #include <utils/qtcassert.h>
42 #include <utils/qtcprocess.h>
43
44 #include <QCoreApplication>
45 #include <QDateTime>
46 #include <QFileInfo>
47 #include <QXmlStreamReader>
48 #include <QXmlStreamWriter>
49
50 using namespace Utils;
51 using namespace Core::Internal;
52
53 namespace Core {
54 namespace Internal {
55
56 const char kExternalTool[] = "externaltool";
57 const char kId[] = "id";
58 const char kDescription[] = "description";
59 const char kDisplayName[] = "displayname";
60 const char kCategory[] = "category";
61 const char kOrder[] = "order";
62 const char kExecutable[] = "executable";
63 const char kPath[] = "path";
64 const char kArguments[] = "arguments";
65 const char kInput[] = "input";
66 const char kWorkingDirectory[] = "workingdirectory";
67 const char kBaseEnvironmentId[] = "baseEnvironmentId";
68 const char kEnvironment[] = "environment";
69
70 const char kXmlLang[] = "xml:lang";
71 const char kOutput[] = "output";
72 const char kError[] = "error";
73 const char kOutputShowInPane[] = "showinpane";
74 const char kOutputReplaceSelection[] = "replaceselection";
75 const char kOutputIgnore[] = "ignore";
76 const char kModifiesDocument[] = "modifiesdocument";
77 const char kYes[] = "yes";
78 const char kNo[] = "no";
79 const char kTrue[] = "true";
80 const char kFalse[] = "false";
81
82 // #pragma mark -- ExternalTool
83
ExternalTool()84 ExternalTool::ExternalTool() :
85 m_displayCategory("") // difference between isNull and isEmpty
86 {
87 }
88
ExternalTool(const ExternalTool * other)89 ExternalTool::ExternalTool(const ExternalTool *other)
90 : m_id(other->m_id),
91 m_description(other->m_description),
92 m_displayName(other->m_displayName),
93 m_displayCategory(other->m_displayCategory),
94 m_order(other->m_order),
95 m_executables(other->m_executables),
96 m_arguments(other->m_arguments),
97 m_input(other->m_input),
98 m_workingDirectory(other->m_workingDirectory),
99 m_baseEnvironmentProviderId(other->m_baseEnvironmentProviderId),
100 m_environment(other->m_environment),
101 m_outputHandling(other->m_outputHandling),
102 m_errorHandling(other->m_errorHandling),
103 m_modifiesCurrentDocument(other->m_modifiesCurrentDocument),
104 m_fileName(other->m_fileName),
105 m_presetTool(other->m_presetTool)
106 {
107 }
108
operator =(const ExternalTool & other)109 ExternalTool &ExternalTool::operator=(const ExternalTool &other)
110 {
111 m_id = other.m_id;
112 m_description = other.m_description;
113 m_displayName = other.m_displayName;
114 m_displayCategory = other.m_displayCategory;
115 m_order = other.m_order;
116 m_executables = other.m_executables;
117 m_arguments = other.m_arguments;
118 m_input = other.m_input;
119 m_workingDirectory = other.m_workingDirectory;
120 m_environment = other.m_environment;
121 m_outputHandling = other.m_outputHandling;
122 m_errorHandling = other.m_errorHandling;
123 m_modifiesCurrentDocument = other.m_modifiesCurrentDocument;
124 m_fileName = other.m_fileName;
125 m_presetFileName = other.m_presetFileName;
126 m_presetTool = other.m_presetTool;
127 return *this;
128 }
129
130 ExternalTool::~ExternalTool() = default;
131
id() const132 QString ExternalTool::id() const
133 {
134 return m_id;
135 }
136
description() const137 QString ExternalTool::description() const
138 {
139 return m_description;
140 }
141
displayName() const142 QString ExternalTool::displayName() const
143 {
144 return m_displayName;
145 }
146
displayCategory() const147 QString ExternalTool::displayCategory() const
148 {
149 return m_displayCategory;
150 }
151
order() const152 int ExternalTool::order() const
153 {
154 return m_order;
155 }
156
executables() const157 QStringList ExternalTool::executables() const
158 {
159 return m_executables;
160 }
161
arguments() const162 QString ExternalTool::arguments() const
163 {
164 return m_arguments;
165 }
166
input() const167 QString ExternalTool::input() const
168 {
169 return m_input;
170 }
171
workingDirectory() const172 QString ExternalTool::workingDirectory() const
173 {
174 return m_workingDirectory;
175 }
176
baseEnvironmentProviderId() const177 Id ExternalTool::baseEnvironmentProviderId() const
178 {
179 return m_baseEnvironmentProviderId;
180 }
181
baseEnvironment() const182 Environment ExternalTool::baseEnvironment() const
183 {
184 if (m_baseEnvironmentProviderId.isValid()) {
185 const optional<EnvironmentProvider> provider = EnvironmentProvider::provider(
186 m_baseEnvironmentProviderId.name());
187 if (provider && provider->environment)
188 return provider->environment();
189 }
190 return Environment::systemEnvironment();
191 }
192
environmentUserChanges() const193 EnvironmentItems ExternalTool::environmentUserChanges() const
194 {
195 return m_environment;
196 }
197
outputHandling() const198 ExternalTool::OutputHandling ExternalTool::outputHandling() const
199 {
200 return m_outputHandling;
201 }
202
errorHandling() const203 ExternalTool::OutputHandling ExternalTool::errorHandling() const
204 {
205 return m_errorHandling;
206 }
207
modifiesCurrentDocument() const208 bool ExternalTool::modifiesCurrentDocument() const
209 {
210 return m_modifiesCurrentDocument;
211 }
212
setFileName(const QString & fileName)213 void ExternalTool::setFileName(const QString &fileName)
214 {
215 m_fileName = fileName;
216 }
217
setPreset(QSharedPointer<ExternalTool> preset)218 void ExternalTool::setPreset(QSharedPointer<ExternalTool> preset)
219 {
220 m_presetTool = preset;
221 }
222
fileName() const223 QString ExternalTool::fileName() const
224 {
225 return m_fileName;
226 }
227
preset() const228 QSharedPointer<ExternalTool> ExternalTool::preset() const
229 {
230 return m_presetTool;
231 }
232
setId(const QString & id)233 void ExternalTool::setId(const QString &id)
234 {
235 m_id = id;
236 }
237
setDisplayCategory(const QString & category)238 void ExternalTool::setDisplayCategory(const QString &category)
239 {
240 m_displayCategory = category;
241 }
242
setDisplayName(const QString & name)243 void ExternalTool::setDisplayName(const QString &name)
244 {
245 m_displayName = name;
246 }
247
setDescription(const QString & description)248 void ExternalTool::setDescription(const QString &description)
249 {
250 m_description = description;
251 }
252
253
setOutputHandling(OutputHandling handling)254 void ExternalTool::setOutputHandling(OutputHandling handling)
255 {
256 m_outputHandling = handling;
257 }
258
259
setErrorHandling(OutputHandling handling)260 void ExternalTool::setErrorHandling(OutputHandling handling)
261 {
262 m_errorHandling = handling;
263 }
264
265
setModifiesCurrentDocument(bool modifies)266 void ExternalTool::setModifiesCurrentDocument(bool modifies)
267 {
268 m_modifiesCurrentDocument = modifies;
269 }
270
271
setExecutables(const QStringList & executables)272 void ExternalTool::setExecutables(const QStringList &executables)
273 {
274 m_executables = executables;
275 }
276
277
setArguments(const QString & arguments)278 void ExternalTool::setArguments(const QString &arguments)
279 {
280 m_arguments = arguments;
281 }
282
283
setInput(const QString & input)284 void ExternalTool::setInput(const QString &input)
285 {
286 m_input = input;
287 }
288
289
setWorkingDirectory(const QString & workingDirectory)290 void ExternalTool::setWorkingDirectory(const QString &workingDirectory)
291 {
292 m_workingDirectory = workingDirectory;
293 }
294
setBaseEnvironmentProviderId(Id id)295 void ExternalTool::setBaseEnvironmentProviderId(Id id)
296 {
297 m_baseEnvironmentProviderId = id;
298 }
299
setEnvironmentUserChanges(const EnvironmentItems & items)300 void ExternalTool::setEnvironmentUserChanges(const EnvironmentItems &items)
301 {
302 m_environment = items;
303 }
304
splitLocale(const QString & locale)305 static QStringList splitLocale(const QString &locale)
306 {
307 QString value = locale;
308 QStringList values;
309 if (!value.isEmpty())
310 values << value;
311 int index = value.indexOf(QLatin1Char('.'));
312 if (index >= 0) {
313 value = value.left(index);
314 if (!value.isEmpty())
315 values << value;
316 }
317 index = value.indexOf(QLatin1Char('_'));
318 if (index >= 0) {
319 value = value.left(index);
320 if (!value.isEmpty())
321 values << value;
322 }
323 return values;
324 }
325
localizedText(const QStringList & locales,QXmlStreamReader * reader,int * currentLocale,QString * currentText)326 static void localizedText(const QStringList &locales, QXmlStreamReader *reader, int *currentLocale, QString *currentText)
327 {
328 Q_ASSERT(reader);
329 Q_ASSERT(currentLocale);
330 Q_ASSERT(currentText);
331 if (reader->attributes().hasAttribute(kXmlLang)) {
332 int index = locales.indexOf(reader->attributes().value(kXmlLang).toString());
333 if (index >= 0 && (index < *currentLocale || *currentLocale < 0)) {
334 *currentText = reader->readElementText();
335 *currentLocale = index;
336 } else {
337 reader->skipCurrentElement();
338 }
339 } else {
340 if (*currentLocale < 0 && currentText->isEmpty()) {
341 *currentText = QCoreApplication::translate("Core::Internal::ExternalTool",
342 reader->readElementText().toUtf8().constData(),
343 "");
344 } else {
345 reader->skipCurrentElement();
346 }
347 }
348 if (currentText->isNull()) // prefer isEmpty over isNull
349 *currentText = "";
350 }
351
parseOutputAttribute(const QString & attribute,QXmlStreamReader * reader,ExternalTool::OutputHandling * value)352 static bool parseOutputAttribute(const QString &attribute, QXmlStreamReader *reader, ExternalTool::OutputHandling *value)
353 {
354 const auto output = reader->attributes().value(attribute);
355 if (output == QLatin1String(kOutputShowInPane)) {
356 *value = ExternalTool::ShowInPane;
357 } else if (output == QLatin1String(kOutputReplaceSelection)) {
358 *value = ExternalTool::ReplaceSelection;
359 } else if (output == QLatin1String(kOutputIgnore)) {
360 *value = ExternalTool::Ignore;
361 } else {
362 reader->raiseError("Allowed values for output attribute are 'showinpane','replaceselection','ignore'");
363 return false;
364 }
365 return true;
366 }
367
createFromXml(const QByteArray & xml,QString * errorMessage,const QString & locale)368 ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *errorMessage, const QString &locale)
369 {
370 int descriptionLocale = -1;
371 int nameLocale = -1;
372 int categoryLocale = -1;
373 const QStringList &locales = splitLocale(locale);
374 auto tool = new ExternalTool;
375 QXmlStreamReader reader(xml);
376
377 if (!reader.readNextStartElement() || reader.name() != QLatin1String(kExternalTool))
378 reader.raiseError("Missing start element <externaltool>");
379 tool->m_id = reader.attributes().value(kId).toString();
380 if (tool->m_id.isEmpty())
381 reader.raiseError("Missing or empty id attribute for <externaltool>");
382 while (reader.readNextStartElement()) {
383 if (reader.name() == QLatin1String(kDescription)) {
384 localizedText(locales, &reader, &descriptionLocale, &tool->m_description);
385 } else if (reader.name() == QLatin1String(kDisplayName)) {
386 localizedText(locales, &reader, &nameLocale, &tool->m_displayName);
387 } else if (reader.name() == QLatin1String(kCategory)) {
388 localizedText(locales, &reader, &categoryLocale, &tool->m_displayCategory);
389 } else if (reader.name() == QLatin1String(kOrder)) {
390 if (tool->m_order >= 0) {
391 reader.raiseError("only one <order> element allowed");
392 break;
393 }
394 bool ok;
395 tool->m_order = reader.readElementText().toInt(&ok);
396 if (!ok || tool->m_order < 0)
397 reader.raiseError("<order> element requires non-negative integer value");
398 } else if (reader.name() == QLatin1String(kExecutable)) {
399 if (reader.attributes().hasAttribute(kOutput)) {
400 if (!parseOutputAttribute(kOutput, &reader, &tool->m_outputHandling))
401 break;
402 }
403 if (reader.attributes().hasAttribute(kError)) {
404 if (!parseOutputAttribute(kError, &reader, &tool->m_errorHandling))
405 break;
406 }
407 if (reader.attributes().hasAttribute(kModifiesDocument)) {
408 const auto value = reader.attributes().value(kModifiesDocument);
409 if (value == QLatin1String(kYes) || value == QLatin1String(kTrue)) {
410 tool->m_modifiesCurrentDocument = true;
411 } else if (value == QLatin1String(kNo) || value == QLatin1String(kFalse)) {
412 tool->m_modifiesCurrentDocument = false;
413 } else {
414 reader.raiseError("Allowed values for modifiesdocument attribute are 'yes','true','no','false'");
415 break;
416 }
417 }
418 while (reader.readNextStartElement()) {
419 if (reader.name() == QLatin1String(kPath)) {
420 tool->m_executables.append(reader.readElementText());
421 } else if (reader.name() == QLatin1String(kArguments)) {
422 if (!tool->m_arguments.isEmpty()) {
423 reader.raiseError("only one <arguments> element allowed");
424 break;
425 }
426 tool->m_arguments = reader.readElementText();
427 } else if (reader.name() == QLatin1String(kInput)) {
428 if (!tool->m_input.isEmpty()) {
429 reader.raiseError("only one <input> element allowed");
430 break;
431 }
432 tool->m_input = reader.readElementText();
433 } else if (reader.name() == QLatin1String(kWorkingDirectory)) {
434 if (!tool->m_workingDirectory.isEmpty()) {
435 reader.raiseError("only one <workingdirectory> element allowed");
436 break;
437 }
438 tool->m_workingDirectory = reader.readElementText();
439 } else if (reader.name() == QLatin1String(kBaseEnvironmentId)) {
440 if (tool->m_baseEnvironmentProviderId.isValid()) {
441 reader.raiseError("only one <baseEnvironmentId> element allowed");
442 break;
443 }
444 tool->m_baseEnvironmentProviderId = Id::fromString(reader.readElementText());
445 } else if (reader.name() == QLatin1String(kEnvironment)) {
446 if (!tool->m_environment.isEmpty()) {
447 reader.raiseError("only one <environment> element allowed");
448 break;
449 }
450 QStringList lines = reader.readElementText().split(QLatin1Char(';'));
451 for (auto iter = lines.begin(); iter != lines.end(); ++iter)
452 *iter = QString::fromUtf8(QByteArray::fromPercentEncoding(iter->toUtf8()));
453 tool->m_environment = EnvironmentItem::fromStringList(lines);
454 } else {
455 reader.raiseError(QString::fromLatin1("Unknown element <%1> as subelement of <%2>").arg(
456 reader.qualifiedName().toString(), QString(kExecutable)));
457 break;
458 }
459 }
460 } else {
461 reader.raiseError(QString::fromLatin1("Unknown element <%1>").arg(reader.qualifiedName().toString()));
462 }
463 }
464 if (reader.hasError()) {
465 if (errorMessage)
466 *errorMessage = reader.errorString();
467 delete tool;
468 return nullptr;
469 }
470 return tool;
471 }
472
createFromFile(const QString & fileName,QString * errorMessage,const QString & locale)473 ExternalTool * ExternalTool::createFromFile(const QString &fileName, QString *errorMessage, const QString &locale)
474 {
475 QString absFileName = QFileInfo(fileName).absoluteFilePath();
476 FileReader reader;
477 if (!reader.fetch(FilePath::fromString(absFileName), errorMessage))
478 return nullptr;
479 ExternalTool *tool = ExternalTool::createFromXml(reader.data(), errorMessage, locale);
480 if (!tool)
481 return nullptr;
482 tool->m_fileName = absFileName;
483 return tool;
484 }
485
stringForOutputHandling(ExternalTool::OutputHandling handling)486 static QString stringForOutputHandling(ExternalTool::OutputHandling handling)
487 {
488 switch (handling) {
489 case ExternalTool::Ignore:
490 return QLatin1String(kOutputIgnore);
491 case ExternalTool::ShowInPane:
492 return QLatin1String(kOutputShowInPane);
493 case ExternalTool::ReplaceSelection:
494 return QLatin1String(kOutputReplaceSelection);
495 }
496 return QString();
497 }
498
save(QString * errorMessage) const499 bool ExternalTool::save(QString *errorMessage) const
500 {
501 if (m_fileName.isEmpty())
502 return false;
503 FileSaver saver(FilePath::fromString(m_fileName));
504 if (!saver.hasError()) {
505 QXmlStreamWriter out(saver.file());
506 out.setAutoFormatting(true);
507 out.writeStartDocument("1.0");
508 out.writeComment(QString::fromLatin1("Written on %1 by %2")
509 .arg(QDateTime::currentDateTime().toString(), ICore::versionString()));
510 out.writeStartElement(kExternalTool);
511 out.writeAttribute(kId, m_id);
512 out.writeTextElement(kDescription, m_description);
513 out.writeTextElement(kDisplayName, m_displayName);
514 out.writeTextElement(kCategory, m_displayCategory);
515 if (m_order != -1)
516 out.writeTextElement(kOrder, QString::number(m_order));
517
518 out.writeStartElement(kExecutable);
519 out.writeAttribute(kOutput, stringForOutputHandling(m_outputHandling));
520 out.writeAttribute(kError, stringForOutputHandling(m_errorHandling));
521 out.writeAttribute(kModifiesDocument, QLatin1String(m_modifiesCurrentDocument ? kYes : kNo));
522 foreach (const QString &executable, m_executables)
523 out.writeTextElement(kPath, executable);
524 if (!m_arguments.isEmpty())
525 out.writeTextElement(kArguments, m_arguments);
526 if (!m_input.isEmpty())
527 out.writeTextElement(kInput, m_input);
528 if (!m_workingDirectory.isEmpty())
529 out.writeTextElement(kWorkingDirectory, m_workingDirectory);
530 if (m_baseEnvironmentProviderId.isValid())
531 out.writeTextElement(kBaseEnvironmentId, m_baseEnvironmentProviderId.toString());
532 if (!m_environment.isEmpty()) {
533 QStringList envLines = EnvironmentItem::toStringList(m_environment);
534 for (auto iter = envLines.begin(); iter != envLines.end(); ++iter)
535 *iter = QString::fromUtf8(iter->toUtf8().toPercentEncoding());
536 out.writeTextElement(kEnvironment, envLines.join(QLatin1Char(';')));
537 }
538 out.writeEndElement();
539
540 out.writeEndDocument();
541
542 saver.setResult(&out);
543 }
544 return saver.finalize(errorMessage);
545 }
546
operator ==(const ExternalTool & other) const547 bool ExternalTool::operator==(const ExternalTool &other) const
548 {
549 return m_id == other.m_id
550 && m_description == other.m_description
551 && m_displayName == other.m_displayName
552 && m_displayCategory == other.m_displayCategory
553 && m_order == other.m_order
554 && m_executables == other.m_executables
555 && m_arguments == other.m_arguments
556 && m_input == other.m_input
557 && m_workingDirectory == other.m_workingDirectory
558 && m_baseEnvironmentProviderId == other.m_baseEnvironmentProviderId
559 && m_environment == other.m_environment
560 && m_outputHandling == other.m_outputHandling
561 && m_modifiesCurrentDocument == other.m_modifiesCurrentDocument
562 && m_errorHandling == other.m_errorHandling
563 && m_fileName == other.m_fileName;
564 }
565
566 // #pragma mark -- ExternalToolRunner
567
ExternalToolRunner(const ExternalTool * tool)568 ExternalToolRunner::ExternalToolRunner(const ExternalTool *tool)
569 : m_tool(new ExternalTool(tool)),
570 m_process(nullptr),
571 m_outputCodec(QTextCodec::codecForLocale()),
572 m_hasError(false)
573 {
574 run();
575 }
576
~ExternalToolRunner()577 ExternalToolRunner::~ExternalToolRunner()
578 {
579 if (m_tool)
580 delete m_tool;
581 }
582
hasError() const583 bool ExternalToolRunner::hasError() const
584 {
585 return m_hasError;
586 }
587
errorString() const588 QString ExternalToolRunner::errorString() const
589 {
590 return m_errorString;
591 }
592
resolve()593 bool ExternalToolRunner::resolve()
594 {
595 if (!m_tool)
596 return false;
597 m_resolvedExecutable.clear();
598 m_resolvedArguments.clear();
599 m_resolvedWorkingDirectory.clear();
600 m_resolvedEnvironment = m_tool->baseEnvironment();
601
602 MacroExpander *expander = globalMacroExpander();
603 EnvironmentItems expandedEnvironment = Utils::transform(
604 m_tool->environmentUserChanges(), [expander](const EnvironmentItem &item) {
605 return EnvironmentItem(item.name, expander->expand(item.value), item.operation);
606 });
607 m_resolvedEnvironment.modify(expandedEnvironment);
608
609 {
610 // executable
611 QStringList expandedExecutables; /* for error message */
612 foreach (const QString &executable, m_tool->executables()) {
613 QString expanded = expander->expand(executable);
614 expandedExecutables.append(expanded);
615 m_resolvedExecutable = m_resolvedEnvironment.searchInPath(expanded);
616 if (!m_resolvedExecutable.isEmpty())
617 break;
618 }
619 if (m_resolvedExecutable.isEmpty()) {
620 m_hasError = true;
621 for (int i = 0; i < expandedExecutables.size(); ++i) {
622 m_errorString += tr("Could not find executable for \"%1\" (expanded \"%2\")")
623 .arg(m_tool->executables().at(i), expandedExecutables.at(i));
624 m_errorString += QLatin1Char('\n');
625 }
626 if (!m_errorString.isEmpty())
627 m_errorString.chop(1);
628 return false;
629 }
630 }
631
632 m_resolvedArguments = expander->expandProcessArgs(m_tool->arguments());
633 m_resolvedInput = expander->expand(m_tool->input());
634 m_resolvedWorkingDirectory = expander->expand(m_tool->workingDirectory());
635
636 return true;
637 }
638
run()639 void ExternalToolRunner::run()
640 {
641 if (!resolve()) {
642 deleteLater();
643 return;
644 }
645 if (m_tool->modifiesCurrentDocument()) {
646 if (IDocument *document = EditorManager::currentDocument()) {
647 m_expectedFilePath = document->filePath();
648 if (!DocumentManager::saveModifiedDocument(document)) {
649 deleteLater();
650 return;
651 }
652 DocumentManager::expectFileChange(m_expectedFilePath);
653 }
654 }
655 m_process = new QtcProcess(this);
656 connect(m_process, &QtcProcess::started, this, &ExternalToolRunner::started);
657 connect(m_process, &QtcProcess::finished, this, &ExternalToolRunner::finished);
658 connect(m_process, &QtcProcess::errorOccurred, this, &ExternalToolRunner::error);
659 connect(m_process, &QtcProcess::readyReadStandardOutput,
660 this, &ExternalToolRunner::readStandardOutput);
661 connect(m_process, &QtcProcess::readyReadStandardError,
662 this, &ExternalToolRunner::readStandardError);
663 if (!m_resolvedWorkingDirectory.isEmpty())
664 m_process->setWorkingDirectory(m_resolvedWorkingDirectory);
665 const CommandLine cmd{m_resolvedExecutable, m_resolvedArguments, CommandLine::Raw};
666 m_process->setCommand(cmd);
667 m_process->setEnvironment(m_resolvedEnvironment);
668 const auto write = m_tool->outputHandling() == ExternalTool::ShowInPane
669 ? QOverload<const QString &>::of(MessageManager::writeDisrupting)
670 : QOverload<const QString &>::of(MessageManager::writeSilently);
671 write(tr("Starting external tool \"%1\"").arg(cmd.toUserOutput()));
672 m_process->start();
673 }
674
started()675 void ExternalToolRunner::started()
676 {
677 if (!m_resolvedInput.isEmpty())
678 m_process->write(m_resolvedInput.toLocal8Bit());
679 m_process->closeWriteChannel();
680 }
681
finished()682 void ExternalToolRunner::finished()
683 {
684 if (m_process->result() == QtcProcess::FinishedWithSuccess
685 && (m_tool->outputHandling() == ExternalTool::ReplaceSelection
686 || m_tool->errorHandling() == ExternalTool::ReplaceSelection)) {
687 ExternalToolManager::emitReplaceSelectionRequested(m_processOutput);
688 }
689 if (m_tool->modifiesCurrentDocument())
690 DocumentManager::unexpectFileChange(m_expectedFilePath);
691 const auto write = m_tool->outputHandling() == ExternalTool::ShowInPane
692 ? QOverload<const QString &>::of(MessageManager::writeFlashing)
693 : QOverload<const QString &>::of(MessageManager::writeSilently);
694 write(tr("\"%1\" finished").arg(m_resolvedExecutable.toUserOutput()));
695 deleteLater();
696 }
697
error(QProcess::ProcessError error)698 void ExternalToolRunner::error(QProcess::ProcessError error)
699 {
700 if (m_tool->modifiesCurrentDocument())
701 DocumentManager::unexpectFileChange(m_expectedFilePath);
702 // TODO inform about errors
703 Q_UNUSED(error)
704 deleteLater();
705 }
706
readStandardOutput()707 void ExternalToolRunner::readStandardOutput()
708 {
709 if (m_tool->outputHandling() == ExternalTool::Ignore)
710 return;
711 const QByteArray data = m_process->readAllStandardOutput();
712 const QString output = m_outputCodec->toUnicode(data.constData(),
713 data.length(),
714 &m_outputCodecState);
715 if (m_tool->outputHandling() == ExternalTool::ShowInPane)
716 MessageManager::writeSilently(output);
717 else if (m_tool->outputHandling() == ExternalTool::ReplaceSelection)
718 m_processOutput.append(output);
719 }
720
readStandardError()721 void ExternalToolRunner::readStandardError()
722 {
723 if (m_tool->errorHandling() == ExternalTool::Ignore)
724 return;
725 const QByteArray data = m_process->readAllStandardError();
726 const QString output = m_outputCodec->toUnicode(data.constData(),
727 data.length(),
728 &m_errorCodecState);
729 if (m_tool->errorHandling() == ExternalTool::ShowInPane)
730 MessageManager::writeSilently(output);
731 else if (m_tool->errorHandling() == ExternalTool::ReplaceSelection)
732 m_processOutput.append(output);
733 }
734
735 } // namespace Internal
736
737 } // namespace Core
738