#include "llvm/ADT/Optional.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SHA1.h"
#include "llvm/Support/ScopedPrinter.h"
#include <cstddef>
+#include <memory>
#include <string>
+#include <vector>
namespace clang {
namespace clangd {
llvm::to_string(InvalidFileCount - 1) + " others)");
}
+// Converts a list of Ranges to a LinkedList of SelectionRange.
+SelectionRange render(const std::vector<Range> &Ranges) {
+ if (Ranges.empty())
+ return {};
+ SelectionRange Result;
+ Result.range = Ranges[0];
+ auto *Next = &Result.parent;
+ for (const auto &R : llvm::make_range(Ranges.begin() + 1, Ranges.end())) {
+ *Next = std::make_unique<SelectionRange>();
+ Next->get()->range = R;
+ Next = &Next->get()->parent;
+ }
+ return Result;
+}
+
} // namespace
// MessageHandler dispatches incoming LSP messages.
{"documentHighlightProvider", true},
{"hoverProvider", true},
{"renameProvider", std::move(RenameProvider)},
+ {"selectionRangeProvider", true},
{"documentSymbolProvider", true},
{"workspaceSymbolProvider", true},
{"referencesProvider", true},
std::move(Reply));
}
+void ClangdLSPServer::onSelectionRange(
+ const SelectionRangeParams &Params,
+ Callback<std::vector<SelectionRange>> Reply) {
+ if (Params.positions.size() != 1) {
+ elog("{0} positions provided to SelectionRange. Supports exactly one "
+ "position.",
+ Params.positions.size());
+ return Reply(llvm::make_error<LSPError>(
+ "SelectionRange supports exactly one position",
+ ErrorCode::InvalidRequest));
+ }
+ Server->semanticRanges(
+ Params.textDocument.uri.file(), Params.positions[0],
+ [Reply = std::move(Reply)](
+ llvm::Expected<std::vector<Range>> Ranges) mutable {
+ if (!Ranges) {
+ return Reply(Ranges.takeError());
+ }
+ std::vector<SelectionRange> Result;
+ Result.emplace_back(render(std::move(*Ranges)));
+ return Reply(std::move(Result));
+ });
+}
+
ClangdLSPServer::ClangdLSPServer(
class Transport &Transp, const FileSystemProvider &FSProvider,
const clangd::CodeCompleteOptions &CCOpts,
MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo);
MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy);
MsgHandler->bind("typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy);
+ MsgHandler->bind("textDocument/selectionRange", &ClangdLSPServer::onSelectionRange);
// clang-format on
}
void onChangeConfiguration(const DidChangeConfigurationParams &);
void onSymbolInfo(const TextDocumentPositionParams &,
Callback<std::vector<SymbolDetails>>);
+ void onSelectionRange(const SelectionRangeParams &,
+ Callback<std::vector<SelectionRange>>);
std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
};
}
+bool fromJSON(const llvm::json::Value &Params, SelectionRangeParams &P) {
+ llvm::json::ObjectMapper O(Params);
+ return O && O.map("textDocument", P.textDocument) &&
+ O.map("positions", P.positions);
+}
+
+llvm::json::Value toJSON(const SelectionRange &Out) {
+ if (Out.parent) {
+ return llvm::json::Object{{"range", Out.range},
+ {"parent", toJSON(*Out.parent)}};
+ }
+ return llvm::json::Object{{"range", Out.range}};
+}
} // namespace clangd
} // namespace clang
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <bitset>
+#include <memory>
#include <string>
#include <vector>
};
llvm::json::Value toJSON(const SemanticHighlightingParams &Highlighting);
+struct SelectionRangeParams {
+ /// The text document.
+ TextDocumentIdentifier textDocument;
+
+ /// The positions inside the text document.
+ std::vector<Position> positions;
+};
+bool fromJSON(const llvm::json::Value &, SelectionRangeParams &);
+
+struct SelectionRange {
+ /**
+ * The range of this selection range.
+ */
+ Range range;
+ /**
+ * The parent selection range containing this range. Therefore `parent.range`
+ * must contain `this.range`.
+ */
+ std::unique_ptr<SelectionRange> parent;
+};
+llvm::json::Value toJSON(const SelectionRange &);
+
} // namespace clangd
} // namespace clang
# CHECK-NEXT: "hoverProvider": true,
# CHECK-NEXT: "referencesProvider": true,
# CHECK-NEXT: "renameProvider": true,
+# CHECK-NEXT: "selectionRangeProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
# CHECK-NEXT: "(",
--- /dev/null
+# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void func() {\n}"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/selectionRange","params":{"textDocument":{"uri":"test:///main.cpp"},"positions":[{"line":1,"character":0}]}}
+# CHECK: "id": 1
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "parent": {
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 1,
+# CHECK-NEXT: "line": 1
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 0,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 1,
+# CHECK-NEXT: "line": 1
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 12,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT:}
+---
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}