diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h --- a/clang-tools-extra/clangd/CodeComplete.h +++ b/clang-tools-extra/clangd/CodeComplete.h @@ -216,6 +216,11 @@ std::vector Completions; bool HasMore = false; CodeCompletionContext::Kind Context = CodeCompletionContext::CCC_Other; + // The text that is being directly completed. + // Example: foo.pb^ -> foo.push_back() + // ~~ + // Typically matches the textEdit.range of Completions, but not guaranteed to. + llvm::Optional CompletionRange; // Usually the source will be parsed with a real C++ parser. // But heuristics may be used instead if e.g. the preamble is not ready. bool RanParser = true; diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1473,6 +1473,7 @@ } Output.HasMore = Incomplete; Output.Context = CCContextKind; + Output.CompletionRange = ReplacedRange; return Output; } diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -2134,6 +2134,7 @@ "some text [[scope::more::]][[identif]]^ier", "some text [[scope::]][[mor]]^e::identifier", "weird case foo::[[::bar::]][[baz]]^", + "/* [[]][[]]^ */", }) { Annotations F(Case); auto Offset = cantFail(positionToOffset(F.code(), F.point())); @@ -2675,6 +2676,28 @@ EXPECT_THAT(Signatures, Contains(Sig("x() -> auto"))); } +TEST(CompletionTest, CompletionRange) { + const char *WithRange = "auto x = [[abc]]^"; + auto Completions = completions(WithRange); + EXPECT_EQ(Completions.CompletionRange, Annotations(WithRange).range()); + Completions = completionsNoCompile(WithRange); + EXPECT_EQ(Completions.CompletionRange, Annotations(WithRange).range()); + + const char *EmptyRange = "auto x = [[]]^"; + Completions = completions(EmptyRange); + EXPECT_EQ(Completions.CompletionRange, Annotations(EmptyRange).range()); + Completions = completionsNoCompile(EmptyRange); + EXPECT_EQ(Completions.CompletionRange, Annotations(EmptyRange).range()); + + // Sema doesn't trigger at all here, while the no-sema completion runs + // heuristics as normal and reports a range. It'd be nice to be consistent. + const char *NoCompletion = "/* [[]]^ */"; + Completions = completions(NoCompletion); + EXPECT_EQ(Completions.CompletionRange, llvm::None); + Completions = completionsNoCompile(NoCompletion); + EXPECT_EQ(Completions.CompletionRange, Annotations(NoCompletion).range()); +} + TEST(NoCompileCompletionTest, Basic) { auto Results = completionsNoCompile(R"cpp( void func() {