File indexing completed on 2024-04-28 05:49:00

0001 /*
0002     SPDX-FileCopyrightText: 2019 Mark Nauwelaerts <mark.nauwelaerts@gmail.com>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #pragma once
0008 
0009 #include "lspclientprotocol.h"
0010 
0011 #include <QJsonValue>
0012 #include <QList>
0013 #include <QObject>
0014 #include <QPointer>
0015 #include <QString>
0016 #include <QUrl>
0017 
0018 #include <functional>
0019 #include <optional>
0020 
0021 namespace utils
0022 {
0023 // template helper
0024 // function bind helpers
0025 template<typename R, typename T, typename Tp, typename... Args>
0026 inline std::function<R(Args...)> mem_fun(R (T::*pm)(Args...), Tp object)
0027 {
0028     return [object, pm](Args... args) {
0029         return (object->*pm)(std::forward<Args>(args)...);
0030     };
0031 }
0032 
0033 template<typename R, typename T, typename Tp, typename... Args>
0034 inline std::function<R(Args...)> mem_fun(R (T::*pm)(Args...) const, Tp object)
0035 {
0036     return [object, pm](Args... args) {
0037         return (object->*pm)(std::forward<Args>(args)...);
0038     };
0039 }
0040 
0041 // prevent argument deduction
0042 template<typename T>
0043 struct identity {
0044     typedef T type;
0045 };
0046 
0047 } // namespace utils
0048 
0049 static const int TIMEOUT_SHUTDOWN = 200;
0050 
0051 template<typename T>
0052 using ReplyHandler = std::function<void(const T &)>;
0053 
0054 using ErrorReplyHandler = ReplyHandler<LSPResponseError>;
0055 using DocumentSymbolsReplyHandler = ReplyHandler<std::list<LSPSymbolInformation>>;
0056 using DocumentDefinitionReplyHandler = ReplyHandler<QList<LSPLocation>>;
0057 using DocumentHighlightReplyHandler = ReplyHandler<QList<LSPDocumentHighlight>>;
0058 using DocumentHoverReplyHandler = ReplyHandler<LSPHover>;
0059 using DocumentCompletionReplyHandler = ReplyHandler<QList<LSPCompletionItem>>;
0060 using DocumentCompletionResolveReplyHandler = ReplyHandler<LSPCompletionItem>;
0061 using SignatureHelpReplyHandler = ReplyHandler<LSPSignatureHelp>;
0062 using FormattingReplyHandler = ReplyHandler<QList<LSPTextEdit>>;
0063 using CodeActionReplyHandler = ReplyHandler<QList<LSPCodeAction>>;
0064 using WorkspaceEditReplyHandler = ReplyHandler<LSPWorkspaceEdit>;
0065 using ApplyEditReplyHandler = ReplyHandler<LSPApplyWorkspaceEditResponse>;
0066 using WorkspaceFoldersReplyHandler = ReplyHandler<QList<LSPWorkspaceFolder>>;
0067 using SwitchSourceHeaderHandler = ReplyHandler<QString>;
0068 using MemoryUsageHandler = ReplyHandler<QString>;
0069 using ExpandMacroHandler = ReplyHandler<LSPExpandedMacro>;
0070 using SemanticTokensDeltaReplyHandler = ReplyHandler<LSPSemanticTokensDelta>;
0071 using WorkspaceSymbolsReplyHandler = ReplyHandler<std::vector<LSPSymbolInformation>>;
0072 using SelectionRangeReplyHandler = ReplyHandler<QList<std::shared_ptr<LSPSelectionRange>>>;
0073 using InlayHintsReplyHandler = ReplyHandler<QList<LSPInlayHint>>;
0074 
0075 class LSPClientPlugin;
0076 
0077 struct LSPClientCapabilities {
0078     bool snippetSupport = false;
0079 };
0080 
0081 class LSPClientServer : public QObject
0082 {
0083     Q_OBJECT
0084 
0085 public:
0086     enum class State { None, Started, Running, Shutdown };
0087 
0088     class LSPClientServerPrivate;
0089     class RequestHandle
0090     {
0091         friend class LSPClientServerPrivate;
0092         QPointer<LSPClientServer> m_server;
0093         int m_id = -1;
0094 
0095     public:
0096         RequestHandle &cancel()
0097         {
0098             if (m_server) {
0099                 m_server->cancel(m_id);
0100             }
0101             return *this;
0102         }
0103     };
0104 
0105     using FoldersType = std::optional<QList<LSPWorkspaceFolder>>;
0106 
0107     // optionally adjust server provided/suggest trigger characters
0108     struct TriggerCharactersOverride {
0109         QList<QChar> exclude;
0110         QList<QChar> include;
0111     };
0112 
0113     // collect additional tweaks into a helper struct to avoid ever growing parameter list
0114     // (which then also needs to be duplicated in a few places)
0115     struct ExtraServerConfig {
0116         FoldersType folders;
0117         LSPClientCapabilities caps;
0118         TriggerCharactersOverride completion;
0119         TriggerCharactersOverride signature;
0120     };
0121 
0122     LSPClientServer(const QStringList &server,
0123                     const QUrl &root,
0124                     const QString &langId = QString(),
0125                     const QJsonValue &init = QJsonValue(),
0126                     const ExtraServerConfig = {});
0127     ~LSPClientServer() override;
0128 
0129     // server management
0130     // request start
0131     bool start(bool forwardStdError);
0132     // request shutdown/stop
0133     // if to_xxx >= 0 -> send signal if not exit'ed after timeout
0134     void stop(int to_term_ms, int to_kill_ms);
0135     int cancel(int id);
0136 
0137     // properties
0138     const QStringList &cmdline() const;
0139     const QUrl &root() const;
0140     const QString &langId() const;
0141     State state() const;
0142     Q_SIGNAL void stateChanged(LSPClientServer *server);
0143 
0144     const LSPServerCapabilities &capabilities() const;
0145 
0146     // language
0147     RequestHandle documentSymbols(const QUrl &document, const QObject *context, const DocumentSymbolsReplyHandler &h, const ErrorReplyHandler &eh = nullptr);
0148     RequestHandle documentDefinition(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentDefinitionReplyHandler &h);
0149     RequestHandle documentDeclaration(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentDefinitionReplyHandler &h);
0150     RequestHandle documentTypeDefinition(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentDefinitionReplyHandler &h);
0151     RequestHandle documentImplementation(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentDefinitionReplyHandler &h);
0152     RequestHandle documentHighlight(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentHighlightReplyHandler &h);
0153     RequestHandle documentHover(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentHoverReplyHandler &h);
0154     RequestHandle documentReferences(const QUrl &document, const LSPPosition &pos, bool decl, const QObject *context, const DocumentDefinitionReplyHandler &h);
0155     RequestHandle documentCompletion(const QUrl &document, const LSPPosition &pos, const QObject *context, const DocumentCompletionReplyHandler &h);
0156     RequestHandle documentCompletionResolve(const LSPCompletionItem &c, const QObject *context, const DocumentCompletionResolveReplyHandler &h);
0157     RequestHandle signatureHelp(const QUrl &document, const LSPPosition &pos, const QObject *context, const SignatureHelpReplyHandler &h);
0158     RequestHandle selectionRange(const QUrl &document, const QList<LSPPosition> &positions, const QObject *context, const SelectionRangeReplyHandler &h);
0159     // clangd specific
0160     RequestHandle clangdSwitchSourceHeader(const QUrl &document, const QObject *context, const SwitchSourceHeaderHandler &h);
0161     RequestHandle clangdMemoryUsage(const QObject *context, const MemoryUsageHandler &h);
0162 
0163     RequestHandle documentFormatting(const QUrl &document, const LSPFormattingOptions &options, const QObject *context, const FormattingReplyHandler &h);
0164     RequestHandle documentRangeFormatting(const QUrl &document,
0165                                           const LSPRange &range,
0166                                           const LSPFormattingOptions &options,
0167                                           const QObject *context,
0168                                           const FormattingReplyHandler &h);
0169     RequestHandle documentOnTypeFormatting(const QUrl &document,
0170                                            const LSPPosition &pos,
0171                                            QChar lastChar,
0172                                            const LSPFormattingOptions &options,
0173                                            const QObject *context,
0174                                            const FormattingReplyHandler &h);
0175     RequestHandle
0176     documentRename(const QUrl &document, const LSPPosition &pos, const QString &newName, const QObject *context, const WorkspaceEditReplyHandler &h);
0177 
0178     RequestHandle documentCodeAction(const QUrl &document,
0179                                      const LSPRange &range,
0180                                      const QList<QString> &kinds,
0181                                      QList<LSPDiagnostic> diagnostics,
0182                                      const QObject *context,
0183                                      const CodeActionReplyHandler &h);
0184 
0185     RequestHandle documentSemanticTokensFull(const QUrl &document, const QString requestId, const QObject *context, const SemanticTokensDeltaReplyHandler &h);
0186 
0187     RequestHandle
0188     documentSemanticTokensFullDelta(const QUrl &document, const QString requestId, const QObject *context, const SemanticTokensDeltaReplyHandler &h);
0189 
0190     RequestHandle documentSemanticTokensRange(const QUrl &document, const LSPRange &range, const QObject *context, const SemanticTokensDeltaReplyHandler &h);
0191 
0192     RequestHandle documentInlayHint(const QUrl &document, const LSPRange &range, const QObject *context, const InlayHintsReplyHandler &h);
0193 
0194     void executeCommand(const LSPCommand &command);
0195 
0196     // rust-analyzer specific
0197     RequestHandle rustAnalyzerExpandMacro(const QObject *context, const QUrl &document, const LSPPosition &pos, const ExpandMacroHandler &h);
0198 
0199     // sync
0200     void didOpen(const QUrl &document, int version, const QString &langId, const QString &text);
0201     // only 1 of text or changes should be non-empty and is considered
0202     void didChange(const QUrl &document, int version, const QString &text, const QList<LSPTextDocumentContentChangeEvent> &changes = {});
0203     void didSave(const QUrl &document, const QString &text);
0204     void didClose(const QUrl &document);
0205 
0206     // workspace
0207     void didChangeConfiguration(const QJsonValue &settings);
0208     void didChangeWorkspaceFolders(const QList<LSPWorkspaceFolder> &added, const QList<LSPWorkspaceFolder> &removed);
0209     void workspaceSymbol(const QString &symbol, const QObject *context, const WorkspaceSymbolsReplyHandler &h);
0210 
0211     // notification = signal
0212 Q_SIGNALS:
0213     void showMessage(const LSPShowMessageParams &);
0214     void logMessage(const LSPLogMessageParams &);
0215     void publishDiagnostics(const LSPPublishDiagnosticsParams &);
0216     void workDoneProgress(const LSPWorkDoneProgressParams &);
0217 
0218     // request = signal
0219     void applyEdit(const LSPApplyWorkspaceEditParams &req, const ApplyEditReplyHandler &h, bool &handled);
0220     void workspaceFolders(const WorkspaceFoldersReplyHandler &h, bool &handled);
0221     void showMessageRequest(const LSPShowMessageParams &message,
0222                             const QList<LSPMessageRequestAction> &actions,
0223                             const std::function<void()> chooseNothing,
0224                             bool &handled);
0225 
0226 private:
0227     // pimpl data holder
0228     LSPClientServerPrivate *const d;
0229 };