[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-13 Thread Kadir Cetinkaya via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL339543: [clangd] Support textEdit in addition to insertText. 
(authored by kadircet, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D50449

Files:
  clang-tools-extra/trunk/clangd/CodeComplete.cpp
  clang-tools-extra/trunk/clangd/CodeComplete.h
  clang-tools-extra/trunk/clangd/SourceCode.cpp
  clang-tools-extra/trunk/clangd/SourceCode.h
  clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
  clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp

Index: clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp
===
--- clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp
+++ clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp
@@ -37,6 +37,13 @@
   return Pos;
 }
 
+Range range(const std::pair p1, const std::pair p2) {
+  Range range;
+  range.start = position(p1.first, p1.second);
+  range.end = position(p2.first, p2.second);
+  return range;
+}
+
 TEST(SourceCodeTests, PositionToOffset) {
   // line out of bounds
   EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed());
@@ -119,6 +126,14 @@
   EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds";
 }
 
+TEST(SourceCodeTests, IsRangeConsecutive) {
+  EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5})));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
===
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
@@ -1439,6 +1439,82 @@
   }
 }
 
+TEST(CompletionTest, RenderWithFixItMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 5;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.CompletionTokenRange.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "->Foo::x");
+  EXPECT_TRUE(R.additionalTextEdits.empty());
+}
+
+TEST(CompletionTest, RenderWithFixItNonMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 4;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.CompletionTokenRange.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "Foo::x");
+  EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt));
+}
+
+TEST(CompletionTest, CompletionTokenRange) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB;
+  IgnoreDiagnostics DiagConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+
+  constexpr const char *TestCodes[] = {
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[Aux]]^;
+}
+  )cpp",
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[]]^;
+}
+  )cpp"};
+  for (const auto  : TestCodes) {
+Annotations TestCode(Text);
+auto Results = completions(Server, TestCode.code(), TestCode.point());
+
+EXPECT_EQ(Results.Completions.size(), 1u);
+EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range());
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/trunk/clangd/CodeComplete.h
===
--- clang-tools-extra/trunk/clangd/CodeComplete.h
+++ clang-tools-extra/trunk/clangd/CodeComplete.h
@@ -123,6 +123,9 @@
   /// converting '->' to '.' on member access.
   std::vector FixIts;
 
+  /// Holds the range of the token we are going to replace with this completion.
+  Range CompletionTokenRange;
+
   // Scores are used to rank completion items.
   struct Scores {
 // The score that items are ranked by.
Index: clang-tools-extra/trunk/clangd/SourceCode.cpp
===
--- clang-tools-extra/trunk/clangd/SourceCode.cpp
+++ clang-tools-extra/trunk/clangd/SourceCode.cpp
@@ -224,5 +224,10 @@
   return Result;
 }
 
+bool IsRangeConsecutive(const Range , const Range ) {
+  return Left.end.line == Right.start.line &&
+ Left.end.character == Right.start.character;
+}
+
 } // 

[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-13 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet updated this revision to Diff 160303.
kadircet added a comment.

- Rebase.
- Resolve discussions.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50449

Files:
  clangd/CodeComplete.cpp
  clangd/CodeComplete.h
  clangd/SourceCode.cpp
  clangd/SourceCode.h
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/SourceCodeTests.cpp

Index: unittests/clangd/SourceCodeTests.cpp
===
--- unittests/clangd/SourceCodeTests.cpp
+++ unittests/clangd/SourceCodeTests.cpp
@@ -37,6 +37,13 @@
   return Pos;
 }
 
+Range range(const std::pair p1, const std::pair p2) {
+  Range range;
+  range.start = position(p1.first, p1.second);
+  range.end = position(p2.first, p2.second);
+  return range;
+}
+
 TEST(SourceCodeTests, PositionToOffset) {
   // line out of bounds
   EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed());
@@ -119,6 +126,14 @@
   EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds";
 }
 
+TEST(SourceCodeTests, IsRangeConsecutive) {
+  EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5})));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/CodeCompleteTests.cpp
===
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -1439,6 +1439,82 @@
   }
 }
 
+TEST(CompletionTest, RenderWithFixItMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 5;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.CompletionTokenRange.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "->Foo::x");
+  EXPECT_TRUE(R.additionalTextEdits.empty());
+}
+
+TEST(CompletionTest, RenderWithFixItNonMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 4;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.CompletionTokenRange.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "Foo::x");
+  EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt));
+}
+
+TEST(CompletionTest, CompletionTokenRange) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB;
+  IgnoreDiagnostics DiagConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+
+  constexpr const char *TestCodes[] = {
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[Aux]]^;
+}
+  )cpp",
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[]]^;
+}
+  )cpp"};
+  for (const auto  : TestCodes) {
+Annotations TestCode(Text);
+auto Results = completions(Server, TestCode.code(), TestCode.point());
+
+EXPECT_EQ(Results.Completions.size(), 1u);
+EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range());
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/SourceCode.h
===
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -76,6 +76,8 @@
 /// are normalized as much as possible.
 llvm::Optional getRealPath(const FileEntry *F,
 const SourceManager );
+
+bool IsRangeConsecutive(const Range , const Range );
 } // namespace clangd
 } // namespace clang
 #endif
Index: clangd/SourceCode.cpp
===
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -224,5 +224,10 @@
   return Result;
 }
 
+bool IsRangeConsecutive(const Range , const Range ) {
+  return Left.end.line == Right.start.line &&
+ Left.end.character == Right.start.character;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/CodeComplete.h
===
--- clangd/CodeComplete.h
+++ clangd/CodeComplete.h
@@ -123,6 +123,9 @@
   /// converting '->' to '.' on member access.
   std::vector FixIts;
 
+  /// Holds the range of the token we are going to replace with this completion.
+  Range CompletionTokenRange;
+
   // Scores are used to rank completion items.
   struct Scores {
 // The score that items are ranked by.
Index: clangd/CodeComplete.cpp

[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-10 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov accepted this revision.
ilya-biryukov added a comment.
This revision is now accepted and ready to land.

LGTM. Thanks for the change!
Could we add an option to clangd to switch it on? (VSCode does not work, but 
our hacked-up ycm integration seems to work, right?)




Comment at: clangd/CodeComplete.cpp:1310
+  // other.
+  for (const auto  : FixIts) {
+if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) {

kadircet wrote:
> ilya-biryukov wrote:
> > Maybe keep the `reserve` call? (we could reserve one extra element, but 
> > that's fine)
> Actually we could have much more than one extra element, not for the current 
> situation but may be in the future when we have more fixit completions.
We can't have more than one edit adjacent to the completion identifier, right? 
Otherwise they'll conflict.
It is possible to have multiple edits which are consectutive and the last of 
them is adjacent to the completion identifier, though. But I don't think we 
handle those cases here anyway.

But I'm not too worried about leaving out the reserve call either. At the very 
worst we could waste some memory on a single completion item, but we shouldn't 
keep too many around at the same time anyway, so feel free to ignore this one.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50449



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-10 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet added inline comments.



Comment at: clangd/CodeComplete.cpp:1310
+  // other.
+  for (const auto  : FixIts) {
+if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) {

ilya-biryukov wrote:
> Maybe keep the `reserve` call? (we could reserve one extra element, but 
> that's fine)
Actually we could have much more than one extra element, not for the current 
situation but may be in the future when we have more fixit completions.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50449



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-10 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet updated this revision to Diff 160073.
kadircet marked 5 inline comments as done.
kadircet added a comment.
Herald added a subscriber: mgrang.

- Resolve discussions.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50449

Files:
  clangd/CodeComplete.cpp
  clangd/CodeComplete.h
  clangd/SourceCode.cpp
  clangd/SourceCode.h
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/SourceCodeTests.cpp

Index: unittests/clangd/SourceCodeTests.cpp
===
--- unittests/clangd/SourceCodeTests.cpp
+++ unittests/clangd/SourceCodeTests.cpp
@@ -37,6 +37,13 @@
   return Pos;
 }
 
+Range range(const std::pair p1, const std::pair p2) {
+  Range range;
+  range.start = position(p1.first, p1.second);
+  range.end = position(p2.first, p2.second);
+  return range;
+}
+
 TEST(SourceCodeTests, PositionToOffset) {
   // line out of bounds
   EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed());
@@ -119,6 +126,14 @@
   EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds";
 }
 
+TEST(SourceCodeTests, IsRangeConsecutive) {
+  EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5})));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/CodeCompleteTests.cpp
===
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -1438,6 +1438,82 @@
   }
 }
 
+TEST(CompletionTest, RenderWithFixItMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 5;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.CompletionTokenRange.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "->Foo::x");
+  EXPECT_TRUE(R.additionalTextEdits.empty());
+}
+
+TEST(CompletionTest, RenderWithFixItNonMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 4;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.CompletionTokenRange.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "Foo::x");
+  EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt));
+}
+
+TEST(CompletionTest, CompletionTokenRange) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB;
+  IgnoreDiagnostics DiagConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+
+  constexpr const char *TestCodes[] = {
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[Aux]]^;
+}
+  )cpp",
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[]]^;
+}
+  )cpp"};
+  for (const auto  : TestCodes) {
+Annotations TestCode(Text);
+auto Results = completions(Server, TestCode.code(), TestCode.point());
+
+EXPECT_EQ(Results.Completions.size(), 1u);
+EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range());
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/SourceCode.h
===
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -69,6 +69,7 @@
 TextEdit toTextEdit(const FixItHint , const SourceManager ,
 const LangOptions );
 
+bool IsRangeConsecutive(const Range , const Range );
 } // namespace clangd
 } // namespace clang
 #endif
Index: clangd/SourceCode.cpp
===
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -208,5 +208,10 @@
   return Result;
 }
 
+bool IsRangeConsecutive(const Range , const Range ) {
+  return Left.end.line == Right.start.line &&
+ Left.end.character == Right.start.character;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/CodeComplete.h
===
--- clangd/CodeComplete.h
+++ clangd/CodeComplete.h
@@ -123,6 +123,9 @@
   /// converting '->' to '.' on member access.
   std::vector FixIts;
 
+  /// Holds the range of the token we are going to replace with this completion.
+  Range CompletionTokenRange;
+
   // Scores are used to rank completion items.
   struct Scores {
 // The score that items are ranked by.
Index: clangd/CodeComplete.cpp

[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-09 Thread Ilya Biryukov via Phabricator via cfe-commits
ilya-biryukov added inline comments.



Comment at: clangd/CodeComplete.cpp:289
   }
+  std::stable_sort(Completion.FixIts.begin(), Completion.FixIts.end(),
+   [](const TextEdit , const TextEdit ) {

We shouldn't have duplicate/overlapping fix-its, right? Maybe use `std::sort`?



Comment at: clangd/CodeComplete.cpp:291
+   [](const TextEdit , const TextEdit ) {
+ return X.range.start.line < Y.range.start.line ||
+X.range.start.character <

use built-in tuples comparison `std::tie(X.line, X.column) < std::tie(Y.line, 
Y.column)`?



Comment at: clangd/CodeComplete.cpp:1057
+Range TextEditRange;
+if (CodeCompletionRange.isValid()) {
+  TextEditRange = halfOpenToRange(Recorder->CCSema->getSourceManager(),

Maybe add a comment describing the cases where `isValid()` is false?
Does it happen when we complete with empty identifier?



Comment at: clangd/CodeComplete.cpp:1310
+  // other.
+  for (const auto  : FixIts) {
+if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) {

Maybe keep the `reserve` call? (we could reserve one extra element, but that's 
fine)



Comment at: clangd/CodeComplete.h:126
 
+  TextEdit textEdit;
+

Could we avoid adding `textEdit` here?
It's an intermediate representation that is only used in clangd, and we should 
be able materialize the actual `textEdit` when converting to LSP.



Comment at: unittests/clangd/SourceCodeTests.cpp:40
 
+Range range(const std::pair p1,
+const std::pair p2) {

we convert those `uint64_t` into `int` when setting the positions anyway.
Maybe use `int` here too?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50449



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50449: [clangd] Support textEdit in addition to insertText.

2018-08-08 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet created this revision.
kadircet added a reviewer: ilya-biryukov.
Herald added subscribers: cfe-commits, arphaman, jkorous, MaskRay, ioeric.

Completion replies contains textEdits as well. Note that this change
relies on https://reviews.llvm.org/D50443.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50449

Files:
  clangd/CodeComplete.cpp
  clangd/CodeComplete.h
  clangd/SourceCode.cpp
  clangd/SourceCode.h
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/SourceCodeTests.cpp

Index: unittests/clangd/SourceCodeTests.cpp
===
--- unittests/clangd/SourceCodeTests.cpp
+++ unittests/clangd/SourceCodeTests.cpp
@@ -37,6 +37,14 @@
   return Pos;
 }
 
+Range range(const std::pair p1,
+const std::pair p2) {
+  Range range;
+  range.start = position(p1.first, p1.second);
+  range.end = position(p2.first, p2.second);
+  return range;
+}
+
 TEST(SourceCodeTests, PositionToOffset) {
   // line out of bounds
   EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed());
@@ -119,6 +127,14 @@
   EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds";
 }
 
+TEST(SourceCodeTests, IsRangeConsecutive) {
+  EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4})));
+  EXPECT_FALSE(
+  IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5})));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/CodeCompleteTests.cpp
===
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -1438,6 +1438,82 @@
   }
 }
 
+TEST(CompletionTest, RenderWithFixItMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 5;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.textEdit.range.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "->Foo::x");
+  EXPECT_TRUE(R.additionalTextEdits.empty());
+}
+
+TEST(CompletionTest, RenderWithFixItNonMerged) {
+  TextEdit FixIt;
+  FixIt.range.end.character = 4;
+  FixIt.newText = "->";
+
+  CodeCompletion C;
+  C.Name = "x";
+  C.RequiredQualifier = "Foo::";
+  C.FixIts = {FixIt};
+  C.textEdit.range.start.character = 5;
+
+  CodeCompleteOptions Opts;
+  Opts.IncludeFixIts = true;
+
+  auto R = C.render(Opts);
+  EXPECT_TRUE(R.textEdit);
+  EXPECT_EQ(R.textEdit->newText, "Foo::x");
+  EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt));
+}
+
+TEST(CompletionTest, CompletionTokenRange) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB;
+  IgnoreDiagnostics DiagConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+
+  constexpr const char *TestCodes[] = {
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[Aux]]^;
+}
+  )cpp",
+  R"cpp(
+class Auxilary {
+ public:
+  void AuxFunction();
+};
+void f() {
+  Auxilary x;
+  x.[[]]^;
+}
+  )cpp"};
+  for (const auto  : TestCodes) {
+Annotations TestCode(Text);
+auto Results = completions(Server, TestCode.code(), TestCode.point());
+
+EXPECT_EQ(Results.Completions.size(), 1u);
+EXPECT_THAT(Results.Completions.front().textEdit.range, TestCode.range());
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/SourceCode.h
===
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -69,6 +69,7 @@
 TextEdit toTextEdit(const FixItHint , const SourceManager ,
 const LangOptions );
 
+bool IsRangeConsecutive(const Range , const Range );
 } // namespace clangd
 } // namespace clang
 #endif
Index: clangd/SourceCode.cpp
===
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -208,5 +208,10 @@
   return Result;
 }
 
+bool IsRangeConsecutive(const Range , const Range ) {
+  return Left.end.line == Right.start.line &&
+ Left.end.character == Right.start.character;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/CodeComplete.h
===
--- clangd/CodeComplete.h
+++ clangd/CodeComplete.h
@@ -123,6 +123,8 @@
   /// converting '->' to '.' on member access.
   std::vector FixIts;
 
+  TextEdit textEdit;
+
   // Scores are used to rank completion items.
   struct Scores {
 // The score that items are ranked by.
Index: clangd/CodeComplete.cpp