aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Ziller <eike.ziller@qt.io>2017-12-20 09:28:29 +0100
committerEike Ziller <eike.ziller@qt.io>2018-01-05 15:08:20 +0000
commitf73e91128adeaca803e53b0f7af79075bf6ee4b1 (patch)
treecb65f02a369cd1a344d6da686179c059645b6ba9
parent1d8b12047ae4a175303b827e6d682189edb2391c (diff)
Propagate ghc-mod errors up
Mostly an enabler for providing more useful information to the user in follow-up patches. Change-Id: Ia457f0f92175340fe93754df6215693d3a93dd3a Reviewed-by: Eike Ziller <eike.ziller@qt.io>
-rw-r--r--plugins/haskell/followsymbol.cpp16
-rw-r--r--plugins/haskell/followsymbol.h6
-rw-r--r--plugins/haskell/ghcmod.cpp64
-rw-r--r--plugins/haskell/ghcmod.h42
-rw-r--r--plugins/haskell/haskellhoverhandler.cpp20
-rw-r--r--plugins/haskell/haskellhoverhandler.h4
6 files changed, 87 insertions, 65 deletions
diff --git a/plugins/haskell/followsymbol.cpp b/plugins/haskell/followsymbol.cpp
index 1f06467..e7ce1ea 100644
--- a/plugins/haskell/followsymbol.cpp
+++ b/plugins/haskell/followsymbol.cpp
@@ -88,28 +88,30 @@ IAssistProposal *FollowSymbolAssistProcessor::perform(const AssistInterface *int
{
const int position = interface->position();
delete interface;
- const optional<SymbolInfo> info = m_symbolFuture.result();
+ const SymbolInfoOrError info = m_symbolFuture.result();
auto item = new FollowSymbolAssistProposalItem(m_ghcmod->basePath(), info, m_inNextSplit);
return new InstantProposal(position, {item});
}
FollowSymbolAssistProposalItem::FollowSymbolAssistProposalItem(const FileName &basePath,
- const optional<SymbolInfo> &info,
+ const SymbolInfoOrError &info,
bool inNextSplit)
: m_basePath(basePath),
m_inNextSplit(inNextSplit)
{
- if (info && !info->file.isEmpty()) {
+ const SymbolInfo *info_p = Utils::get_if<SymbolInfo>(&info);
+ if (info_p && !info_p->file.isEmpty()) {
m_info = info;
- setText(m_basePath.toString() + '/' + m_info->file.toString());
+ setText(m_basePath.toString() + '/' + info_p->file.toString());
}
}
void FollowSymbolAssistProposalItem::apply(TextDocumentManipulatorInterface &, int) const
{
- if (m_info)
- Core::EditorManager::openEditorAt(m_basePath.toString() + '/' + m_info->file.toString(),
- m_info->line, m_info->col - 1, Core::Id(),
+ const SymbolInfo *info_p = Utils::get_if<SymbolInfo>(&m_info);
+ if (info_p)
+ Core::EditorManager::openEditorAt(m_basePath.toString() + '/' + info_p->file.toString(),
+ info_p->line, info_p->col - 1, Core::Id(),
m_inNextSplit ? Core::EditorManager::OpenInOtherSplit
: Core::EditorManager::NoFlags);
}
diff --git a/plugins/haskell/followsymbol.h b/plugins/haskell/followsymbol.h
index 2e8f454..63ad772 100644
--- a/plugins/haskell/followsymbol.h
+++ b/plugins/haskell/followsymbol.h
@@ -40,14 +40,14 @@ class FollowSymbolAssistProposalItem : public TextEditor::AssistProposalItem
{
public:
FollowSymbolAssistProposalItem(const Utils::FileName &basePath,
- const Utils::optional<SymbolInfo> &info,
+ const SymbolInfoOrError &info,
bool inNextSplit);
void apply(TextEditor::TextDocumentManipulatorInterface &, int) const override;
private:
Utils::FileName m_basePath;
- Utils::optional<SymbolInfo> m_info;
+ SymbolInfoOrError m_info;
bool m_inNextSplit;
};
@@ -86,7 +86,7 @@ public:
private:
std::shared_ptr<AsyncGhcMod> m_ghcmod;
- QFuture<Utils::optional<SymbolInfo>> m_symbolFuture;
+ QFuture<SymbolInfoOrError> m_symbolFuture;
bool m_inNextSplit;
};
diff --git a/plugins/haskell/ghcmod.cpp b/plugins/haskell/ghcmod.cpp
index 1d5595c..c2aa713 100644
--- a/plugins/haskell/ghcmod.cpp
+++ b/plugins/haskell/ghcmod.cpp
@@ -88,12 +88,12 @@ static QString toUnicode(QByteArray data)
return QString::fromUtf8(data);
}
-Utils::optional<SymbolInfo> GhcMod::findSymbol(const FileName &filePath, const QString &symbol)
+SymbolInfoOrError GhcMod::findSymbol(const FileName &filePath, const QString &symbol)
{
return parseFindSymbol(runFindSymbol(filePath, symbol));
}
-Utils::optional<QString> GhcMod::typeInfo(const FileName &filePath, int line, int col)
+QStringOrError GhcMod::typeInfo(const FileName &filePath, int line, int col)
{
return parseTypeInfo(runTypeInfo(filePath, line, col));
}
@@ -107,7 +107,7 @@ static QStringList fileMapArgs(const QHash<FileName, FileName> &map)
return result;
}
-bool GhcMod::ensureStarted()
+Utils::optional<Error> GhcMod::ensureStarted()
{
m_mutex.lock();
const FileName plainStackExecutable = m_stackExecutable;
@@ -117,7 +117,7 @@ bool GhcMod::ensureStarted()
if (m_process && FileName::fromString(m_process->program()) != stackExecutable)
shutdown();
if (m_process)
- return true;
+ return Utils::nullopt;
log("starting");
// for ghc-mod finding stack back:
env.prependOrSetPath(stackExecutable.toFileInfo().absolutePath());
@@ -129,11 +129,12 @@ bool GhcMod::ensureStarted()
<< "legacy-interactive");
if (!m_process->waitForStarted(kTimeoutMS)) {
log("failed to start");
- return false;
+ m_process.reset();
+ return Error({Error::Type::FailedToStartStack, plainStackExecutable.toUserOutput()});
}
log("started");
m_process->setReadChannel(QProcess::StandardOutput);
- return true;
+ return Utils::nullopt;
}
void GhcMod::shutdown()
@@ -152,10 +153,11 @@ void GhcMod::log(const QString &message)
qCDebug(ghcModLog) << "ghcmod for" << m_path.toString() << ":" << qPrintable(message);
}
-Utils::optional<QByteArray> GhcMod::runQuery(const QString &query)
+QByteArrayOrError GhcMod::runQuery(const QString &query)
{
- if (!ensureStarted())
- return Utils::nullopt;
+ const Utils::optional<Error> error = ensureStarted();
+ if (error)
+ return error.value();
log("query \"" + query + "\"");
m_process->write(query.toUtf8() + "\n");
bool ok = false;
@@ -172,7 +174,7 @@ Utils::optional<QByteArray> GhcMod::runQuery(const QString &query)
if (!ok) {
log("failed");
m_process.reset();
- return Utils::nullopt;
+ return Error({Error::Type::Other, QString()});
}
log("success");
// convert to unix line endings
@@ -181,27 +183,28 @@ Utils::optional<QByteArray> GhcMod::runQuery(const QString &query)
return response;
}
-Utils::optional<QByteArray> GhcMod::runFindSymbol(const FileName &filePath, const QString &symbol)
+QByteArrayOrError GhcMod::runFindSymbol(const FileName &filePath, const QString &symbol)
{
return runQuery(QString("info %1 %2").arg(filePath.toString()) // TODO toNative? quoting?
.arg(symbol));
}
-Utils::optional<QByteArray> GhcMod::runTypeInfo(const FileName &filePath, int line, int col)
+QByteArrayOrError GhcMod::runTypeInfo(const FileName &filePath, int line, int col)
{
return runQuery(QString("type %1 %2 %3").arg(filePath.toString()) // TODO toNative? quoting?
.arg(line)
.arg(col + 1));
}
-Utils::optional<SymbolInfo> GhcMod::parseFindSymbol(const Utils::optional<QByteArray> &response)
+SymbolInfoOrError GhcMod::parseFindSymbol(const QByteArrayOrError &response)
{
QRegularExpression infoRegEx("^\\s*(.*?)\\s+--\\sDefined ((at (.+?)(:(\\d+):(\\d+))?)|(in ‘(.+)’.*))$");
- if (!response)
- return Utils::nullopt;
+ const QByteArray *bytes = Utils::get_if<QByteArray>(&response);
+ if (!bytes)
+ return Utils::get<Error>(response);
SymbolInfo info;
bool hasFileOrModule = false;
- const QString str = toUnicode(QByteArray(response.value()).replace('\x0', '\n'));
+ const QString str = toUnicode(QByteArray(*bytes).replace('\x0', '\n'));
for (const QString &line : str.split('\n')) {
if (hasFileOrModule) {
info.additionalInfo += line;
@@ -229,19 +232,20 @@ Utils::optional<SymbolInfo> GhcMod::parseFindSymbol(const Utils::optional<QByteA
}
if (hasFileOrModule)
return info;
- return Utils::nullopt;
+ return Error({Error::Type::Other, QString()});
}
-Utils::optional<QString> GhcMod::parseTypeInfo(const Utils::optional<QByteArray> &response)
+QStringOrError GhcMod::parseTypeInfo(const QByteArrayOrError &response)
{
QRegularExpression typeRegEx("^\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+\"(.*)\"$",
QRegularExpression::MultilineOption);
- if (!response)
- return Utils::nullopt;
- QRegularExpressionMatch result = typeRegEx.match(toUnicode(response.value()));
+ const QByteArray *bytes = Utils::get_if<QByteArray>(&response);
+ if (!bytes)
+ return Utils::get<Error>(response);
+ QRegularExpressionMatch result = typeRegEx.match(toUnicode(*bytes));
if (result.hasMatch())
return result.captured(1);
- return Utils::nullopt;
+ return Error({Error::Type::Other, QString()});
}
void GhcMod::setStackExecutable(const FileName &filePath)
@@ -285,13 +289,13 @@ FileName AsyncGhcMod::basePath() const
template <typename Result>
QFuture<Result> createFuture(AsyncGhcMod::Operation op,
- const std::function<Result(const Utils::optional<QByteArray>&)> &postOp)
+ const std::function<Result(const QByteArrayOrError &)> &postOp)
{
auto fi = new QFutureInterface<Result>;
fi->reportStarted();
// propagate inner events to outside future
- auto opWatcher = new QFutureWatcher<Utils::optional<QByteArray>>();
+ auto opWatcher = new QFutureWatcher<QByteArrayOrError>();
QObject::connect(opWatcher, &QFutureWatcherBase::canceled, [fi] { fi->cancel(); });
QObject::connect(opWatcher, &QFutureWatcherBase::finished, opWatcher, &QObject::deleteLater);
QObject::connect(opWatcher, &QFutureWatcherBase::finished, [fi] {
@@ -319,14 +323,14 @@ QFuture<Result> createFuture(AsyncGhcMod::Operation op,
Returns a QFuture handle for the asynchronous operation. You may not block the main event loop
while waiting for it to finish - doing so will result in a deadlock.
*/
-QFuture<Utils::optional<SymbolInfo>> AsyncGhcMod::findSymbol(const FileName &filePath,
+QFuture<SymbolInfoOrError> AsyncGhcMod::findSymbol(const FileName &filePath,
const QString &symbol)
{
QMutexLocker lock(&m_mutex);
Operation op([this, filePath, symbol] { return m_ghcmod.runFindSymbol(filePath, symbol); });
m_queue.append(op);
QTimer::singleShot(0, &m_threadTarget, [this] { reduceQueue(); });
- return createFuture<Utils::optional<SymbolInfo>>(op, &GhcMod::parseFindSymbol);
+ return createFuture<SymbolInfoOrError>(op, &GhcMod::parseFindSymbol);
}
/*!
@@ -335,13 +339,13 @@ QFuture<Utils::optional<SymbolInfo>> AsyncGhcMod::findSymbol(const FileName &fil
Returns a QFuture handle for the asynchronous operation. You may not block the main event loop
while waiting for it to finish - doing so will result in a deadlock.
*/
-QFuture<Utils::optional<QString>> AsyncGhcMod::typeInfo(const FileName &filePath, int line, int col)
+QFuture<QStringOrError> AsyncGhcMod::typeInfo(const FileName &filePath, int line, int col)
{
QMutexLocker lock(&m_mutex);
Operation op([this, filePath, line, col] { return m_ghcmod.runTypeInfo(filePath, line, col); });
m_queue.append(op);
QTimer::singleShot(0, &m_threadTarget, [this] { reduceQueue(); });
- return createFuture<Utils::optional<QString>>(op, &GhcMod::parseTypeInfo);
+ return createFuture<QStringOrError>(op, &GhcMod::parseTypeInfo);
}
/*!
@@ -380,14 +384,14 @@ void AsyncGhcMod::reduceQueue()
if (!op.fi.isCanceled()) {
QMetaObject::invokeMethod(this, "updateCache", Qt::BlockingQueuedConnection);
m_ghcmod.setFileMap(m_fileCache.fileMap());
- Utils::optional<QByteArray> result = op.op();
+ QByteArrayOrError result = op.op();
op.fi.reportResult(result);
}
op.fi.reportFinished();
}
}
-AsyncGhcMod::Operation::Operation(const std::function<Utils::optional<QByteArray>()> &op)
+AsyncGhcMod::Operation::Operation(const std::function<QByteArrayOrError()> &op)
: op(op)
{
fi.reportStarted();
diff --git a/plugins/haskell/ghcmod.h b/plugins/haskell/ghcmod.h
index 0553472..978b58e 100644
--- a/plugins/haskell/ghcmod.h
+++ b/plugins/haskell/ghcmod.h
@@ -30,6 +30,7 @@
#include <utils/fileutils.h>
#include <utils/optional.h>
#include <utils/synchronousprocess.h>
+#include <utils/variant.h>
#include <QFuture>
#include <QMutex>
@@ -44,6 +45,16 @@ QT_END_NAMESPACE
namespace Haskell {
namespace Internal {
+class Error {
+public:
+ enum class Type {
+ FailedToStartStack,
+ Other // TODO get rid of it
+ };
+ Type type;
+ QString details;
+};
+
class SymbolInfo {
public:
QStringList definition;
@@ -54,6 +65,10 @@ public:
QString module;
};
+using QByteArrayOrError = Utils::variant<QByteArray, Error>;
+using QStringOrError = Utils::variant<QString, Error>;
+using SymbolInfoOrError = Utils::variant<SymbolInfo, Error>;
+
template <typename T> class ghcmod_deleter;
template <> class ghcmod_deleter<QProcess>
{
@@ -71,21 +86,21 @@ public:
Utils::FileName basePath() const;
void setFileMap(const QHash<Utils::FileName, Utils::FileName> &fileMap);
- Utils::optional<SymbolInfo> findSymbol(const Utils::FileName &filePath, const QString &symbol);
- Utils::optional<QString> typeInfo(const Utils::FileName &filePath, int line, int col);
+ SymbolInfoOrError findSymbol(const Utils::FileName &filePath, const QString &symbol);
+ QStringOrError typeInfo(const Utils::FileName &filePath, int line, int col);
- Utils::optional<QByteArray> runQuery(const QString &query);
+ QByteArrayOrError runQuery(const QString &query);
- Utils::optional<QByteArray> runFindSymbol(const Utils::FileName &filePath, const QString &symbol);
- Utils::optional<QByteArray> runTypeInfo(const Utils::FileName &filePath, int line, int col);
+ QByteArrayOrError runFindSymbol(const Utils::FileName &filePath, const QString &symbol);
+ QByteArrayOrError runTypeInfo(const Utils::FileName &filePath, int line, int col);
- static Utils::optional<SymbolInfo> parseFindSymbol(const Utils::optional<QByteArray> &response);
- static Utils::optional<QString> parseTypeInfo(const Utils::optional<QByteArray> &response);
+ static SymbolInfoOrError parseFindSymbol(const QByteArrayOrError &response);
+ static QStringOrError parseTypeInfo(const QByteArrayOrError &response);
static void setStackExecutable(const Utils::FileName &filePath);
private:
- bool ensureStarted();
+ Utils::optional<Error> ensureStarted();
void shutdown();
void log(const QString &message);
@@ -104,9 +119,9 @@ class AsyncGhcMod : public QObject
public:
struct Operation {
Operation() = default;
- Operation(const std::function<Utils::optional<QByteArray>()> &op);
- mutable QFutureInterface<Utils::optional<QByteArray>> fi;
- std::function<Utils::optional<QByteArray>()> op;
+ Operation(const std::function<QByteArrayOrError()> &op);
+ mutable QFutureInterface<QByteArrayOrError> fi;
+ std::function<QByteArrayOrError()> op;
};
AsyncGhcMod(const Utils::FileName &path);
@@ -114,9 +129,8 @@ public:
Utils::FileName basePath() const;
- QFuture<Utils::optional<SymbolInfo>> findSymbol(const Utils::FileName &filePath,
- const QString &symbol);
- QFuture<Utils::optional<QString>> typeInfo(const Utils::FileName &filePath, int line, int col);
+ QFuture<SymbolInfoOrError> findSymbol(const Utils::FileName &filePath, const QString &symbol);
+ QFuture<QStringOrError> typeInfo(const Utils::FileName &filePath, int line, int col);
private slots:
void updateCache(); // called through QMetaObject::invokeMethod
diff --git a/plugins/haskell/haskellhoverhandler.cpp b/plugins/haskell/haskellhoverhandler.cpp
index f418170..bb8472b 100644
--- a/plugins/haskell/haskellhoverhandler.cpp
+++ b/plugins/haskell/haskellhoverhandler.cpp
@@ -87,17 +87,19 @@ void HaskellHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidg
}
static void tryShowToolTip(const QPointer<QWidget> &widget, const QPoint &point,
- QFuture<Utils::optional<QString>> typeFuture,
- QFuture<Utils::optional<SymbolInfo>> symbolFuture)
+ QFuture<QStringOrError> typeFuture,
+ QFuture<SymbolInfoOrError> symbolFuture)
{
if (Utils::ToolTip::isVisible() && widget
&& symbolFuture.isResultReadyAt(0) && typeFuture.isResultReadyAt(0)) {
- const Utils::optional<QString> type = typeFuture.result();
- const Utils::optional<SymbolInfo> info = symbolFuture.result();
- const QString typeString = !type || type.value().isEmpty()
+ const QStringOrError typeOrError = typeFuture.result();
+ const SymbolInfoOrError infoOrError = symbolFuture.result();
+ const QString *type = Utils::get_if<QString>(&typeOrError);
+ const SymbolInfo *info = Utils::get_if<SymbolInfo>(&infoOrError);
+ const QString typeString = !type || type->isEmpty()
? QString()
- : toCode(":: " + type.value());
- const QString infoString = info ? symbolToHtml(info.value()) : QString();
+ : toCode(":: " + *type);
+ const QString infoString = info ? symbolToHtml(*info) : QString();
const QString tip = typeString + infoString;
Utils::ToolTip::show(point, tip, widget);
}
@@ -123,13 +125,13 @@ void HaskellHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWid
Utils::onResultReady(m_typeFuture,
[typeFuture = m_typeFuture, symbolFuture = m_symbolFuture,
ghcmod, widget, point] // hold shared ghcmod pointer
- (const Utils::optional<QString> &) {
+ (const QStringOrError &) {
tryShowToolTip(widget, point, typeFuture, symbolFuture);
});
Utils::onResultReady(m_symbolFuture,
[typeFuture = m_typeFuture, symbolFuture = m_symbolFuture,
ghcmod, widget, point] // hold shared ghcmod pointer
- (const Utils::optional<SymbolInfo> &) {
+ (const SymbolInfoOrError &) {
tryShowToolTip(widget, point, typeFuture, symbolFuture);
});
}
diff --git a/plugins/haskell/haskellhoverhandler.h b/plugins/haskell/haskellhoverhandler.h
index 6df2da9..8cad5c5 100644
--- a/plugins/haskell/haskellhoverhandler.h
+++ b/plugins/haskell/haskellhoverhandler.h
@@ -47,8 +47,8 @@ private:
int m_col = -1;
QString m_name;
- QFuture<Utils::optional<SymbolInfo>> m_symbolFuture;
- QFuture<Utils::optional<QString>> m_typeFuture;
+ QFuture<SymbolInfoOrError> m_symbolFuture;
+ QFuture<QStringOrError> m_typeFuture;
};
} // namespace Internal