Nebiroth updated this revision to Diff 127131.
Nebiroth added a comment.
Rebase on master
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D35894
Files:
clangd/ClangdLSPServer.cpp
clangd/ClangdLSPServer.h
clangd/ClangdServer.cpp
clangd/ClangdServer.h
clangd/ClangdUnit.cpp
clangd/ClangdUnit.h
clangd/Protocol.cpp
clangd/Protocol.h
clangd/ProtocolHandlers.cpp
clangd/ProtocolHandlers.h
test/clangd/hover.test
test/clangd/initialize-params-invalid.test
test/clangd/initialize-params.test
Index: test/clangd/initialize-params.test
===================================================================
--- test/clangd/initialize-params.test
+++ test/clangd/initialize-params.test
@@ -31,6 +31,7 @@
# CHECK-NEXT: "clangd.applyFix"
# CHECK-NEXT: ]
# CHECK-NEXT: },
+# CHECK-NEXT: "hoverProvider": true,
# CHECK-NEXT: "renameProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
Index: test/clangd/initialize-params-invalid.test
===================================================================
--- test/clangd/initialize-params-invalid.test
+++ test/clangd/initialize-params-invalid.test
@@ -31,6 +31,7 @@
# CHECK-NEXT: "clangd.applyFix"
# CHECK-NEXT: ]
# CHECK-NEXT: },
+# CHECK-NEXT: "hoverProvider": true,
# CHECK-NEXT: "renameProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
Index: test/clangd/hover.test
===================================================================
--- /dev/null
+++ test/clangd/hover.test
@@ -0,0 +1,56 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 125
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+
+Content-Length: 611
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"#define MACRO 1\nnamespace ns1 {\nint test = 5;\nstruct MyClass {\nint xasd;\nvoid anotherOperation() {\n}\nstatic int foo(MyClass*) {\nreturn 0;\n}\n\n};}\n//comments test\ntemplate<class T>\nT templateTest(T foo) {\nreturn foo;}\ntemplate<classT>\nclass classTemplateTest {\npublic: T test;};\nint main() {\nint a;\na;\nint b = ns1::test;\nns1::MyClass* Params;\nParams->anotherOperation();\nMACRO;\nint temp = 5;\ntemplateTest(temp);classTemplateTest<int> test;}\n"}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":12}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"#define MACRO 1"}],"range":{"end":{"character":15,"line":0},"start":{"character":8,"line":0}}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":21,"character":1}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"int a"}],"range":{"end":{"character":5,"line":20},"start":{"character":0,"line":20}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":22,"character":15}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"In ns1"},{"language":"C++","value":"int test = 5"}],"range":{"end":{"character":12,"line":2},"start":{"character":0,"line":2}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":23,"character":10}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"In ns1"},{"language":"C++","value":"struct MyClass {"}],"range":{"end":{"character":16,"line":3},"start":{"character":0,"line":3}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":24,"character":13}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"In ns1::MyClass"},{"language":"C++","value":"void anotherOperation() {"}],"range":{"end":{"character":25,"line":5},"start":{"character":0,"line":5}}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":25,"character":1}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"#define MACRO 1"}],"range":{"end":{"character":15,"line":0},"start":{"character":8,"line":0}}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":26,"character":8}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"int temp = 5"}],"range":{"end":{"character":12,"line":26},"start":{"character":0,"line":26}}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":27,"character":1}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"//comments test\ntemplate<class T>\nT templateTest(T foo) {"}],"range":{"end":{"character":23,"line":14},"start":{"character":0,"line":12}}}}
+
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+
+
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -57,6 +57,7 @@
virtual void onRename(Ctx C, RenameParams &Parames) = 0;
virtual void onDocumentHighlight(Ctx C,
TextDocumentPositionParams &Params) = 0;
+ virtual void onCodeHover(Ctx C, TextDocumentPositionParams &Params) = 0;
};
void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -69,6 +69,7 @@
Register("textDocument/switchSourceHeader",
&ProtocolCallbacks::onSwitchSourceHeader);
Register("textDocument/rename", &ProtocolCallbacks::onRename);
+ Register("textDocument/hover", &ProtocolCallbacks::onCodeHover);
Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
Register("textDocument/documentHighlight",
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -397,6 +397,47 @@
};
bool fromJSON(const json::Expr &, TextDocumentPositionParams &);
+struct MarkedString {
+
+ /// MarkedString can be used to render human readable text. It is either a
+ /// markdown string
+ /// or a code-block that provides a language and a code snippet. The language
+ /// identifier
+ /// is sematically equal to the optional language identifier in fenced code
+ /// blocks in GitHub
+ /// issues. See
+ /// https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
+ ///
+ /// The pair of a language and a value is an equivalent to markdown:
+ /// ```
+ /// ${language}
+ /// ${value}
+ /// ```
+ ///
+ /// Note that markdown strings will be sanitized - that means html will be
+ /// escaped.
+
+ std::string markdownString;
+ std::string language;
+ std::string codeBlockValue;
+};
+json::Expr toJSON(const MarkedString &MS);
+
+struct Hover {
+
+ ///
+ /// The hover's content
+ ///
+ std::vector<MarkedString> contents;
+
+ ///
+ /// An optional range is a range inside a text document
+ /// that is used to visualize a hover, e.g. by changing the background color.
+ ///
+ llvm::Optional<Range> range;
+};
+json::Expr toJSON(const Hover &H);
+
/// The kind of a completion entry.
enum class CompletionItemKind {
Missing = 0,
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -286,6 +286,27 @@
O.map("position", R.position);
}
+json::Expr toJSON(const MarkedString &MS) {
+ return json::obj{
+ {"language", MS.language},
+ {"value", MS.codeBlockValue},
+ };
+}
+
+
+json::Expr toJSON(const Hover &H) {
+ if (H.range.hasValue()) {
+ return json::obj{
+ {"contents", json::ary(H.contents)},
+ {"range", H.range.getValue()},
+ };
+ }
+
+ return json::obj{
+ {"contents", json::ary(H.contents)},
+ };
+}
+
json::Expr toJSON(const CompletionItem &CI) {
assert(!CI.label.empty() && "completion item label is required");
json::obj Result{{"label", CI.label}};
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -265,6 +265,8 @@
std::vector<DocumentHighlight>
findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos);
+Hover getHover(const Context &Ctx, ParsedAST &AST, Position Pos);
+
/// For testing/debugging purposes. Note that this method deserializes all
/// unserialized Decls, so use with care.
void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS);
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -27,6 +27,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/Format.h"
#include <algorithm>
@@ -218,6 +219,7 @@
template <class T> bool futureIsReady(std::shared_future<T> const &Future) {
return Future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+
}
} // namespace
@@ -282,7 +284,8 @@
return Mgr.getMacroArgExpandedLocation(InputLoc);
}
-/// Finds declarations locations that a given source location refers to.
+/// Finds declarations and macros that a given source location refers to.
+
class DeclarationAndMacrosFinder : public index::IndexDataConsumer {
std::vector<const Decl *> Decls;
std::vector<const MacroInfo *> MacroInfos;
@@ -297,20 +300,21 @@
: SearchedLocation(SearchedLocation), AST(AST), PP(PP) {}
std::vector<const Decl *> takeDecls() {
- // Don't keep the same declaration multiple times.
+ // Don't keep the same location multiple times.
// This can happen when nodes in the AST are visited twice.
std::sort(Decls.begin(), Decls.end());
auto Last = std::unique(Decls.begin(), Decls.end());
Decls.erase(Last, Decls.end());
- return std::move(Decls);
+ return Decls;
}
std::vector<const MacroInfo *> takeMacroInfos() {
// Don't keep the same Macro info multiple times.
+ // This can happen when nodes in the AST are visited twice.
std::sort(MacroInfos.begin(), MacroInfos.end());
auto Last = std::unique(MacroInfos.begin(), MacroInfos.end());
MacroInfos.erase(Last, MacroInfos.end());
- return std::move(MacroInfos);
+ return MacroInfos;
}
bool
@@ -426,26 +430,76 @@
}
};
+/// Obtains scope for in which a NamedDecl is contained
+std::string getScope(const NamedDecl *ND, const LangOptions &LangOpts) {
+ // Get the full qualified name, the non-qualified name and then diff
+ // them. If there's something left, use that as the scope in the hover,
+ // trimming the extra "::"
+ std::string Res;
+ PrintingPolicy WithScopePP(LangOpts);
+ std::string WithScopeBuf;
+ llvm::raw_string_ostream WithScopeOS(WithScopeBuf);
+ ND->printQualifiedName(WithScopeOS, WithScopePP);
+
+ std::string ResWithScope = WithScopeOS.str();
+ if (!ResWithScope.empty()) {
+ // Get non-qualified name
+ std::string PrintedNameBuf;
+ llvm::raw_string_ostream PrintedNameOS(PrintedNameBuf);
+ ND->printName(PrintedNameOS);
+ auto Last = ResWithScope.rfind(PrintedNameOS.str());
+ if (Last != std::string::npos) {
+ Res = ResWithScope.substr(0, Last);
+ if (Res.length() > 2 &&
+ Res.substr(Res.length() - 2, Res.length()) == "::")
+ Res = Res.substr(0, Res.length() - 2);
+ }
+ }
+ return Res;
+}
+
+/// Returns the file buffer data that the given SourceRange points to.
+llvm::ErrorOr<StringRef> getBufferDataFromSourceRange(ParsedAST &AST,
+ Location L) {
+ StringRef Ref;
+ const FileEntry *F =
+ AST.getASTContext().getSourceManager().getFileManager().getFile(
+ L.uri.file);
+ if (!F)
+ return llvm::errc::no_such_file_or_directory;
+
+ FileID FID = AST.getASTContext().getSourceManager().translateFile(F);
+ Ref = AST.getASTContext().getSourceManager().getBufferData(FID);
+ unsigned Start = AST.getASTContext().getSourceManager().getFileOffset(
+ AST.getASTContext().getSourceManager().translateFileLineCol(
+ F, L.range.start.line + 1, L.range.start.character + 1));
+ unsigned End = AST.getASTContext().getSourceManager().getFileOffset(
+ AST.getASTContext().getSourceManager().translateFileLineCol(
+ F, L.range.end.line + 1, L.range.end.character + 1));
+ Ref = Ref.slice(Start, End);
+ return Ref;
+}
+
} // namespace
-llvm::Optional<Location>
+llvm::ErrorOr<Location>
getDeclarationLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
- SourceLocation LocStart = ValSourceRange.getBegin();
-
+ SourceLocation LocStart =
+ SourceMgr.getExpansionLoc(ValSourceRange.getBegin());
const FileEntry *F =
SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart));
if (!F)
- return llvm::None;
+ return llvm::errc::no_such_file_or_directory;
SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
SourceMgr, LangOpts);
Position Begin;
- Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
- Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+ Begin.line = SourceMgr.getExpansionLineNumber(LocStart) - 1;
+ Begin.character = SourceMgr.getExpansionColumnNumber(LocStart) - 1;
Position End;
- End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
- End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+ End.line = SourceMgr.getExpansionLineNumber(LocEnd) - 1;
+ End.character = SourceMgr.getExpansionColumnNumber(LocEnd) - 1;
Range R = {Begin, End};
Location L;
@@ -501,10 +555,90 @@
std::vector<DocumentHighlight>
clangd::findDocumentHighlights(const Context &Ctx, ParsedAST &AST,
Position Pos) {
+ const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+ const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+ if (!FE)
+ return {};
+
+ SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
+
+ auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>(
+ llvm::errs(), SourceLocationBeg, AST.getASTContext(),
+ AST.getPreprocessor());
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = true;
+
+ // Macro occurences are not currently handled.
+ indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+ DeclMacrosFinder, IndexOpts);
+
+ std::vector<const Decl *> SelectedDecls = DeclMacrosFinder->takeDecls();
+
+ auto DocHighlightsFinder = std::make_shared<DocumentHighlightsFinder>(
+ llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls);
+
+ indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+ DocHighlightsFinder, IndexOpts);
+
+ return DocHighlightsFinder->takeHighlights();
+}
+
+// Returns SourceLocation pointing to the beginning of function comments when
+// hovering on a function. This function backtracks from the starting location
+// one line at a time to determine what will be included.
+SourceLocation getFunctionComments(ParsedAST &AST, const Decl *D) {
+
+ const SourceManager &SM = AST.getASTContext().getSourceManager();
+ const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
+
+ SourceLocation LocStart = D->getSourceRange().getBegin();
+ SourceLocation LocEnd =
+ Lexer::getLocForEndOfToken(D->getSourceRange().getEnd(), 0, SM, LangOpts);
+ Position Begin;
+ Begin.line = SM.getSpellingLineNumber(LocStart) - 1;
+ Begin.character = SM.getSpellingColumnNumber(LocStart) - 1;
+ Position End;
+ End.line = SM.getSpellingLineNumber(LocEnd) - 1;
+ End.character = SM.getSpellingColumnNumber(LocEnd) - 1;
+ Range R = {Begin, End};
+ Location StartLocation;
+ StartLocation.uri =
+ URI::fromFile(SM.getFilename(SM.getSpellingLoc(LocStart)));
+ StartLocation.range = R;
+
+ SourceLocation LocationCandidate;
+
+ const FileEntry *FE = SM.getFileManager().getFile(StartLocation.uri.file);
+ FileID ID = SM.translateFile(FE);
+ StringRef Ref = SM.getBufferData(ID);
+ int StartLine =
+ StartLocation.range.start.line; // previous line from the actual start
+ bool done = false;
+ while (!done && StartLine > 0) {
+ unsigned Start =
+ SM.getFileOffset(SM.translateFileLineCol(FE, StartLine, 1));
+ unsigned End = SM.getFileOffset(SM.translateFileLineCol(FE, StartLine, 50));
+ StringRef CurrentBuffer = Ref.slice(Start, End);
+ if (CurrentBuffer.find("//") != std::string::npos &&
+ CurrentBuffer.find("}") == std::string::npos)
+ LocationCandidate = SM.translateFileLineCol(FE, StartLine, 1);
+ else
+ done = true;
+
+ StartLine--;
+ }
+
+ return LocationCandidate;
+}
+
+Hover clangd::getHover(const Context &Ctx, ParsedAST &AST, Position Pos) {
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+ const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
if (!FE)
- return {};
+ return Hover();
SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
@@ -515,20 +649,103 @@
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = true;
-
- // Macro occurences are not currently handled.
indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
DeclMacrosFinder, IndexOpts);
- std::vector<const Decl *> SelectedDecls = DeclMacrosFinder->takeDecls();
-
- auto DocHighlightsFinder = std::make_shared<DocumentHighlightsFinder>(
- llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls);
+ auto Decls = DeclMacrosFinder->takeDecls();
+ if (!Decls.empty() && Decls[0]) {
+ const Decl *LocationDecl = Decls[0];
+ std::vector<MarkedString> HoverContents;
+
+ // Compute scope as the first "section" of the hover.
+ if (const NamedDecl *NamedD = dyn_cast<NamedDecl>(LocationDecl)) {
+ std::string Scope = getScope(NamedD, AST.getASTContext().getLangOpts());
+ if (!Scope.empty()) {
+ MarkedString Info = {"", "C++", "In " + Scope};
+ HoverContents.push_back(Info);
+ }
+ }
- indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
- DocHighlightsFinder, IndexOpts);
+ // Adjust beginning of hover text depending on the presence of templates and comments.
+ TemplateDecl * TDec = LocationDecl->getDescribedTemplate();
+ const Decl * BeginDecl = TDec ? TDec : LocationDecl;
+ SourceLocation BeginLocation = BeginDecl->getSourceRange().getBegin();
+ RawComment *RC = AST.getASTContext().getRawCommentForDeclNoCache(
+ BeginDecl);
+ if (RC)
+ BeginLocation = RC->getLocStart();
+
+ // Adjust end of hover text for things that have braces/bodies. We don't
+ // want to show the whole definition of a function, class, etc.
+ SourceLocation EndLocation = LocationDecl->getSourceRange().getEnd();
+ if (auto FuncDecl = dyn_cast<FunctionDecl>(LocationDecl)) {
+ if (FuncDecl->isThisDeclarationADefinition() && FuncDecl->getBody())
+ EndLocation = FuncDecl->getBody()->getLocStart();
+ } else if (auto TagDeclaration = dyn_cast<TagDecl>(LocationDecl)) {
+ if (TagDeclaration->isThisDeclarationADefinition())
+ EndLocation = TagDeclaration->getBraceRange().getBegin();
+ } else if (dyn_cast<NamespaceDecl>(LocationDecl)) {
+ SourceLocation BeforeLBraceLoc = Lexer::getLocForEndOfToken(
+ LocationDecl->getLocation(), 0, SourceMgr, LangOpts);
+ if (BeforeLBraceLoc.isValid())
+ EndLocation = BeforeLBraceLoc;
+ }
+
+ SourceRange SR(BeginLocation, EndLocation);
+ if (SR.isValid()) {
+ auto L = getDeclarationLocation(AST, SR);
+ if (L) {
+ auto Ref = getBufferDataFromSourceRange(AST, *L);
+ if (Ref) {
+ Hover H;
+ if (SourceMgr.isInMainFile(BeginLocation))
+ H.range = L->range;
+ MarkedString MS = {"", "C++", *Ref};
+ HoverContents.push_back(MS);
+ H.contents = std::move(HoverContents);
+ return H;
+ }
+ }
+ }
+ }
+
+
+ auto MacroInfos = DeclMacrosFinder->takeMacroInfos();
+ if (!MacroInfos.empty() && MacroInfos[0]) {
+ const MacroInfo *MacroInf = MacroInfos[0];
+ SourceRange SR(MacroInf->getDefinitionLoc(),
+ MacroInf->getDefinitionEndLoc());
+ if (SR.isValid()) {
+ auto L = getDeclarationLocation(AST, SR);
+ if (L) {
+ auto Ref = getBufferDataFromSourceRange(AST, *L);
+ if (Ref) {
+ Hover H;
+ FileID FID =
+ SourceMgr.getFileID(SourceMgr.getExpansionLoc(SR.getBegin()));
+ bool IsInMainFile = FID.isValid() && SourceMgr.getMainFileID() == FID;
+ if (!IsInMainFile) {
+ // Special case when a macro is defined in a preamble, it could
+ // still be in the "main file", below the inclusions. The
+ // SourceManager considers the preamble and the main file as two
+ // different FileIDs but the FileEntries should match.
+ bool IsInPreamble = FID == SourceMgr.getPreambleFileID();
+ if (IsInPreamble)
+ IsInMainFile =
+ SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()) ==
+ SourceMgr.getFileEntryForID(FID);
+ }
+ if (IsInMainFile)
+ H.range = L->range;
+ MarkedString MS = {"", "C++", "#define " + Ref->str()};
+ H.contents.push_back(MS);
+ return H;
+ }
+ }
+ }
+ }
- return DocHighlightsFinder->takeHighlights();
+ return Hover();
}
void ParsedAST::ensurePreambleDeclsDeserialized() {
@@ -748,6 +965,7 @@
CI =
createInvocationFromCommandLine(ArgStrs, CommandLineDiagsEngine, VFS);
// createInvocationFromCommandLine sets DisableFree.
+ CI->LangOpts->CommentOpts.ParseAllComments = true;
CI->getFrontendOpts().DisableFree = false;
}
assert(CI && "Couldn't create CompilerInvocation");
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -300,6 +300,15 @@
llvm::Expected<tooling::Replacements>
formatOnType(StringRef Code, PathRef File, Position Pos);
+ llvm::Expected<Tagged<Hover>> findHover(const Context &Ctx, PathRef File, Position Pos);
+
+ /// Run formatting for \p Rng inside \p File.
+ std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
+ /// Run formatting for the whole \p File.
+ std::vector<tooling::Replacement> formatFile(PathRef File);
+ /// Run formatting after a character was typed at \p Pos in \p File.
+ std::vector<tooling::Replacement> formatOnType(PathRef File, Position Pos);
+
/// Rename all occurrences of the symbol at the \p Pos in \p File to
/// \p NewName.
Expected<std::vector<tooling::Replacement>> rename(const Context &Ctx,
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -425,9 +425,10 @@
llvm::errc::invalid_argument);
std::vector<Location> Result;
- Resources->getAST().get()->runUnderLock([Pos, &Result, &Ctx](ParsedAST *AST) {
+ Resources->getAST().get()->runUnderLock([Pos, &Result, &Ctx, this](ParsedAST *AST) {
if (!AST)
return;
+
Result = clangd::findDefinitions(Ctx, *AST, Pos);
});
return make_tagged(std::move(Result), TaggedFS.Tag);
@@ -508,89 +509,106 @@
llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
ClangdServer::findDocumentHighlights(const Context &Ctx, PathRef File,
- Position Pos) {
- auto FileContents = DraftMgr.getDraft(File);
- if (!FileContents.Draft)
- return llvm::make_error<llvm::StringError>(
- "findDocumentHighlights called on non-added file",
- llvm::errc::invalid_argument);
+ Position Pos) {
+ auto FileContents = DraftMgr.getDraft(File);
+ if (!FileContents.Draft)
+ return llvm::make_error<llvm::StringError>(
+ "findDocumentHighlights called on non-added file",
+ llvm::errc::invalid_argument);
+
+ auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+
+ std::shared_ptr<CppFile> Resources = Units.getFile(File);
+ if (!Resources)
+ return llvm::make_error<llvm::StringError>(
+ "findDocumentHighlights called on non-added file",
+ llvm::errc::invalid_argument);
+
+ std::vector<DocumentHighlight> Result;
+ llvm::Optional<llvm::Error> Err;
+ Resources->getAST().get()->runUnderLock([Pos, &Ctx, &Err,
+ &Result](ParsedAST *AST) {
+ if (!AST) {
+ Err = llvm::make_error<llvm::StringError>("Invalid AST",
+ llvm::errc::invalid_argument);
+ return;
+ }
+ Result = clangd::findDocumentHighlights(Ctx, *AST, Pos);
+ });
+
+ if (Err)
+ return std::move(*Err);
+ return make_tagged(Result, TaggedFS.Tag);
+}
+llvm::Expected<Tagged<Hover>> ClangdServer::findHover(const Context &Ctx, PathRef File,
+ Position Pos) {
+ Hover FinalHover;
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
std::shared_ptr<CppFile> Resources = Units.getFile(File);
- if (!Resources)
- return llvm::make_error<llvm::StringError>(
- "findDocumentHighlights called on non-added file",
- llvm::errc::invalid_argument);
-
- std::vector<DocumentHighlight> Result;
- llvm::Optional<llvm::Error> Err;
- Resources->getAST().get()->runUnderLock([Pos, &Ctx, &Err,
- &Result](ParsedAST *AST) {
- if (!AST) {
- Err = llvm::make_error<llvm::StringError>("Invalid AST",
- llvm::errc::invalid_argument);
- return;
- }
- Result = clangd::findDocumentHighlights(Ctx, *AST, Pos);
+ assert(Resources && "Calling findHover on non-added file");
+ Resources->getAST().get()->runUnderLock(
+ [Pos, &FinalHover, &Ctx, this](ParsedAST *AST) {
+ if (!AST)
+ return;
+ FinalHover = clangd::getHover(Ctx, *AST, Pos);
});
- if (Err)
- return std::move(*Err);
- return make_tagged(Result, TaggedFS.Tag);
+ return make_tagged(std::move(FinalHover), TaggedFS.Tag);
}
std::future<Context> ClangdServer::scheduleReparseAndDiags(
- Context Ctx, PathRef File, VersionedDraft Contents,
- std::shared_ptr<CppFile> Resources,
- Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {
-
- assert(Contents.Draft && "Draft must have contents");
- UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)>
- DeferredRebuild =
- Resources->deferRebuild(*Contents.Draft, TaggedFS.Value);
- std::promise<Context> DonePromise;
- std::future<Context> DoneFuture = DonePromise.get_future();
-
- DocVersion Version = Contents.Version;
- Path FileStr = File;
- VFSTag Tag = TaggedFS.Tag;
- auto ReparseAndPublishDiags =
- [this, FileStr, Version,
- Tag](UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(
- const Context &)>
- DeferredRebuild,
- std::promise<Context> DonePromise, Context Ctx) -> void {
- auto Guard = onScopeExit([&]() { DonePromise.set_value(std::move(Ctx)); });
-
- auto CurrentVersion = DraftMgr.getVersion(FileStr);
- if (CurrentVersion != Version)
- return; // This request is outdated
-
- auto Diags = DeferredRebuild(Ctx);
- if (!Diags)
- return; // A new reparse was requested before this one completed.
-
- // We need to serialize access to resulting diagnostics to avoid calling
- // `onDiagnosticsReady` in the wrong order.
- std::lock_guard<std::mutex> DiagsLock(DiagnosticsMutex);
- DocVersion &LastReportedDiagsVersion = ReportedDiagnosticVersions[FileStr];
- // FIXME(ibiryukov): get rid of '<' comparison here. In the current
- // implementation diagnostics will not be reported after version counters'
- // overflow. This should not happen in practice, since DocVersion is a
- // 64-bit unsigned integer.
- if (Version < LastReportedDiagsVersion)
- return;
- LastReportedDiagsVersion = Version;
-
- DiagConsumer.onDiagnosticsReady(FileStr,
- make_tagged(std::move(*Diags), Tag));
- };
-
- WorkScheduler.addToFront(std::move(ReparseAndPublishDiags),
- std::move(DeferredRebuild), std::move(DonePromise),
- std::move(Ctx));
- return DoneFuture;
+ Context Ctx, PathRef File, VersionedDraft Contents,
+ std::shared_ptr<CppFile> Resources,
+ Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {
+
+ assert(Contents.Draft && "Draft must have contents");
+ UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)>
+ DeferredRebuild =
+ Resources->deferRebuild(*Contents.Draft, TaggedFS.Value);
+ std::promise<Context> DonePromise;
+ std::future<Context> DoneFuture = DonePromise.get_future();
+
+ DocVersion Version = Contents.Version;
+ Path FileStr = File;
+ VFSTag Tag = TaggedFS.Tag;
+ auto ReparseAndPublishDiags =
+ [this, FileStr, Version,
+ Tag](UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(
+ const Context &)>
+ DeferredRebuild,
+ std::promise<Context> DonePromise, Context Ctx) -> void {
+ auto Guard = onScopeExit([&]() { DonePromise.set_value(std::move(Ctx)); });
+
+ auto CurrentVersion = DraftMgr.getVersion(FileStr);
+ if (CurrentVersion != Version)
+ return; // This request is outdated
+
+ auto Diags = DeferredRebuild(Ctx);
+ if (!Diags)
+ return; // A new reparse was requested before this one completed.
+
+ // We need to serialize access to resulting diagnostics to avoid calling
+ // `onDiagnosticsReady` in the wrong order.
+ std::lock_guard<std::mutex> DiagsLock(DiagnosticsMutex);
+ DocVersion &LastReportedDiagsVersion = ReportedDiagnosticVersions[FileStr];
+ // FIXME(ibiryukov): get rid of '<' comparison here. In the current
+ // implementation diagnostics will not be reported after version counters'
+ // overflow. This should not happen in practice, since DocVersion is a
+ // 64-bit unsigned integer.
+ if (Version < LastReportedDiagsVersion)
+ return;
+ LastReportedDiagsVersion = Version;
+
+ DiagConsumer.onDiagnosticsReady(FileStr,
+ make_tagged(std::move(*Diags), Tag));
+ };
+
+ WorkScheduler.addToFront(std::move(ReparseAndPublishDiags),
+ std::move(DeferredRebuild), std::move(DonePromise),
+ std::move(Ctx));
+ return DoneFuture;
}
std::future<Context>
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -73,6 +73,7 @@
void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
void onCommand(Ctx C, ExecuteCommandParams &Params) override;
void onRename(Ctx C, RenameParams &Parames) override;
+ void onCodeHover(Ctx C, TextDocumentPositionParams &Params) override;
std::vector<TextEdit> getFixIts(StringRef File, const clangd::Diagnostic &D);
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -69,6 +69,7 @@
}},
{"definitionProvider", true},
{"documentHighlightProvider", true},
+ {"hoverProvider", true},
{"renameProvider", true},
{"executeCommandProvider",
json::obj{
@@ -246,15 +247,16 @@
void ClangdLSPServer::onGoToDefinition(Ctx C,
TextDocumentPositionParams &Params) {
- auto Items = Server.findDefinitions(
- C, Params.textDocument.uri.file,
+ auto Items = Server.findDefinitions(C,
+ Params.textDocument.uri.file,
Position{Params.position.line, Params.position.character});
if (!Items)
return replyError(C, ErrorCode::InvalidParams,
- llvm::toString(Items.takeError()));
+ llvm::toString(Items.takeError()));
reply(C, json::ary(Items->Value));
}
+
void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
TextDocumentIdentifier &Params) {
llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
@@ -278,6 +280,20 @@
reply(C, json::ary(Highlights->Value));
}
+void ClangdLSPServer::onCodeHover(Ctx C, TextDocumentPositionParams &Params) {
+
+ auto H = Server.findHover(C,
+ Params.textDocument.uri.file,
+ Position{Params.position.line, Params.position.character});
+
+ if (!H) {
+ replyError(C, ErrorCode::InternalError, llvm::toString(H.takeError()));
+ return;
+ }
+
+ reply(C, H->Value);
+}
+
ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
const clangd::CodeCompleteOptions &CCOpts,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits