@@ -142,6 +142,11 @@ void update(SelectionTree::Selection &Result, SelectionTree::Selection New) {
142
142
Result = SelectionTree::Partial;
143
143
}
144
144
145
+ // As well as comments, don't count semicolons as real tokens.
146
+ // They're not properly claimed as expr-statement is missing from the AST.
147
+ bool shouldIgnore (const syntax::Token &Tok) {
148
+ return Tok.kind () == tok::comment || Tok.kind () == tok::semi;
149
+ }
145
150
146
151
// SelectionTester can determine whether a range of tokens from the PP-expanded
147
152
// stream (corresponding to an AST node) is considered selected.
@@ -172,9 +177,7 @@ class SelectionTester {
172
177
});
173
178
// Precompute selectedness and offset for selected spelled tokens.
174
179
for (const syntax::Token *T = SelFirst; T < SelLimit; ++T) {
175
- // As well as comments, don't count semicolons as real tokens.
176
- // They're not properly claimed as expr-statement is missing from the AST.
177
- if (T->kind () == tok::comment || T->kind () == tok::semi)
180
+ if (shouldIgnore (*T))
178
181
continue ;
179
182
SpelledTokens.emplace_back ();
180
183
Tok &S = SpelledTokens.back ();
@@ -671,24 +674,49 @@ std::string SelectionTree::Node::kind() const {
671
674
return std::move (OS.str ());
672
675
}
673
676
674
- // Decide which selection emulates a "point" query in between characters.
675
- static std::pair<unsigned , unsigned > pointBounds (unsigned Offset, FileID FID,
676
- ASTContext &AST) {
677
- StringRef Buf = AST.getSourceManager ().getBufferData (FID);
678
- // Edge-cases where the choice is forced.
679
- if (Buf.size () == 0 )
680
- return {0 , 0 };
681
- if (Offset == 0 )
682
- return {0 , 1 };
683
- if (Offset == Buf.size ())
684
- return {Offset - 1 , Offset};
685
- // We could choose either this byte or the previous. Usually we prefer the
686
- // character on the right of the cursor (or under a block cursor).
687
- // But if that's whitespace/semicolon, we likely want the token on the left.
688
- auto IsIgnoredChar = [](char C) { return isWhitespace (C) || C == ' ;' ; };
689
- if (IsIgnoredChar (Buf[Offset]) && !IsIgnoredChar (Buf[Offset - 1 ]))
690
- return {Offset - 1 , Offset};
691
- return {Offset, Offset + 1 };
677
+ // Decide which selections emulate a "point" query in between characters.
678
+ // If it's ambiguous (the neighboring characters are selectable tokens), returns
679
+ // both possibilities in preference order.
680
+ // Always returns at least one range - if no tokens touched, and empty range.
681
+ static llvm::SmallVector<std::pair<unsigned , unsigned >, 2 >
682
+ pointBounds (unsigned Offset, const syntax::TokenBuffer &Tokens) {
683
+ const auto &SM = Tokens.sourceManager ();
684
+ SourceLocation Loc = SM.getComposedLoc (SM.getMainFileID (), Offset);
685
+ llvm::SmallVector<std::pair<unsigned , unsigned >, 2 > Result;
686
+ // Prefer right token over left.
687
+ for (const syntax::Token &Tok :
688
+ llvm::reverse (spelledTokensTouching (Loc, Tokens))) {
689
+ if (shouldIgnore (Tok))
690
+ continue ;
691
+ unsigned Offset = Tokens.sourceManager ().getFileOffset (Tok.location ());
692
+ Result.emplace_back (Offset, Offset + Tok.length ());
693
+ }
694
+ if (Result.empty ())
695
+ Result.emplace_back (Offset, Offset);
696
+ return Result;
697
+ }
698
+
699
+ bool SelectionTree::createEach (ASTContext &AST,
700
+ const syntax::TokenBuffer &Tokens,
701
+ unsigned Begin, unsigned End,
702
+ llvm::function_ref<bool (SelectionTree)> Func) {
703
+ if (Begin != End)
704
+ return Func (SelectionTree (AST, Tokens, Begin, End));
705
+ for (std::pair<unsigned , unsigned > Bounds : pointBounds (Begin, Tokens))
706
+ if (Func (SelectionTree (AST, Tokens, Bounds.first , Bounds.second )))
707
+ return true ;
708
+ return false ;
709
+ }
710
+
711
+ SelectionTree SelectionTree::createRight (ASTContext &AST,
712
+ const syntax::TokenBuffer &Tokens,
713
+ unsigned int Begin, unsigned int End) {
714
+ llvm::Optional<SelectionTree> Result;
715
+ createEach (AST, Tokens, Begin, End, [&](SelectionTree T) {
716
+ Result = std::move (T);
717
+ return true ;
718
+ });
719
+ return std::move (*Result);
692
720
}
693
721
694
722
SelectionTree::SelectionTree (ASTContext &AST, const syntax::TokenBuffer &Tokens,
@@ -698,8 +726,6 @@ SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
698
726
// but that's all clangd has needed so far.
699
727
const SourceManager &SM = AST.getSourceManager ();
700
728
FileID FID = SM.getMainFileID ();
701
- if (Begin == End)
702
- std::tie (Begin, End) = pointBounds (Begin, FID, AST);
703
729
PrintPolicy.TerseOutput = true ;
704
730
PrintPolicy.IncludeNewlines = false ;
705
731
@@ -711,10 +737,6 @@ SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
711
737
dlog (" Built selection tree\n {0}" , *this );
712
738
}
713
739
714
- SelectionTree::SelectionTree (ASTContext &AST, const syntax::TokenBuffer &Tokens,
715
- unsigned Offset)
716
- : SelectionTree(AST, Tokens, Offset, Offset) {}
717
-
718
740
const Node *SelectionTree::commonAncestor () const {
719
741
const Node *Ancestor = Root;
720
742
while (Ancestor->Children .size () == 1 && !Ancestor->Selected )
0 commit comments