summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/Selection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/Selection.cpp')
-rw-r--r--clang-tools-extra/clangd/Selection.cpp76
1 files changed, 27 insertions, 49 deletions
diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp
index 4dae2f73539..ffa48f3a57d 100644
--- a/clang-tools-extra/clangd/Selection.cpp
+++ b/clang-tools-extra/clangd/Selection.cpp
@@ -142,11 +142,6 @@ void update(SelectionTree::Selection &Result, SelectionTree::Selection New) {
Result = SelectionTree::Partial;
}
-// As well as comments, don't count semicolons as real tokens.
-// They're not properly claimed as expr-statement is missing from the AST.
-bool shouldIgnore(const syntax::Token &Tok) {
- return Tok.kind() == tok::comment || Tok.kind() == tok::semi;
-}
// SelectionTester can determine whether a range of tokens from the PP-expanded
// stream (corresponding to an AST node) is considered selected.
@@ -177,7 +172,9 @@ public:
});
// Precompute selectedness and offset for selected spelled tokens.
for (const syntax::Token *T = SelFirst; T < SelLimit; ++T) {
- if (shouldIgnore(*T))
+ // As well as comments, don't count semicolons as real tokens.
+ // They're not properly claimed as expr-statement is missing from the AST.
+ if (T->kind() == tok::comment || T->kind() == tok::semi)
continue;
SpelledTokens.emplace_back();
Tok &S = SpelledTokens.back();
@@ -653,49 +650,24 @@ std::string SelectionTree::Node::kind() const {
return std::move(OS.str());
}
-// Decide which selections emulate a "point" query in between characters.
-// If it's ambiguous (the neighboring characters are selectable tokens), returns
-// both possibilities in preference order.
-// Always returns at least one range - if no tokens touched, and empty range.
-static llvm::SmallVector<std::pair<unsigned, unsigned>, 2>
-pointBounds(unsigned Offset, const syntax::TokenBuffer &Tokens) {
- const auto &SM = Tokens.sourceManager();
- SourceLocation Loc = SM.getComposedLoc(SM.getMainFileID(), Offset);
- llvm::SmallVector<std::pair<unsigned, unsigned>, 2> Result;
- // Prefer right token over left.
- for (const syntax::Token &Tok :
- llvm::reverse(spelledTokensTouching(Loc, Tokens))) {
- if (shouldIgnore(Tok))
- continue;
- unsigned Offset = Tokens.sourceManager().getFileOffset(Tok.location());
- Result.emplace_back(Offset, Offset + Tok.length());
- }
- if (Result.empty())
- Result.emplace_back(Offset, Offset);
- return Result;
-}
-
-bool SelectionTree::createEach(ASTContext &AST,
- const syntax::TokenBuffer &Tokens,
- unsigned Begin, unsigned End,
- llvm::function_ref<bool(SelectionTree)> Func) {
- if (Begin != End)
- return Func(SelectionTree(AST, Tokens, Begin, End));
- for (std::pair<unsigned, unsigned> Bounds : pointBounds(Begin, Tokens))
- if (Func(SelectionTree(AST, Tokens, Bounds.first, Bounds.second)))
- return true;
- return false;
-}
-
-SelectionTree SelectionTree::createRight(ASTContext &AST,
- const syntax::TokenBuffer &Tokens,
- unsigned int Begin, unsigned int End) {
- llvm::Optional<SelectionTree> Result;
- createEach(AST, Tokens, Begin, End, [&](SelectionTree T) {
- Result = std::move(T);
- return true;
- });
- return std::move(*Result);
+// Decide which selection emulates a "point" query in between characters.
+static std::pair<unsigned, unsigned> pointBounds(unsigned Offset, FileID FID,
+ ASTContext &AST) {
+ StringRef Buf = AST.getSourceManager().getBufferData(FID);
+ // Edge-cases where the choice is forced.
+ if (Buf.size() == 0)
+ return {0, 0};
+ if (Offset == 0)
+ return {0, 1};
+ if (Offset == Buf.size())
+ return {Offset - 1, Offset};
+ // We could choose either this byte or the previous. Usually we prefer the
+ // character on the right of the cursor (or under a block cursor).
+ // But if that's whitespace/semicolon, we likely want the token on the left.
+ auto IsIgnoredChar = [](char C) { return isWhitespace(C) || C == ';'; };
+ if (IsIgnoredChar(Buf[Offset]) && !IsIgnoredChar(Buf[Offset - 1]))
+ return {Offset - 1, Offset};
+ return {Offset, Offset + 1};
}
SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
@@ -705,6 +677,8 @@ SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
// but that's all clangd has needed so far.
const SourceManager &SM = AST.getSourceManager();
FileID FID = SM.getMainFileID();
+ if (Begin == End)
+ std::tie(Begin, End) = pointBounds(Begin, FID, AST);
PrintPolicy.TerseOutput = true;
PrintPolicy.IncludeNewlines = false;
@@ -716,6 +690,10 @@ SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
dlog("Built selection tree\n{0}", *this);
}
+SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
+ unsigned Offset)
+ : SelectionTree(AST, Tokens, Offset, Offset) {}
+
const Node *SelectionTree::commonAncestor() const {
const Node *Ancestor = Root;
while (Ancestor->Children.size() == 1 && !Ancestor->Selected)
OpenPOWER on IntegriCloud