Hi klimek, sbenza,
This function returns a list of completions for a given expression and
completion position.
http://llvm-reviews.chandlerc.com/D2261
Files:
include/clang/ASTMatchers/Dynamic/Parser.h
lib/ASTMatchers/Dynamic/Parser.cpp
unittests/ASTMatchers/Dynamic/ParserTest.cpp
Index: include/clang/ASTMatchers/Dynamic/Parser.h
===================================================================
--- include/clang/ASTMatchers/Dynamic/Parser.h
+++ include/clang/ASTMatchers/Dynamic/Parser.h
@@ -144,6 +144,13 @@
static bool parseExpression(StringRef Code, Sema *S,
VariantValue *Value, Diagnostics *Error);
+ /// \brief Complete an expression at the given offset.
+ ///
+ /// \return The list of completions, which may be empty if there are no
+ /// available completions or if an error occurred.
+ static std::vector<MatcherCompletion>
+ completeExpression(StringRef Code, unsigned CompletionOffset);
+
private:
class CodeTokenizer;
struct TokenInfo;
@@ -154,9 +161,17 @@
bool parseExpressionImpl(VariantValue *Value);
bool parseMatcherExpressionImpl(VariantValue *Value);
+ void addCompletion(const TokenInfo &CompToken, StringRef TypedText,
+ StringRef Decl);
+ void addExpressionCompletions();
+
CodeTokenizer *const Tokenizer;
Sema *const S;
Diagnostics *const Error;
+
+ typedef std::vector<std::pair<MatcherCtor, unsigned> > ContextStackTy;
+ ContextStackTy ContextStack;
+ std::vector<MatcherCompletion> Completions;
};
} // namespace dynamic
Index: lib/ASTMatchers/Dynamic/Parser.cpp
===================================================================
--- lib/ASTMatchers/Dynamic/Parser.cpp
+++ lib/ASTMatchers/Dynamic/Parser.cpp
@@ -29,15 +29,16 @@
struct Parser::TokenInfo {
/// \brief Different possible tokens.
enum TokenKind {
- TK_Eof = 0,
- TK_OpenParen = 1,
- TK_CloseParen = 2,
- TK_Comma = 3,
- TK_Period = 4,
- TK_Literal = 5,
- TK_Ident = 6,
- TK_InvalidChar = 7,
- TK_Error = 8
+ TK_Eof,
+ TK_OpenParen,
+ TK_CloseParen,
+ TK_Comma,
+ TK_Period,
+ TK_Literal,
+ TK_Ident,
+ TK_InvalidChar,
+ TK_Error,
+ TK_CodeCompletion
};
/// \brief Some known identifiers.
@@ -57,7 +58,15 @@
class Parser::CodeTokenizer {
public:
explicit CodeTokenizer(StringRef MatcherCode, Diagnostics *Error)
- : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error) {
+ : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error),
+ CodeCompletionLocation(0) {
+ NextToken = getNextToken();
+ }
+
+ CodeTokenizer(StringRef MatcherCode, Diagnostics *Error,
+ unsigned CodeCompletionOffset)
+ : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error),
+ CodeCompletionLocation(MatcherCode.data() + CodeCompletionOffset) {
NextToken = getNextToken();
}
@@ -79,6 +88,13 @@
TokenInfo Result;
Result.Range.Start = currentLocation();
+ if (CodeCompletionLocation && CodeCompletionLocation <= Code.data()) {
+ Result.Kind = TokenInfo::TK_CodeCompletion;
+ Result.Text = StringRef(CodeCompletionLocation, 0);
+ CodeCompletionLocation = 0;
+ return Result;
+ }
+
if (Code.empty()) {
Result.Kind = TokenInfo::TK_Eof;
Result.Text = "";
@@ -123,8 +139,21 @@
if (isAlphanumeric(Code[0])) {
// Parse an identifier
size_t TokenLength = 1;
- while (TokenLength < Code.size() && isAlphanumeric(Code[TokenLength]))
+ while (1) {
+ // A code completion location in/immediately after an identifier will
+ // cause the portion of the identifier before the code completion
+ // location to become a code completion token.
+ if (CodeCompletionLocation == Code.data() + TokenLength) {
+ CodeCompletionLocation = 0;
+ Result.Kind = TokenInfo::TK_CodeCompletion;
+ Result.Text = Code.substr(0, TokenLength);
+ Code = Code.drop_front(TokenLength);
+ return Result;
+ }
+ if (TokenLength == Code.size() || !isAlphanumeric(Code[TokenLength]))
+ break;
++TokenLength;
+ }
Result.Kind = TokenInfo::TK_Ident;
Result.Text = Code.substr(0, TokenLength);
Code = Code.drop_front(TokenLength);
@@ -225,6 +254,7 @@
unsigned Line;
Diagnostics *Error;
TokenInfo NextToken;
+ const char *CodeCompletionLocation;
};
Parser::Sema::~Sema() {}
@@ -245,6 +275,7 @@
llvm::Optional<MatcherCtor> Ctor =
S->lookupMatcherCtor(NameToken.Text, NameToken.Range, Error);
+ ContextStack.push_back(std::make_pair(*Ctor, 0u));
std::vector<ParserValue> Args;
TokenInfo EndToken;
@@ -260,6 +291,7 @@
if (CommaToken.Kind != TokenInfo::TK_Comma) {
Error->addError(CommaToken.Range, Error->ET_ParserNoComma)
<< CommaToken.Text;
+ ContextStack.pop_back();
return false;
}
}
@@ -269,11 +301,17 @@
ParserValue ArgValue;
ArgValue.Text = Tokenizer->peekNextToken().Text;
ArgValue.Range = Tokenizer->peekNextToken().Range;
- if (!parseExpressionImpl(&ArgValue.Value)) return false;
+ if (!parseExpressionImpl(&ArgValue.Value)) {
+ ContextStack.pop_back();
+ return false;
+ }
Args.push_back(ArgValue);
+ ++ContextStack.back().second;
}
+ ContextStack.pop_back();
+
if (EndToken.Kind == TokenInfo::TK_Eof) {
Error->addError(OpenToken.Range, Error->ET_ParserNoCloseParen);
return false;
@@ -284,6 +322,11 @@
// Parse .bind("foo")
Tokenizer->consumeNextToken(); // consume the period.
const TokenInfo BindToken = Tokenizer->consumeNextToken();
+ if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
+ addCompletion(BindToken, "bind(\"", "bind");
+ return false;
+ }
+
const TokenInfo OpenToken = Tokenizer->consumeNextToken();
const TokenInfo IDToken = Tokenizer->consumeNextToken();
const TokenInfo CloseToken = Tokenizer->consumeNextToken();
@@ -326,6 +369,39 @@
return true;
}
+// If the prefix of this completion matches the completion token, add it to
+// Completions minus the prefix.
+void Parser::addCompletion(const TokenInfo &CompToken, StringRef TypedText,
+ StringRef Decl) {
+ if (TypedText.size() >= CompToken.Text.size() &&
+ TypedText.substr(0, CompToken.Text.size()) == CompToken.Text) {
+ Completions.push_back(
+ MatcherCompletion(TypedText.substr(CompToken.Text.size()), Decl));
+ }
+}
+
+void Parser::addExpressionCompletions() {
+ const TokenInfo CompToken = Tokenizer->consumeNextToken();
+ assert(CompToken.Kind == TokenInfo::TK_CodeCompletion);
+
+ // We cannot complete code if there is an invalid element on the context
+ // stack.
+ for (ContextStackTy::iterator I = ContextStack.begin(),
+ E = ContextStack.end();
+ I != E; ++I) {
+ if (!I->first)
+ return;
+ }
+
+ std::vector<MatcherCompletion> RegCompletions =
+ Registry::getCompletions(ContextStack);
+ for (std::vector<MatcherCompletion>::iterator I = RegCompletions.begin(),
+ E = RegCompletions.end();
+ I != E; ++I) {
+ addCompletion(CompToken, I->TypedText, I->MatcherDecl);
+ }
+}
+
/// \brief Parse an <Expresssion>
bool Parser::parseExpressionImpl(VariantValue *Value) {
switch (Tokenizer->nextTokenKind()) {
@@ -336,6 +412,10 @@
case TokenInfo::TK_Ident:
return parseMatcherExpressionImpl(Value);
+ case TokenInfo::TK_CodeCompletion:
+ addExpressionCompletions();
+ return false;
+
case TokenInfo::TK_Eof:
Error->addError(Tokenizer->consumeNextToken().Range,
Error->ET_ParserNoCode);
@@ -402,6 +482,18 @@
return true;
}
+std::vector<MatcherCompletion>
+Parser::completeExpression(StringRef Code, unsigned CompletionOffset) {
+ Diagnostics Error;
+ CodeTokenizer Tokenizer(Code, &Error, CompletionOffset);
+ RegistrySema S;
+ Parser P(&Tokenizer, &S, &Error);
+ VariantValue Dummy;
+ P.parseExpressionImpl(&Dummy);
+
+ return P.Completions;
+}
+
llvm::Optional<DynTypedMatcher>
Parser::parseMatcherExpression(StringRef Code, Diagnostics *Error) {
RegistrySema S;
Index: unittests/ASTMatchers/Dynamic/ParserTest.cpp
===================================================================
--- unittests/ASTMatchers/Dynamic/ParserTest.cpp
+++ unittests/ASTMatchers/Dynamic/ParserTest.cpp
@@ -246,6 +246,20 @@
ParseWithError("callee(\"A\")"));
}
+TEST(ParserTest, Completion) {
+ std::vector<MatcherCompletion> Comps =
+ Parser::completeExpression("while", 5);
+ ASSERT_EQ(1u, Comps.size());
+ EXPECT_EQ("Stmt(", Comps[0].TypedText);
+ EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
+ Comps[0].MatcherDecl);
+
+ Comps = Parser::completeExpression("whileStmt().", 12);
+ ASSERT_EQ(1u, Comps.size());
+ EXPECT_EQ("bind(\"", Comps[0].TypedText);
+ EXPECT_EQ("bind", Comps[0].MatcherDecl);
+}
+
} // end anonymous namespace
} // end namespace dynamic
} // end namespace ast_matchers
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits