[clangd] Fix header-name completion replace ranges (#225473)
Handle header-name completion as a special case for replace edits. Its insert range already extends past the identifier, including delimiters such as '"' and '>'. Reuse that range for replace edits so that the insert range remains a prefix of the replace range, as required by the LSP specification. Add a unit test covering this case. Fixes llvm/llvm-project#225326 (cherry picked from commit da43ad37dcc98427fe95039b75ca037d9995d3e8)
This commit is contained in:
1 parent
0da1d6ee12
commit
d9eddcfb47
2 files changed
+63
-3
No files matched your search
@@ -1833,9 +1833,13 @@ private:
|
||||
}
|
||||
|
||||
if (Opts.EnableInsertReplace) {
|
||||
ReplaceRange.emplace();
|
||||
ReplaceRange->start = InsertRange.start;
|
||||
ReplaceRange->end = getEndOfCodeCompletionReplace(SM);
|
||||
// Header-name completion always provides a replace-like range,
|
||||
// including delimiters, even in the Insert mode.
|
||||
const Position ReplaceRangeEnd =
|
||||
CCContextKind == CodeCompletionContext::CCC_IncludedFile
|
||||
? InsertRange.end
|
||||
: getEndOfCodeCompletionReplace(SM);
|
||||
ReplaceRange = Range{InsertRange.start, ReplaceRangeEnd};
|
||||
}
|
||||
Filter = FuzzyMatcher(
|
||||
Recorder->CCSema->getPreprocessor().getCodeCompletionFilter());
|
||||
|
||||
@@ -4327,6 +4327,62 @@ TEST(CompletionTest, ReplaceRangeNoCompile) {
|
||||
EXPECT_EQ(Results.ReplaceRange, std::nullopt);
|
||||
}
|
||||
|
||||
TEST(CompletionTest, ReplaceRangeInclude) {
|
||||
clangd::CodeCompleteOptions Opts;
|
||||
Opts.EnableInsertReplace = true;
|
||||
|
||||
TestTU TU;
|
||||
TU.AdditionalFiles["include/sub/foo.h"] = "";
|
||||
TU.AdditionalFiles["include/sub/u00e9/bar.h"] = "";
|
||||
TU.ExtraArgs = {"-Iinclude"};
|
||||
|
||||
// Replace range starts after a preceding path component and includes the
|
||||
// closing angle bracket.
|
||||
const char *NestedPath = R"cpp(#include <sub/[[fo^o.h>]])cpp";
|
||||
Annotations A(NestedPath);
|
||||
TU.Code = A.code();
|
||||
CodeCompleteResult Results =
|
||||
completions(TU, A.point(), /*IndexSymbols=*/{}, Opts);
|
||||
EXPECT_EQ(Results.InsertRange, A.range());
|
||||
EXPECT_EQ(Results.ReplaceRange, A.range());
|
||||
|
||||
// Replace range starts after a preceding path component and includes the
|
||||
// closing quote.
|
||||
const char *WithQuote = R"cpp(#include "sub/[[fo^o.h"]])cpp";
|
||||
A = Annotations(WithQuote);
|
||||
TU.Code = A.code();
|
||||
Results = completions(TU, A.point(), /*IndexSymbols=*/{}, Opts);
|
||||
EXPECT_EQ(Results.InsertRange, A.range());
|
||||
EXPECT_EQ(Results.ReplaceRange, A.range());
|
||||
|
||||
// Replace range includes the path component and its trailing separator.
|
||||
const char *PathComponent = R"cpp(#include "[[su^b/]]foo.h")cpp";
|
||||
A = Annotations(PathComponent);
|
||||
TU.Code = A.code();
|
||||
Results = completions(TU, A.point(), /*IndexSymbols=*/{}, Opts);
|
||||
EXPECT_EQ(Results.InsertRange, A.range());
|
||||
EXPECT_EQ(Results.ReplaceRange, A.range());
|
||||
|
||||
// A UCN-like escape is not interpreted in a header name.
|
||||
TU.ExtraArgs.push_back("-fno-ms-compatibility");
|
||||
const char *UnicodeLike = R"cpp(#include "[[su^\u00e9/]]foo.h")cpp";
|
||||
A = Annotations(UnicodeLike);
|
||||
TU.Code = A.code();
|
||||
Results = completions(TU, A.point(), /*IndexSymbols=*/{}, Opts);
|
||||
EXPECT_EQ(Results.InsertRange, A.range());
|
||||
EXPECT_EQ(Results.ReplaceRange, A.range());
|
||||
TU.ExtraArgs.pop_back();
|
||||
|
||||
// In MSVC compatibility mode, backslash is a path separator.
|
||||
TU.ExtraArgs.push_back("-fms-compatibility");
|
||||
const char *UnicodeLikeMs = R"cpp(#include "[[su^\]]u00e9/bar.h")cpp";
|
||||
A = Annotations(UnicodeLikeMs);
|
||||
TU.Code = A.code();
|
||||
Results = completions(TU, A.point(), /*IndexSymbols=*/{}, Opts);
|
||||
EXPECT_EQ(Results.InsertRange, A.range());
|
||||
EXPECT_EQ(Results.ReplaceRange, A.range());
|
||||
}
|
||||
|
||||
TEST(NoCompileCompletionTest, Basic) {
|
||||
auto Results = completionsNoCompile(R"cpp(
|
||||
void func() {
|
||||
|
||||
Reference in new issue
Block a user