diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -691,6 +691,13 @@ llvm::StringRef Remove = Lexer::getSourceText(FixIt.RemoveRange, SM, *LangOpts, &Invalid); llvm::StringRef Insert = FixIt.CodeToInsert; + if (FixIt.InsertFromRange.isValid()) { + assert(FixIt.CodeToInsert.empty() && + "Cannot insert text when range is specified"); + if (!Invalid) + Insert = Lexer::getSourceText(FixIt.InsertFromRange, SM, *LangOpts, + &Invalid); + } if (!Invalid) { llvm::raw_svector_ostream M(Message); if (!Remove.empty() && !Insert.empty()) { diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -548,7 +548,15 @@ TextEdit Result; Result.range = halfOpenToRange(M, Lexer::makeFileCharRange(FixIt.RemoveRange, M, L)); - Result.newText = FixIt.CodeToInsert; + + if (FixIt.InsertFromRange.isValid()) { + assert(FixIt.CodeToInsert.empty() && + "Cannot insert text when range is specified"); + // FIXME: Propagate errors from getSourceText + Result.newText = Lexer::getSourceText(FixIt.InsertFromRange, M, L).str(); + } else { + Result.newText = FixIt.CodeToInsert; + } return Result; } diff --git a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp --- a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp +++ b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp @@ -802,6 +802,49 @@ EXPECT_FALSE(isKeyword("override", LangOpts)); } +Expected sourceRangeInMainFile(SourceManager &SM, Range R) { + if (auto Beg = sourceLocationInMainFile(SM, R.start)) { + if (auto End = sourceLocationInMainFile(SM, R.end)) { + return SourceRange(*Beg, *End); + } else { + return End.takeError(); + } + } else { + return Beg.takeError(); + } +} + +TEST(SourceCodeTests, FixItToTextEdit) { + Annotations Code(R"( + int Var = $V1[[$V1^Value1]] + $V2[[$V2^Value2]]; + )"); + SourceManagerForFile SMFF("File", Code.code()); + SourceManager &SM = SMFF.get(); + LangOptions LangOpts; + SourceRange V1Char = cantFail(sourceRangeInMainFile(SM, Code.range("V1"))); + SourceRange V2Char = cantFail(sourceRangeInMainFile(SM, Code.range("V2"))); + SourceLocation V1Tok = + cantFail(sourceLocationInMainFile(SM, Code.point("V1"))); + SourceLocation V2Tok = + cantFail(sourceLocationInMainFile(SM, Code.point("V2"))); + { + FixItHint Hint; + Hint.RemoveRange = CharSourceRange::getCharRange(V1Char); + Hint.InsertFromRange = CharSourceRange::getCharRange(V2Char); + TextEdit E = toTextEdit(Hint, SM, LangOpts); + EXPECT_EQ(E.range, Code.range("V1")); + EXPECT_EQ(E.newText, "Value2"); + } + { + FixItHint Hint; + Hint.RemoveRange = CharSourceRange::getTokenRange(V1Tok, V1Tok); + Hint.InsertFromRange = CharSourceRange::getTokenRange(V2Tok); + TextEdit E = toTextEdit(Hint, SM, LangOpts); + EXPECT_EQ(E.range, Code.range("V1")); + EXPECT_EQ(E.newText, "Value2"); + } +} + } // namespace } // namespace clangd } // namespace clang