/* Copyright 2017-2018 ccls Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include "clang_complete.h" #include "message_handler.h" #include "pipeline.hh" using namespace ccls; #include namespace { MethodType kMethodType = "textDocument/signatureHelp"; struct In_TextDocumentSignatureHelp : public RequestInMessage { MethodType GetMethodType() const override { return kMethodType; } lsTextDocumentPositionParams params; }; MAKE_REFLECT_STRUCT(In_TextDocumentSignatureHelp, id, params); REGISTER_IN_MESSAGE(In_TextDocumentSignatureHelp); // Represents a parameter of a callable-signature. A parameter can // have a label and a doc-comment. struct lsParameterInformation { // The label of this parameter. Will be shown in // the UI. std::string label; // The human-readable doc-comment of this parameter. Will be shown // in the UI but can be omitted. std::optional documentation; }; MAKE_REFLECT_STRUCT(lsParameterInformation, label, documentation); // Represents the signature of something callable. A signature // can have a label, like a function-name, a doc-comment, and // a set of parameters. struct lsSignatureInformation { // The label of this signature. Will be shown in // the UI. std::string label; // The human-readable doc-comment of this signature. Will be shown // in the UI but can be omitted. std::optional documentation; // The parameters of this signature. std::vector parameters; }; MAKE_REFLECT_STRUCT(lsSignatureInformation, label, documentation, parameters); // Signature help represents the signature of something // callable. There can be multiple signature but only one // active and only one active parameter. struct lsSignatureHelp { // One or more signatures. std::vector signatures; // The active signature. If omitted or the value lies outside the // range of `signatures` the value defaults to zero or is ignored if // `signatures.length === 0`. Whenever possible implementors should // make an active decision about the active signature and shouldn't // rely on a default value. // In future version of the protocol this property might become // mandantory to better express this. std::optional activeSignature; // The active parameter of the active signature. If omitted or the value // lies outside the range of `signatures[activeSignature].parameters` // defaults to 0 if the active signature has parameters. If // the active signature has no parameters it is ignored. // In future version of the protocol this property might become // mandantory to better express the active parameter if the // active signature does have any. std::optional activeParameter; }; MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature, activeParameter); struct Out_TextDocumentSignatureHelp : public lsOutMessage { lsRequestId id; lsSignatureHelp result; }; MAKE_REFLECT_STRUCT(Out_TextDocumentSignatureHelp, jsonrpc, id, result); struct Handler_TextDocumentSignatureHelp : MessageHandler { MethodType GetMethodType() const override { return kMethodType; } void Run(std::unique_ptr message) override { auto request = static_cast(message.get()); lsTextDocumentPositionParams ¶ms = request->params; WorkingFile *file = working_files->GetFileByFilename(params.textDocument.uri.GetPath()); std::string search; int active_param = 0; if (file) { lsPosition completion_position; search = file->FindClosestCallNameInBuffer(params.position, &active_param, &completion_position); params.position = completion_position; } if (search.empty()) return; ClangCompleteManager::OnComplete callback = std::bind( [this](InMessage *message, std::string search, int active_param, const std::vector &results, bool is_cached_result) { auto msg = static_cast(message); Out_TextDocumentSignatureHelp out; out.id = msg->id; for (auto &result : results) { if (result.label != search) continue; lsSignatureInformation signature; signature.label = result.detail; for (auto ¶meter : result.parameters_) { lsParameterInformation ls_param; ls_param.label = parameter; signature.parameters.push_back(ls_param); } out.result.signatures.push_back(signature); } // Prefer the signature with least parameter count but still larger // than active_param. out.result.activeSignature = 0; if (out.result.signatures.size()) { size_t num_parameters = SIZE_MAX; for (size_t i = 0; i < out.result.signatures.size(); ++i) { size_t t = out.result.signatures[i].parameters.size(); if (active_param < t && t < num_parameters) { out.result.activeSignature = int(i); num_parameters = t; } } } // Set signature to what we parsed from the working file. out.result.activeParameter = active_param; pipeline::WriteStdout(kMethodType, out); if (!is_cached_result) { signature_cache->WithLock([&]() { signature_cache->cached_path_ = msg->params.textDocument.uri.GetPath(); signature_cache->cached_completion_position_ = msg->params.position; signature_cache->cached_results_ = results; }); } delete message; }, message.release(), search, active_param, std::placeholders::_1, std::placeholders::_2); if (signature_cache->IsCacheValid(params)) { signature_cache->WithLock([&]() { callback(signature_cache->cached_results_, true /*is_cached_result*/); }); } else { clang_complete->CodeComplete(request->id, params, std::move(callback)); } } }; REGISTER_MESSAGE_HANDLER(Handler_TextDocumentSignatureHelp); } // namespace