diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/haskell/followsymbol.cpp | 16 | ||||
-rw-r--r-- | plugins/haskell/followsymbol.h | 6 | ||||
-rw-r--r-- | plugins/haskell/ghcmod.cpp | 64 | ||||
-rw-r--r-- | plugins/haskell/ghcmod.h | 42 | ||||
-rw-r--r-- | plugins/haskell/haskellhoverhandler.cpp | 20 | ||||
-rw-r--r-- | plugins/haskell/haskellhoverhandler.h | 4 |
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 |