@@ -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 ();
@@ -650,24 +653,49 @@ std::string SelectionTree::Node::kind() const {
650
653
return std::move (OS.str ());
651
654
}
652
655
653
- // Decide which selection emulates a "point" query in between characters.
654
- static std::pair<unsigned , unsigned > pointBounds (unsigned Offset, FileID FID,
655
- ASTContext &AST) {
656
- StringRef Buf = AST.getSourceManager ().getBufferData (FID);
657
- // Edge-cases where the choice is forced.
658
- if (Buf.size () == 0 )
659
- return {0 , 0 };
660
- if (Offset == 0 )
661
- return {0 , 1 };
662
- if (Offset == Buf.size ())
663
- return {Offset - 1 , Offset};
664
- // We could choose either this byte or the previous. Usually we prefer the
665
- // character on the right of the cursor (or under a block cursor).
666
- // But if that's whitespace/semicolon, we likely want the token on the left.
667
- auto IsIgnoredChar = [](char C) { return isWhitespace (C) || C == ' ;' ; };
668
- if (IsIgnoredChar (Buf[Offset]) && !IsIgnoredChar (Buf[Offset - 1 ]))
669
- return {Offset - 1 , Offset};
670
- return {Offset, Offset + 1 };
656
+ // Decide which selections emulate a "point" query in between characters.
657
+ // If it's ambiguous (the neighboring characters are selectable tokens), returns
658
+ // both possibilities in preference order.
659
+ // Always returns at least one range - if no tokens touched, and empty range.
660
+ static llvm::SmallVector<std::pair<unsigned , unsigned >, 2 >
661
+ pointBounds (unsigned Offset, const syntax::TokenBuffer &Tokens) {
662
+ const auto &SM = Tokens.sourceManager ();
663
+ SourceLocation Loc = SM.getComposedLoc (SM.getMainFileID (), Offset);
664
+ llvm::SmallVector<std::pair<unsigned , unsigned >, 2 > Result;
665
+ // Prefer right token over left.
666
+ for (const syntax::Token &Tok :
667
+ llvm::reverse (spelledTokensTouching (Loc, Tokens))) {
668
+ if (shouldIgnore (Tok))
669
+ continue ;
670
+ unsigned Offset = Tokens.sourceManager ().getFileOffset (Tok.location ());
671
+ Result.emplace_back (Offset, Offset + Tok.length ());
672
+ }
673
+ if (Result.empty ())
674
+ Result.emplace_back (Offset, Offset);
675
+ return Result;
676
+ }
677
+
678
+ bool SelectionTree::createEach (ASTContext &AST,
679
+ const syntax::TokenBuffer &Tokens,
680
+ unsigned Begin, unsigned End,
681
+ llvm::function_ref<bool (SelectionTree)> Func) {
682
+ if (Begin != End)
683
+ return Func (SelectionTree (AST, Tokens, Begin, End));
684
+ for (std::pair<unsigned , unsigned > Bounds : pointBounds (Begin, Tokens))
685
+ if (Func (SelectionTree (AST, Tokens, Bounds.first , Bounds.second )))
686
+ return true ;
687
+ return false ;
688
+ }
689
+
690
+ SelectionTree SelectionTree::createRight (ASTContext &AST,
691
+ const syntax::TokenBuffer &Tokens,
692
+ unsigned int Begin, unsigned int End) {
693
+ llvm::Optional<SelectionTree> Result;
694
+ createEach (AST, Tokens, Begin, End, [&](SelectionTree T) {
695
+ Result = std::move (T);
696
+ return true ;
697
+ });
698
+ return std::move (*Result);
671
699
}
672
700
673
701
SelectionTree::SelectionTree (ASTContext &AST, const syntax::TokenBuffer &Tokens,
@@ -677,8 +705,6 @@ SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
677
705
// but that's all clangd has needed so far.
678
706
const SourceManager &SM = AST.getSourceManager ();
679
707
FileID FID = SM.getMainFileID ();
680
- if (Begin == End)
681
- std::tie (Begin, End) = pointBounds (Begin, FID, AST);
682
708
PrintPolicy.TerseOutput = true ;
683
709
PrintPolicy.IncludeNewlines = false ;
684
710
@@ -690,10 +716,6 @@ SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
690
716
dlog (" Built selection tree\n {0}" , *this );
691
717
}
692
718
693
- SelectionTree::SelectionTree (ASTContext &AST, const syntax::TokenBuffer &Tokens,
694
- unsigned Offset)
695
- : SelectionTree(AST, Tokens, Offset, Offset) {}
696
-
697
719
const Node *SelectionTree::commonAncestor () const {
698
720
const Node *Ancestor = Root;
699
721
while (Ancestor->Children .size () == 1 && !Ancestor->Selected )
0 commit comments