diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -107,6 +107,8 @@ void onChangeConfiguration(const DidChangeConfigurationParams &); void onSymbolInfo(const TextDocumentPositionParams &, Callback>); + void onSelectionRange(const SelectionRangeParams &, + Callback>); std::vector getFixes(StringRef File, const clangd::Diagnostic &D); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1125,6 +1125,30 @@ std::move(Reply)); } +void ClangdLSPServer::onSelectionRange( + const SelectionRangeParams &Params, + Callback> Reply) { + if (Params.positions.size() != 1) { + elog("{0} positions provided to SelectionRange. Supports exactly one " + "position.", + Params.positions.size()); + Reply(llvm::make_error( + "More than one position provided to SelectionRange", + ErrorCode::InvalidRequest)); + } + Server->semanticRanges( + Params.textDocument.uri.file(), Params.positions[0], + [Reply = std::move(Reply)]( + llvm::Expected> Ranges) mutable { + if (!Ranges) { + return Reply(Ranges.takeError()); + } + SelectionRange Result; + Result.ranges = std::move(*Ranges); + Reply(std::vector{Result}); + }); +} + ClangdLSPServer::ClangdLSPServer( class Transport &Transp, const FileSystemProvider &FSProvider, const clangd::CodeCompleteOptions &CCOpts, @@ -1167,6 +1191,7 @@ 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 } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1222,6 +1222,24 @@ }; llvm::json::Value toJSON(const SemanticHighlightingParams &Highlighting); +struct SelectionRangeParams { + /// The text document. + TextDocumentIdentifier textDocument; + + /// The positions inside the text document. + std::vector positions; +}; +bool fromJSON(const llvm::json::Value &, SelectionRangeParams &); + +struct SelectionRange { + // The semantic ranges for a position. Any range must contain all the previous + // ranges. ranges.front() must be the inner most range. ranges.back() must be + // the outermost range. + std::vector ranges; +}; +llvm::json::Value toJSON(const SelectionRange &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SelectionRange &); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -15,6 +15,7 @@ #include "URI.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" @@ -1073,5 +1074,27 @@ }; } +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) { + llvm::json::Object Result; + for (const auto &R : llvm::reverse(Out.ranges)) { + llvm::json::Object Head; + Head["range"] = R; + if (!Result.empty()) + Head["parent"] = std::move(Result); + Result = std::move(Head); + } + return Result; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SelectionRange &R) { + return OS << toJSON(R); +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/test/selection-range.test b/clang-tools-extra/clangd/test/selection-range.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/selection-range.test @@ -0,0 +1,74 @@ +# 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 int var1;\n int var2 = var1;\n}"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/selectionRange","params":{"textDocument":{"uri":"test:///main.cpp"},"positions":[{"line":2,"character":14}]}} +# CHECK: "id": 1 +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "parent": { +# CHECK-NEXT: "parent": { +# CHECK-NEXT: "parent": { +# CHECK-NEXT: "parent": { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 1, +# CHECK-NEXT: "line": 3 +# 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": 3 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 1, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 16, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 1, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 16, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"}