Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Kirill Likhodedov
2010-06-25 10:21:56 +04:00
74 changed files with 1381 additions and 950 deletions
@@ -24,4 +24,5 @@ public interface RegExpLanguageHost {
boolean supportsPossessiveQuantifiers();
boolean supportsPythonNamedGroups();
boolean supportsPythonConditionalRefs();
boolean supportsRubyNamedGroups();
}
@@ -23,417 +23,462 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings({ "RedundantIfStatement" })
@SuppressWarnings({"RedundantIfStatement"})
public class RegExpParser implements PsiParser {
private boolean myAllowDanglingMetacharacters;
private boolean myAllowOmitNumbersInQuantifiers;
public void setAllowDanglingMetacharacters(boolean allowDanglingMetacharacters) {
myAllowDanglingMetacharacters = allowDanglingMetacharacters;
}
@NotNull
public ASTNode parse(IElementType root, PsiBuilder builder) {
public void setAllowOmitNumbersInQuantifiers(boolean allowOmitNumbersInQuantifiers) {
myAllowOmitNumbersInQuantifiers = allowOmitNumbersInQuantifiers;
}
@NotNull
public ASTNode parse(IElementType root, PsiBuilder builder) {
// builder.setDebugMode(true);
final PsiBuilder.Marker rootMarker = builder.mark();
final PsiBuilder.Marker rootMarker = builder.mark();
parsePattern(builder);
parsePattern(builder);
while (!builder.eof()) {
patternExpected(builder);
builder.advanceLexer();
}
rootMarker.done(root);
return builder.getTreeBuilt();
while (!builder.eof()) {
patternExpected(builder);
builder.advanceLexer();
}
rootMarker.done(root);
return builder.getTreeBuilt();
}
/**
* PATTERN ::= BRANCH "|" PATTERN | BRANCH
*/
private boolean parsePattern(PsiBuilder builder) {
final PsiBuilder.Marker marker = builder.mark();
if (!parseBranch(builder)) {
marker.drop();
return false;
}
/**
* PATTERN ::= BRANCH "|" PATTERN | BRANCH
*/
private boolean parsePattern(PsiBuilder builder) {
final PsiBuilder.Marker marker = builder.mark();
while (builder.getTokenType() == RegExpTT.UNION) {
builder.advanceLexer();
if (!parseBranch(builder)) {
// TODO: no test coverage
patternExpected(builder);
break;
}
}
marker.done(RegExpElementTypes.PATTERN);
return true;
if (!parseBranch(builder)) {
marker.drop();
return false;
}
/**
* BRANCH ::= ATOM BRANCH | ""
*/
@SuppressWarnings({ "StatementWithEmptyBody" })
private boolean parseBranch(PsiBuilder builder) {
PsiBuilder.Marker marker = builder.mark();
while (builder.getTokenType() == RegExpTT.UNION) {
builder.advanceLexer();
if (!parseBranch(builder)) {
// TODO: no test coverage
patternExpected(builder);
break;
}
}
if (!parseAtom(builder)) {
final IElementType token = builder.getTokenType();
if (token == RegExpTT.GROUP_END || token == RegExpTT.UNION || token == null) {
// empty branches are allowed
marker.done(RegExpElementTypes.BRANCH);
return true;
}
marker.drop();
return false;
}
marker.done(RegExpElementTypes.PATTERN);
for (;parseAtom(builder);) ;
return true;
}
/**
* BRANCH ::= ATOM BRANCH | ""
*/
@SuppressWarnings({"StatementWithEmptyBody"})
private boolean parseBranch(PsiBuilder builder) {
PsiBuilder.Marker marker = builder.mark();
if (!parseAtom(builder)) {
final IElementType token = builder.getTokenType();
if (token == RegExpTT.GROUP_END || token == RegExpTT.UNION || token == null) {
// empty branches are allowed
marker.done(RegExpElementTypes.BRANCH);
return true;
}
marker.drop();
return false;
}
/**
* ATOM ::= CLOSURE | GROUP
* CLOSURE ::= GROUP QUANTIFIER
*/
private boolean parseAtom(PsiBuilder builder) {
PsiBuilder.Marker marker = parseGroup(builder);
for (; parseAtom(builder);) ;
if (marker == null) {
return false;
}
marker = marker.precede();
marker.done(RegExpElementTypes.BRANCH);
return true;
}
if (parseQuantifier(builder)) {
marker.done(RegExpElementTypes.CLOSURE);
} else {
marker.drop();
}
/**
* ATOM ::= CLOSURE | GROUP
* CLOSURE ::= GROUP QUANTIFIER
*/
private boolean parseAtom(PsiBuilder builder) {
PsiBuilder.Marker marker = parseGroup(builder);
return true;
if (marker == null) {
return false;
}
marker = marker.precede();
if (parseQuantifier(builder)) {
marker.done(RegExpElementTypes.CLOSURE);
}
else {
marker.drop();
}
/**
* QUANTIFIER ::= Q TYPE | ""
* Q ::= "{" BOUND "}" | "*" | "?" | "+"
* BOUND ::= NUM | NUM "," | NUM "," NUM
* TYPE ::= "?" | "+" | ""
*/
private boolean parseQuantifier(PsiBuilder builder) {
final PsiBuilder.Marker marker = builder.mark();
return true;
}
if (builder.getTokenType() == RegExpTT.LBRACE) {
builder.advanceLexer();
if (builder.getTokenType() != RegExpTT.NUMBER && myAllowDanglingMetacharacters) {
marker.done(RegExpTT.CHARACTER);
return true;
}
checkMatches(builder, RegExpTT.NUMBER, "Number expected");
if (builder.getTokenType() == RegExpTT.RBRACE) {
builder.advanceLexer();
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
} else {
checkMatches(builder, RegExpTT.COMMA, "',' expected");
if (builder.getTokenType() == RegExpTT.RBRACE) {
builder.advanceLexer();
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
} else if (builder.getTokenType() == RegExpTT.NUMBER) {
builder.advanceLexer();
checkMatches(builder, RegExpTT.RBRACE, "'}' expected");
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
} else {
builder.error("'}' or number expected");
marker.done(RegExpElementTypes.QUANTIFIER);
return true;
}
}
} else if (RegExpTT.QUANTIFIERS.contains(builder.getTokenType())) {
builder.advanceLexer();
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
} else {
marker.drop();
return false;
}
/**
* QUANTIFIER ::= Q TYPE | ""
* Q ::= "{" BOUND "}" | "*" | "?" | "+"
* BOUND ::= NUM | NUM "," | NUM "," NUM
* TYPE ::= "?" | "+" | ""
*/
private boolean parseQuantifier(PsiBuilder builder) {
final PsiBuilder.Marker marker = builder.mark();
return true;
}
private static void parseQuantifierType(PsiBuilder builder) {
if (builder.getTokenType() == RegExpTT.PLUS) {
builder.advanceLexer();
} else if (builder.getTokenType() == RegExpTT.QUEST) {
builder.advanceLexer();
} else {
if (RegExpTT.QUANTIFIERS.contains(builder.getTokenType())) {
builder.error("Dangling metacharacter");
}
}
}
/**
* CLASS ::= "[" NEGATION DEFLIST "]"
* NEGATION ::= "^" | ""
* DEFLIST ::= INTERSECTION DEFLIST
* INTERSECTION ::= INTERSECTION "&&" CLASSDEF | CLASSDEF
* CLASSDEF ::= CLASS | SIMPLE_CLASSDEF | ""
* SIMPLE_CLASSDEF ::= CHARACTER | CHARACTER "-" CLASSDEF
*/
private PsiBuilder.Marker parseClass(PsiBuilder builder) {
final PsiBuilder.Marker marker = builder.mark();
if (builder.getTokenType() == RegExpTT.LBRACE) {
builder.advanceLexer();
boolean minOmitted = false;
if (builder.getTokenType() == RegExpTT.COMMA && myAllowOmitNumbersInQuantifiers) {
minOmitted = true;
builder.advanceLexer();
if (builder.getTokenType() == RegExpTT.CARET) {
builder.advanceLexer();
}
// DEFLIST
if (parseClassIntersection(builder)) {
while (RegExpTT.CHARACTERS2.contains(builder.getTokenType())
|| builder.getTokenType() == RegExpTT.CLASS_BEGIN
|| builder.getTokenType() == RegExpTT.PROPERTY)
{
parseClassIntersection(builder);
}
}
checkMatches(builder, RegExpTT.CLASS_END, "Unclosed character class");
marker.done(RegExpElementTypes.CLASS);
return marker;
}
private boolean parseClassIntersection(PsiBuilder builder) {
PsiBuilder.Marker marker = builder.mark();
if (!parseClassdef(builder, false)) {
marker.drop();
return false;
}
while (RegExpTT.ANDAND == builder.getTokenType()) {
builder.advanceLexer();
parseClassdef(builder, true);
marker.done(RegExpElementTypes.INTERSECTION);
marker = marker.precede();
}
marker.drop();
}
else if (builder.getTokenType() != RegExpTT.NUMBER && myAllowDanglingMetacharacters) {
marker.done(RegExpTT.CHARACTER);
return true;
}
private boolean parseClassdef(PsiBuilder builder, boolean mayBeEmpty) {
final IElementType token = builder.getTokenType();
if (token == RegExpTT.CLASS_BEGIN) {
parseClass(builder);
} else if (RegExpTT.CHARACTERS2.contains(token)) {
parseSimpleClassdef(builder);
} else if (token == RegExpTT.PROPERTY) {
parseProperty(builder);
} else if (mayBeEmpty) {
// TODO: no test coverage
return true;
} else {
return false;
}
return true;
}
private void parseSimpleClassdef(PsiBuilder builder) {
assert RegExpTT.CHARACTERS2.contains(builder.getTokenType());
final PsiBuilder.Marker marker = builder.mark();
makeChar(builder);
IElementType t = builder.getTokenType();
if (t == RegExpTT.MINUS) {
final PsiBuilder.Marker m = builder.mark();
builder.advanceLexer();
t = builder.getTokenType();
if (RegExpTT.CHARACTERS2.contains(t)) {
m.drop();
makeChar(builder);
marker.done(RegExpElementTypes.CHAR_RANGE);
} else {
marker.drop();
m.done(t == RegExpTT.CHAR_CLASS ?
RegExpElementTypes.SIMPLE_CLASS :
RegExpElementTypes.CHAR);
if (t == RegExpTT.CLASS_END) { // [a-]
return;
} else if (t == RegExpTT.CLASS_BEGIN) { // [a-[b]]
if (parseClassdef(builder, false)) {
return;
}
}
builder.error("Illegal character range");
}
} else {
marker.drop();
}
}
private static void makeChar(PsiBuilder builder) {
final IElementType t = builder.getTokenType();
PsiBuilder.Marker m = builder.mark();
}
else {
checkMatches(builder, RegExpTT.NUMBER, "Number expected");
}
if (builder.getTokenType() == RegExpTT.RBRACE) {
builder.advanceLexer();
m.done(t == RegExpTT.CHAR_CLASS ?
RegExpElementTypes.SIMPLE_CLASS :
RegExpElementTypes.CHAR);
}
/**
* GROUP ::= "(" PATTERN ")" | TERM
* TERM ::= "." | "$" | "^" | CHAR | CLASS | BACKREF
*/
@Nullable
private PsiBuilder.Marker parseGroup(PsiBuilder builder) {
final IElementType type = builder.getTokenType();
final PsiBuilder.Marker marker = builder.mark();
if (RegExpTT.GROUPS.contains(type)) {
builder.advanceLexer();
if (!parsePattern(builder)) {
patternExpected(builder);
} else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.GROUP);
} else if (type == RegExpTT.SET_OPTIONS) {
builder.advanceLexer();
final PsiBuilder.Marker o = builder.mark();
if (builder.getTokenType() == RegExpTT.OPTIONS_ON) {
builder.advanceLexer();
}
if (builder.getTokenType() == RegExpTT.OPTIONS_OFF) {
builder.advanceLexer();
}
o.done(RegExpElementTypes.OPTIONS);
if (builder.getTokenType() == RegExpTT.COLON) {
builder.advanceLexer();
if (!parsePattern(builder)) {
// TODO: no test coverage
patternExpected(builder);
} else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.GROUP);
} else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed options group");
marker.done(RegExpElementTypes.SET_OPTIONS);
}
} else if (type == StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN) {
builder.error("Illegal/unsupported escape sequence");
builder.advanceLexer();
marker.done(RegExpElementTypes.CHAR);
} else if (RegExpTT.CHARACTERS.contains(type)) {
builder.advanceLexer();
marker.done(RegExpElementTypes.CHAR);
} else if (RegExpTT.BOUNDARIES.contains(type)) {
builder.advanceLexer();
marker.done(RegExpElementTypes.BOUNDARY);
} else if (type == RegExpTT.BACKREF) {
builder.advanceLexer();
marker.done(RegExpElementTypes.BACKREF);
} else if (type == RegExpTT.PYTHON_NAMED_GROUP) {
builder.advanceLexer();
checkMatches(builder, RegExpTT.NAME, "Group name expected");
checkMatches(builder, RegExpTT.GT, "Unclosed group name");
if (!parsePattern(builder)) {
patternExpected(builder);
} else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.GROUP);
} else if (type == RegExpTT.PYTHON_NAMED_GROUP_REF) {
builder.advanceLexer();
checkMatches(builder, RegExpTT.NAME, "Group name expected");
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group reference");
marker.done(RegExpElementTypes.PY_NAMED_GROUP_REF);
} else if (type == RegExpTT.PYTHON_COND_REF) {
builder.advanceLexer();
if (builder.getTokenType() == RegExpTT.NAME || builder.getTokenType() == RegExpTT.NUMBER) {
builder.advanceLexer();
}
else {
builder.error("Group name or number expected");
}
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group reference");
if (!parseBranch(builder)) {
patternExpected(builder);
} else {
if (builder.getTokenType() == RegExpTT.UNION) {
builder.advanceLexer();
if (!parseBranch(builder)) {
patternExpected(builder);
}
}
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.PY_COND_REF);
} else if (type == RegExpTT.PROPERTY) {
parseProperty(builder);
marker.done(RegExpElementTypes.PROPERTY);
} else if (RegExpTT.SIMPLE_CLASSES.contains(type)) {
builder.advanceLexer();
marker.done(RegExpElementTypes.SIMPLE_CLASS);
} else if (type == RegExpTT.CLASS_BEGIN) {
marker.drop();
return parseClass(builder);
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
}
else {
if (!minOmitted) {
checkMatches(builder, RegExpTT.COMMA, "',' expected");
}
else if (type == RegExpTT.LBRACE && myAllowDanglingMetacharacters) {
builder.advanceLexer();
marker.done(RegExpElementTypes.CHAR);
if (builder.getTokenType() == RegExpTT.RBRACE) {
builder.advanceLexer();
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
}
else if (builder.getTokenType() == RegExpTT.NUMBER) {
builder.advanceLexer();
checkMatches(builder, RegExpTT.RBRACE, "'}' expected");
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
}
else {
marker.drop();
return null;
builder.error("'}' or number expected");
marker.done(RegExpElementTypes.QUANTIFIER);
return true;
}
return marker;
}
}
else if (RegExpTT.QUANTIFIERS.contains(builder.getTokenType())) {
builder.advanceLexer();
parseQuantifierType(builder);
marker.done(RegExpElementTypes.QUANTIFIER);
}
else {
marker.drop();
return false;
}
private static void parseProperty(PsiBuilder builder) {
checkMatches(builder, RegExpTT.PROPERTY, "'\\p' expected");
return true;
}
checkMatches(builder, RegExpTT.LBRACE, "Character category expected");
if (builder.getTokenType() == RegExpTT.NAME) {
builder.advanceLexer();
} else if (builder.getTokenType() == RegExpTT.RBRACE) {
builder.error("Empty character family");
} else {
builder.error("Character family name expected");
builder.advanceLexer();
}
checkMatches(builder, RegExpTT.RBRACE, "Unclosed character family");
private static void parseQuantifierType(PsiBuilder builder) {
if (builder.getTokenType() == RegExpTT.PLUS) {
builder.advanceLexer();
}
else if (builder.getTokenType() == RegExpTT.QUEST) {
builder.advanceLexer();
}
else {
if (RegExpTT.QUANTIFIERS.contains(builder.getTokenType())) {
builder.error("Dangling metacharacter");
}
}
}
/**
* CLASS ::= "[" NEGATION DEFLIST "]"
* NEGATION ::= "^" | ""
* DEFLIST ::= INTERSECTION DEFLIST
* INTERSECTION ::= INTERSECTION "&&" CLASSDEF | CLASSDEF
* CLASSDEF ::= CLASS | SIMPLE_CLASSDEF | ""
* SIMPLE_CLASSDEF ::= CHARACTER | CHARACTER "-" CLASSDEF
*/
private PsiBuilder.Marker parseClass(PsiBuilder builder) {
final PsiBuilder.Marker marker = builder.mark();
builder.advanceLexer();
if (builder.getTokenType() == RegExpTT.CARET) {
builder.advanceLexer();
}
private static void patternExpected(PsiBuilder builder) {
final IElementType token = builder.getTokenType();
if (token == RegExpTT.GROUP_END) {
builder.error("Unmatched closing ')'");
} else if (RegExpTT.QUANTIFIERS.contains(token)) {
builder.error("Dangling metacharacter");
} else {
builder.error("Pattern expected");
}
// DEFLIST
if (parseClassIntersection(builder)) {
while (RegExpTT.CHARACTERS2.contains(builder.getTokenType()) ||
builder.getTokenType() == RegExpTT.CLASS_BEGIN ||
builder.getTokenType() == RegExpTT.PROPERTY) {
parseClassIntersection(builder);
}
}
protected static void checkMatches(final PsiBuilder builder, final IElementType token, final String message) {
if (builder.getTokenType() == token) {
builder.advanceLexer();
} else {
builder.error(message);
}
checkMatches(builder, RegExpTT.CLASS_END, "Unclosed character class");
marker.done(RegExpElementTypes.CLASS);
return marker;
}
private boolean parseClassIntersection(PsiBuilder builder) {
PsiBuilder.Marker marker = builder.mark();
if (!parseClassdef(builder, false)) {
marker.drop();
return false;
}
while (RegExpTT.ANDAND == builder.getTokenType()) {
builder.advanceLexer();
parseClassdef(builder, true);
marker.done(RegExpElementTypes.INTERSECTION);
marker = marker.precede();
}
marker.drop();
return true;
}
private boolean parseClassdef(PsiBuilder builder, boolean mayBeEmpty) {
final IElementType token = builder.getTokenType();
if (token == RegExpTT.CLASS_BEGIN) {
parseClass(builder);
}
else if (RegExpTT.CHARACTERS2.contains(token)) {
parseSimpleClassdef(builder);
}
else if (token == RegExpTT.PROPERTY) {
parseProperty(builder);
}
else if (mayBeEmpty) {
// TODO: no test coverage
return true;
}
else {
return false;
}
return true;
}
private void parseSimpleClassdef(PsiBuilder builder) {
assert RegExpTT.CHARACTERS2.contains(builder.getTokenType());
final PsiBuilder.Marker marker = builder.mark();
makeChar(builder);
IElementType t = builder.getTokenType();
if (t == RegExpTT.MINUS) {
final PsiBuilder.Marker m = builder.mark();
builder.advanceLexer();
t = builder.getTokenType();
if (RegExpTT.CHARACTERS2.contains(t)) {
m.drop();
makeChar(builder);
marker.done(RegExpElementTypes.CHAR_RANGE);
}
else {
marker.drop();
m.done(t == RegExpTT.CHAR_CLASS ? RegExpElementTypes.SIMPLE_CLASS : RegExpElementTypes.CHAR);
if (t == RegExpTT.CLASS_END) { // [a-]
return;
}
else if (t == RegExpTT.CLASS_BEGIN) { // [a-[b]]
if (parseClassdef(builder, false)) {
return;
}
}
builder.error("Illegal character range");
}
}
else {
marker.drop();
}
}
private static void makeChar(PsiBuilder builder) {
final IElementType t = builder.getTokenType();
PsiBuilder.Marker m = builder.mark();
builder.advanceLexer();
m.done(t == RegExpTT.CHAR_CLASS ? RegExpElementTypes.SIMPLE_CLASS : RegExpElementTypes.CHAR);
}
/**
* GROUP ::= "(" PATTERN ")" | TERM
* TERM ::= "." | "$" | "^" | CHAR | CLASS | BACKREF
*/
@Nullable
private PsiBuilder.Marker parseGroup(PsiBuilder builder) {
final IElementType type = builder.getTokenType();
final PsiBuilder.Marker marker = builder.mark();
if (RegExpTT.GROUPS.contains(type)) {
builder.advanceLexer();
if (!parsePattern(builder)) {
patternExpected(builder);
}
else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.GROUP);
}
else if (type == RegExpTT.SET_OPTIONS) {
builder.advanceLexer();
final PsiBuilder.Marker o = builder.mark();
if (builder.getTokenType() == RegExpTT.OPTIONS_ON) {
builder.advanceLexer();
}
if (builder.getTokenType() == RegExpTT.OPTIONS_OFF) {
builder.advanceLexer();
}
o.done(RegExpElementTypes.OPTIONS);
if (builder.getTokenType() == RegExpTT.COLON) {
builder.advanceLexer();
if (!parsePattern(builder)) {
// TODO: no test coverage
patternExpected(builder);
}
else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.GROUP);
}
else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed options group");
marker.done(RegExpElementTypes.SET_OPTIONS);
}
}
else if (type == StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN) {
builder.error("Illegal/unsupported escape sequence");
builder.advanceLexer();
marker.done(RegExpElementTypes.CHAR);
}
else if (RegExpTT.CHARACTERS.contains(type)) {
builder.advanceLexer();
marker.done(RegExpElementTypes.CHAR);
}
else if (RegExpTT.BOUNDARIES.contains(type)) {
builder.advanceLexer();
marker.done(RegExpElementTypes.BOUNDARY);
}
else if (type == RegExpTT.BACKREF) {
builder.advanceLexer();
marker.done(RegExpElementTypes.BACKREF);
}
else if (type == RegExpTT.PYTHON_NAMED_GROUP || type == RegExpTT.RUBY_NAMED_GROUP || type == RegExpTT.RUBY_QUOTED_NAMED_GROUP) {
builder.advanceLexer();
checkMatches(builder, RegExpTT.NAME, "Group name expected");
checkMatches(builder, type == RegExpTT.RUBY_QUOTED_NAMED_GROUP ? RegExpTT.QUOTE : RegExpTT.GT, "Unclosed group name");
if (!parsePattern(builder)) {
patternExpected(builder);
}
else {
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.GROUP);
}
else if (type == RegExpTT.PYTHON_NAMED_GROUP_REF) {
builder.advanceLexer();
checkMatches(builder, RegExpTT.NAME, "Group name expected");
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group reference");
marker.done(RegExpElementTypes.PY_NAMED_GROUP_REF);
}
else if (type == RegExpTT.PYTHON_COND_REF) {
builder.advanceLexer();
if (builder.getTokenType() == RegExpTT.NAME || builder.getTokenType() == RegExpTT.NUMBER) {
builder.advanceLexer();
}
else {
builder.error("Group name or number expected");
}
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group reference");
if (!parseBranch(builder)) {
patternExpected(builder);
}
else {
if (builder.getTokenType() == RegExpTT.UNION) {
builder.advanceLexer();
if (!parseBranch(builder)) {
patternExpected(builder);
}
}
checkMatches(builder, RegExpTT.GROUP_END, "Unclosed group");
}
marker.done(RegExpElementTypes.PY_COND_REF);
}
else if (type == RegExpTT.PROPERTY) {
parseProperty(builder);
marker.done(RegExpElementTypes.PROPERTY);
}
else if (RegExpTT.SIMPLE_CLASSES.contains(type)) {
builder.advanceLexer();
marker.done(RegExpElementTypes.SIMPLE_CLASS);
}
else if (type == RegExpTT.CLASS_BEGIN) {
marker.drop();
return parseClass(builder);
}
else if (type == RegExpTT.LBRACE && myAllowDanglingMetacharacters) {
builder.advanceLexer();
marker.done(RegExpElementTypes.CHAR);
}
else {
marker.drop();
return null;
}
return marker;
}
private static void parseProperty(PsiBuilder builder) {
checkMatches(builder, RegExpTT.PROPERTY, "'\\p' expected");
checkMatches(builder, RegExpTT.LBRACE, "Character category expected");
if (builder.getTokenType() == RegExpTT.NAME) {
builder.advanceLexer();
}
else if (builder.getTokenType() == RegExpTT.RBRACE) {
builder.error("Empty character family");
}
else {
builder.error("Character family name expected");
builder.advanceLexer();
}
checkMatches(builder, RegExpTT.RBRACE, "Unclosed character family");
}
private static void patternExpected(PsiBuilder builder) {
final IElementType token = builder.getTokenType();
if (token == RegExpTT.GROUP_END) {
builder.error("Unmatched closing ')'");
}
else if (RegExpTT.QUANTIFIERS.contains(token)) {
builder.error("Dangling metacharacter");
}
else {
builder.error("Pattern expected");
}
}
protected static void checkMatches(final PsiBuilder builder, final IElementType token, final String message) {
if (builder.getTokenType() == token) {
builder.advanceLexer();
}
else {
builder.error(message);
}
}
}
@@ -64,8 +64,10 @@ public interface RegExpTT {
IElementType DOT = new RegExpElementType("DOT");
IElementType UNION = new RegExpElementType("UNION");
// > in Python named group
// > in Python/Ruby named group
IElementType GT = new RegExpElementType("GT");
// ' in Ruby quoted named group
IElementType QUOTE = new RegExpElementType("QUOTE");
// "\b" | "\t" | "\f" | "\r" | "\n"
IElementType CTRL_CHARACTER = new RegExpElementType("CTRL_CHARACTER");
@@ -107,6 +109,11 @@ public interface RegExpTT {
// (?(id/name)yes-pattern|no-pattern)
IElementType PYTHON_COND_REF = new RegExpElementType("PYTHON_COND_REF");
// (?<name>...
IElementType RUBY_NAMED_GROUP = new RegExpElementType("RUBY_NAMED_GROUP");
// (?'name'...
IElementType RUBY_QUOTED_NAMED_GROUP = new RegExpElementType("RUBY_QUOTED_NAMED_GROUP");
TokenSet KEYWORDS = TokenSet.create(DOT, STAR, QUEST, PLUS);
@@ -1,12 +1,13 @@
/* The following code was generated by JFlex 1.4.3 on 22.03.10 22:06 */
/* The following code was generated by JFlex 1.4.3 on 01.06.10 15:51 */
/* It's an automatically generated code. Do not modify it. */
package org.intellij.lang.regexp;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import java.util.LinkedList;
import com.intellij.psi.StringEscapesTokenTypes;
import com.intellij.psi.tree.IElementType;
import java.util.LinkedList;
// IDEADEV-11055
@SuppressWarnings({ "ALL", "SameParameterValue", "WeakerAccess", "SameReturnValue", "RedundantThrows", "UnusedDeclaration", "UnusedDeclaration" })
@@ -14,8 +15,8 @@ import com.intellij.psi.StringEscapesTokenTypes;
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.4.3
* on 22.03.10 22:06 from the specification file
* <tt>C:/JetBrains/IDEA/tools/lexer/../../community/RegExpSupport/src/org/intellij/lang/regexp/regexp-lexer.flex</tt>
* on 01.06.10 15:51 from the specification file
* <tt>C:/JetBrains/idea.branches/maia/tools/lexer/../../community/RegExpSupport/src/org/intellij/lang/regexp/regexp-lexer.flex</tt>
*/
class _RegExLexer implements FlexLexer {
/** initial size of the lookahead buffer */
@@ -23,15 +24,16 @@ class _RegExLexer implements FlexLexer {
/** lexical states */
public static final int EMBRACED = 4;
public static final int PY_NAMED_GROUP = 16;
public static final int PY_NAMED_GROUP_REF = 18;
public static final int PY_NAMED_GROUP_REF = 20;
public static final int QUOTED = 2;
public static final int OPTIONS = 12;
public static final int PY_COND_REF = 20;
public static final int PY_COND_REF = 22;
public static final int YYINITIAL = 0;
public static final int NAMED_GROUP = 16;
public static final int CLASS2 = 8;
public static final int CLASS1 = 6;
public static final int COMMENT = 14;
public static final int QUOTED_NAMED_GROUP = 18;
public static final int PROP = 10;
/**
@@ -42,15 +44,15 @@ class _RegExLexer implements FlexLexer {
*/
private static final int ZZ_LEXSTATE[] = {
0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
8, 8, 9, 9, 10, 10
8, 8, 9, 9, 10, 10, 11, 11
};
/**
* Translates characters to character classes
*/
private static final String ZZ_CMAP_PACKED =
"\10\0\2\52\1\13\1\0\1\52\1\61\22\0\1\45\1\56\1\0"+
"\1\60\1\15\1\0\1\51\1\0\1\4\1\5\1\17\1\20\1\50"+
"\10\0\2\52\1\13\1\0\1\52\1\62\22\0\1\45\1\56\1\0"+
"\1\60\1\15\1\0\1\51\1\61\1\4\1\5\1\17\1\20\1\50"+
"\1\43\1\3\1\0\1\2\7\42\2\1\1\53\1\0\1\57\1\55"+
"\1\54\1\16\1\0\1\24\1\24\1\30\1\27\1\37\1\35\1\25"+
"\1\44\1\32\6\44\1\34\1\36\1\44\1\26\3\44\1\26\1\26"+
@@ -128,20 +130,20 @@ class _RegExLexer implements FlexLexer {
private static final int [] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
"\6\0\1\1\1\2\3\0\1\3\1\4\1\5\1\6"+
"\6\0\1\1\1\2\4\0\1\3\1\4\1\5\1\6"+
"\1\7\1\10\1\11\1\12\1\13\1\14\1\15\1\16"+
"\1\17\1\20\1\21\1\22\1\23\1\3\1\24\1\25"+
"\1\26\1\27\1\30\1\31\1\32\1\33\1\34\1\3"+
"\1\35\1\36\1\35\1\37\1\40\1\1\1\41\1\42"+
"\1\2\1\43\1\44\1\45\1\46\1\47\1\50\1\51"+
"\1\52\1\53\1\54\1\55\2\56\1\57\1\11\1\60"+
"\1\61\1\62\1\63\1\64\1\65\1\0\1\66\1\67"+
"\1\70\2\0\1\71\1\72\2\61\2\62\1\73\1\74"+
"\1\75\1\76\1\0\1\71\1\61\1\77\2\62\1\100"+
"\1\71\3\62\1\101";
"\1\52\1\53\1\54\1\55\1\56\2\57\1\60\1\11"+
"\1\61\1\62\1\63\1\64\1\65\1\66\1\0\1\67"+
"\1\70\1\71\1\72\1\0\1\73\1\74\1\75\2\62"+
"\2\63\1\76\1\77\1\100\1\101\1\0\1\74\1\62"+
"\1\102\2\63\1\103\1\74\3\63\1\104";
private static int [] zzUnpackAction() {
int [] result = new int[97];
int [] result = new int[100];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
@@ -166,22 +168,22 @@ class _RegExLexer implements FlexLexer {
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
private static final String ZZ_ROWMAP_PACKED_0 =
"\0\0\0\62\0\144\0\226\0\310\0\372\0\u012c\0\u015e"+
"\0\u0190\0\u01c2\0\u01f4\0\u0226\0\u0226\0\u0258\0\u0226\0\u0226"+
"\0\u028a\0\u02bc\0\u0226\0\u0226\0\u0226\0\u0226\0\u0226\0\u0226"+
"\0\u0226\0\u0226\0\u0226\0\u0226\0\u02ee\0\u0226\0\u0320\0\u0226"+
"\0\u0352\0\u0226\0\u0226\0\u0226\0\u0226\0\u0226\0\u0384\0\u0226"+
"\0\u0226\0\u028a\0\u0226\0\u0226\0\u03b6\0\u03e8\0\u0226\0\u0226"+
"\0\u0226\0\u041a\0\u0226\0\u0226\0\u044c\0\u047e\0\u0226\0\u0226"+
"\0\u0226\0\u0226\0\u0226\0\u0226\0\u04b0\0\u0226\0\u0226\0\u0226"+
"\0\u04e2\0\u0514\0\u0226\0\u0226\0\u0226\0\u0546\0\u0226\0\u0226"+
"\0\u0226\0\u0578\0\u05aa\0\u05dc\0\u0226\0\u060e\0\u0640\0\u0672"+
"\0\u06a4\0\u0226\0\u0226\0\u0226\0\u0226\0\u06d6\0\u0708\0\u0226"+
"\0\u0226\0\u073a\0\u076c\0\u0226\0\u0226\0\u079e\0\u07d0\0\u0226"+
"\0\u0226";
"\0\0\0\63\0\146\0\231\0\314\0\377\0\u0132\0\u0165"+
"\0\u0198\0\u01cb\0\u01fe\0\u0231\0\u0264\0\u0264\0\u0297\0\u0264"+
"\0\u0264\0\u02ca\0\u02fd\0\u0264\0\u0264\0\u0264\0\u0264\0\u0264"+
"\0\u0264\0\u0264\0\u0264\0\u0264\0\u0264\0\u0330\0\u0264\0\u0363"+
"\0\u0264\0\u0396\0\u0264\0\u0264\0\u0264\0\u0264\0\u0264\0\u03c9"+
"\0\u0264\0\u0264\0\u02ca\0\u0264\0\u0264\0\u03fc\0\u042f\0\u0264"+
"\0\u0264\0\u0264\0\u0264\0\u0462\0\u0264\0\u0264\0\u0495\0\u04c8"+
"\0\u0264\0\u0264\0\u0264\0\u0264\0\u0264\0\u0264\0\u04fb\0\u0264"+
"\0\u0264\0\u0264\0\u052e\0\u0561\0\u0264\0\u0264\0\u0264\0\u0594"+
"\0\u0264\0\u0264\0\u0264\0\u05c7\0\u05fa\0\u0264\0\u062d\0\u0264"+
"\0\u0660\0\u0693\0\u06c6\0\u06f9\0\u0264\0\u0264\0\u0264\0\u0264"+
"\0\u072c\0\u075f\0\u0264\0\u0264\0\u0792\0\u07c5\0\u0264\0\u0264"+
"\0\u07f8\0\u082b\0\u0264\0\u0264";
private static int [] zzUnpackRowMap() {
int [] result = new int[97];
int [] result = new int[100];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
@@ -204,46 +206,47 @@ class _RegExLexer implements FlexLexer {
private static final int [] ZZ_TRANS = zzUnpackTrans();
private static final String ZZ_TRANS_PACKED_0 =
"\3\14\1\15\1\16\1\17\1\20\1\14\1\21\1\14"+
"\1\22\1\23\1\24\1\25\1\26\1\27\1\30\1\31"+
"\21\14\1\32\1\14\1\33\4\14\1\23\5\14\1\34"+
"\1\23\12\14\1\35\1\0\46\14\1\36\2\37\4\36"+
"\1\40\12\36\20\41\1\37\1\36\1\41\2\36\1\37"+
"\1\42\11\36\11\43\1\44\1\43\1\0\46\43\6\14"+
"\1\20\1\14\1\21\1\45\1\22\1\46\1\24\26\14"+
"\1\32\5\14\1\47\1\46\6\14\1\46\6\50\1\51"+
"\1\50\1\52\1\50\1\22\47\50\5\53\1\54\14\53"+
"\20\55\1\53\1\56\1\55\6\53\1\57\6\53\13\10"+
"\1\60\45\10\1\60\22\0\20\41\2\0\1\41\7\0"+
"\1\61\5\0\5\53\1\54\14\53\20\41\2\53\1\41"+
"\16\53\2\37\2\53\1\54\14\53\20\41\1\37\1\53"+
"\1\41\2\53\1\37\12\53\100\0\1\62\54\0\1\63"+
"\50\0\1\64\1\65\1\66\4\67\1\64\1\67\1\64"+
"\1\67\1\70\6\67\2\71\2\72\2\73\1\74\1\75"+
"\1\74\2\76\1\77\1\100\1\77\1\101\1\102\1\65"+
"\1\67\1\77\1\70\4\64\1\70\6\64\1\70\37\0"+
"\1\103\23\0\2\37\37\0\1\37\4\0\1\37\13\0"+
"\2\41\17\0\21\41\1\0\1\41\1\0\2\41\63\0"+
"\1\104\32\0\20\55\2\0\1\55\37\0\20\56\2\0"+
"\1\56\21\0\1\105\27\0\1\106\16\0\2\107\1\110"+
"\1\111\1\112\1\113\2\0\2\65\37\0\1\65\21\0"+
"\1\114\37\0\1\114\17\0\62\115\1\116\2\117\20\116"+
"\2\117\2\116\3\117\3\116\1\117\1\116\1\117\2\116"+
"\1\117\17\116\1\120\2\121\20\120\2\121\2\120\3\121"+
"\3\120\1\121\1\120\1\121\2\120\1\121\17\120\55\0"+
"\1\122\1\0\1\123\57\0\1\124\1\125\3\0\5\126"+
"\1\0\54\126\2\0\1\127\37\0\1\127\17\0\63\130"+
"\2\131\20\130\2\131\2\130\3\131\3\130\1\131\1\130"+
"\1\131\2\130\1\131\17\130\63\132\2\133\20\132\2\133"+
"\2\132\3\133\3\132\1\133\1\132\1\133\2\132\1\133"+
"\17\132\5\126\1\134\54\126\2\0\1\135\37\0\1\135"+
"\17\0\63\136\2\137\20\136\2\137\2\136\3\137\3\136"+
"\1\137\1\136\1\137\2\136\1\137\17\136\63\140\2\141"+
"\20\140\2\141\2\140\3\141\3\140\1\141\1\140\1\141"+
"\2\140\1\141\17\140";
"\3\15\1\16\1\17\1\20\1\21\1\15\1\22\1\15"+
"\1\23\1\24\1\25\1\26\1\27\1\30\1\31\1\32"+
"\21\15\1\33\1\15\1\34\4\15\1\24\5\15\1\35"+
"\1\15\1\24\12\15\1\36\1\0\47\15\1\37\2\40"+
"\4\37\1\41\12\37\20\42\1\40\1\37\1\42\2\37"+
"\1\40\1\43\12\37\11\44\1\45\1\44\1\0\47\44"+
"\6\15\1\21\1\15\1\22\1\46\1\23\1\47\1\25"+
"\26\15\1\33\5\15\1\50\1\47\7\15\1\47\6\51"+
"\1\52\1\51\1\53\1\51\1\23\50\51\5\54\1\55"+
"\14\54\20\56\1\54\1\57\1\56\6\54\1\60\7\54"+
"\13\10\1\61\46\10\1\61\22\54\20\42\2\54\1\42"+
"\7\54\1\62\30\54\20\42\2\54\1\42\14\54\1\63"+
"\6\54\1\55\14\54\20\42\2\54\1\42\17\54\2\40"+
"\2\54\1\55\14\54\20\42\1\40\1\54\1\42\2\54"+
"\1\40\13\54\101\0\1\64\55\0\1\65\51\0\1\66"+
"\1\67\1\70\4\71\1\66\1\71\1\66\1\71\1\72"+
"\6\71\2\73\2\74\2\75\1\76\1\77\1\76\2\100"+
"\1\101\1\102\1\101\1\103\1\104\1\67\1\71\1\101"+
"\1\72\4\66\1\72\7\66\1\72\37\0\1\105\24\0"+
"\2\40\37\0\1\40\4\0\1\40\14\0\2\42\17\0"+
"\21\42\1\0\1\42\1\0\2\42\64\0\1\106\33\0"+
"\20\56\2\0\1\56\40\0\20\57\2\0\1\57\22\0"+
"\1\107\27\0\1\110\16\0\2\111\1\112\1\113\1\114"+
"\1\115\1\116\2\0\2\67\37\0\1\67\22\0\1\117"+
"\37\0\1\117\20\0\63\120\1\121\2\122\20\121\2\122"+
"\2\121\3\122\3\121\1\122\1\121\1\122\2\121\1\122"+
"\20\121\1\123\2\124\20\123\2\124\2\123\3\124\3\123"+
"\1\124\1\123\1\124\2\123\1\124\20\123\55\0\1\125"+
"\1\0\1\126\60\0\1\127\1\130\4\0\5\131\1\0"+
"\55\131\2\0\1\132\37\0\1\132\20\0\64\133\2\134"+
"\20\133\2\134\2\133\3\134\3\133\1\134\1\133\1\134"+
"\2\133\1\134\20\133\64\135\2\136\20\135\2\136\2\135"+
"\3\136\3\135\1\136\1\135\1\136\2\135\1\136\20\135"+
"\5\131\1\137\55\131\2\0\1\140\37\0\1\140\20\0"+
"\64\141\2\142\20\141\2\142\2\141\3\142\3\141\1\142"+
"\1\141\1\142\2\141\1\142\20\141\64\143\2\144\20\143"+
"\2\144\2\143\3\144\3\143\1\144\1\143\1\144\2\143"+
"\1\144\20\143";
private static int [] zzUnpackTrans() {
int [] result = new int[2050];
int [] result = new int[2142];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
@@ -284,15 +287,15 @@ class _RegExLexer implements FlexLexer {
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 =
"\6\0\2\1\3\0\2\11\1\1\2\11\2\1\12\11"+
"\6\0\2\1\4\0\2\11\1\1\2\11\2\1\12\11"+
"\1\1\1\11\1\1\1\11\1\1\5\11\1\1\2\11"+
"\1\1\2\11\2\1\3\11\1\1\2\11\2\1\6\11"+
"\1\1\3\11\2\1\3\11\1\0\3\11\2\0\1\1"+
"\1\11\4\1\4\11\1\0\1\1\2\11\2\1\2\11"+
"\2\1\2\11";
"\1\1\2\11\2\1\4\11\1\1\2\11\2\1\6\11"+
"\1\1\3\11\2\1\3\11\1\0\3\11\1\1\1\0"+
"\1\11\1\1\1\11\4\1\4\11\1\0\1\1\2\11"+
"\2\1\2\11\2\1\2\11";
private static int [] zzUnpackAttribute() {
int [] result = new int[97];
int [] result = new int[100];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
@@ -648,257 +651,269 @@ class _RegExLexer implements FlexLexer {
case 7:
{ if (yystate() != CLASS2) yypushstate(EMBRACED); return RegExpTT.LBRACE;
}
case 66: break;
case 69: break;
case 6:
{ return RegExpTT.GROUP_END;
}
case 67: break;
case 37:
case 70: break;
case 38:
// lookahead expression with fixed base length
zzMarkedPos = zzStartRead + 1;
{ yypushstate(CLASS1); return RegExpTT.CLASS_BEGIN;
}
case 68: break;
case 71: break;
case 14:
{ return RegExpTT.STAR;
}
case 69: break;
case 41:
case 72: break;
case 42:
{ return RegExpTT.ESC_CHARACTER;
}
case 70: break;
case 73: break;
case 28:
{ return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.ESC_CHARACTER;
}
case 71: break;
case 42:
case 74: break;
case 43:
{ return commentMode ? RegExpTT.CHARACTER : RegExpTT.REDUNDANT_ESCAPE;
}
case 72: break;
case 38:
case 75: break;
case 39:
{ return RegExpTT.REDUNDANT_ESCAPE;
}
case 73: break;
case 76: break;
case 24:
{ return RegExpTT.COMMA;
}
case 74: break;
case 61:
case 77: break;
case 58:
{ yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP;
}
case 78: break;
case 64:
{ return RegExpTT.POS_LOOKBEHIND;
}
case 75: break;
case 40:
case 79: break;
case 41:
{ return RegExpTT.BAD_OCT_VALUE;
}
case 76: break;
case 80: break;
case 10:
{ return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.CTRL_CHARACTER;
}
case 77: break;
case 81: break;
case 30:
{ yypopstate(); yypushstate(EMBRACED); return RegExpTT.LBRACE;
}
case 78: break;
case 50:
case 82: break;
case 51:
{ return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN;
}
case 79: break;
case 62:
case 83: break;
case 65:
{ return RegExpTT.NEG_LOOKBEHIND;
}
case 80: break;
case 65:
case 84: break;
case 68:
{ return RegExpTT.UNICODE_CHAR;
}
case 81: break;
case 58:
case 85: break;
case 61:
{ if (xmlSchemaMode) { yypushback(1); return RegExpTT.CHAR_CLASS; } else return RegExpTT.CTRL;
}
case 82: break;
case 36:
case 86: break;
case 37:
{ yybegin(OPTIONS); return RegExpTT.SET_OPTIONS;
}
case 83: break;
case 87: break;
case 12:
{ return RegExpTT.DOLLAR;
}
case 84: break;
case 51:
case 88: break;
case 52:
{ yypopstate(); return RegExpTT.QUOTE_END;
}
case 85: break;
case 55:
case 89: break;
case 56:
{ return RegExpTT.POS_LOOKAHEAD;
}
case 86: break;
case 90: break;
case 22:
{ yypopstate(); return RegExpTT.RBRACE;
}
case 87: break;
case 91: break;
case 3:
{ return RegExpTT.CHARACTER;
}
case 88: break;
case 56:
case 92: break;
case 57:
{ return RegExpTT.NEG_LOOKAHEAD;
}
case 89: break;
case 43:
case 93: break;
case 44:
{ return RegExpTT.ESC_CTRL_CHARACTER;
}
case 90: break;
case 94: break;
case 23:
{ return RegExpTT.NAME;
}
case 91: break;
case 52:
case 95: break;
case 53:
{ return RegExpTT.ANDAND;
}
case 92: break;
case 96: break;
case 13:
{ return RegExpTT.QUEST;
}
case 93: break;
case 45:
case 97: break;
case 46:
{ return RegExpTT.CHAR_CLASS;
}
case 94: break;
case 98: break;
case 17:
{ return RegExpTT.MINUS;
}
case 95: break;
case 64:
case 99: break;
case 67:
{ return RegExpTT.COMMENT;
}
case 96: break;
case 39:
case 100: break;
case 40:
{ return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER;
}
case 97: break;
case 101: break;
case 16:
{ return RegExpTT.UNION;
}
case 98: break;
case 46:
case 102: break;
case 47:
{ if (xmlSchemaMode) return RegExpTT.CHAR_CLASS; else return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN;
}
case 99: break;
case 57:
case 103: break;
case 60:
{ return RegExpTT.OCT_CHAR;
}
case 100: break;
case 104: break;
case 4:
{ return RegExpTT.DOT;
}
case 101: break;
case 105: break;
case 32:
{ yybegin(YYINITIAL); return RegExpTT.GROUP_END;
}
case 102: break;
case 106: break;
case 21:
{ return RegExpTT.NUMBER;
}
case 103: break;
case 107: break;
case 35:
{ yybegin(YYINITIAL); return RegExpTT.GT;
}
case 104: break;
case 53:
case 108: break;
case 59:
{ yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP;
}
case 109: break;
case 54:
{ yybegin(PY_COND_REF); return RegExpTT.PYTHON_COND_REF;
}
case 105: break;
case 47:
case 110: break;
case 48:
{ yypushstate(PROP); return RegExpTT.PROPERTY;
}
case 106: break;
case 111: break;
case 18:
{ return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.CHARACTER;
}
case 107: break;
case 112: break;
case 33:
{ handleOptions(); return RegExpTT.OPTIONS_OFF;
}
case 108: break;
case 113: break;
case 34:
{ yybegin(YYINITIAL); return RegExpTT.COLON;
}
case 109: break;
case 114: break;
case 25:
{ assert false : yytext();
}
case 110: break;
case 59:
case 115: break;
case 62:
{ yybegin(PY_NAMED_GROUP_REF); return RegExpTT.PYTHON_NAMED_GROUP_REF;
}
case 111: break;
case 116: break;
case 27:
{ yypopstate(); return RegExpTT.CLASS_END;
}
case 112: break;
case 117: break;
case 11:
{ return RegExpTT.CARET;
}
case 113: break;
case 118: break;
case 1:
{ handleOptions(); return RegExpTT.OPTIONS_ON;
}
case 114: break;
case 119: break;
case 29:
{ yypopstate(); yypushback(1);
}
case 115: break;
case 44:
case 120: break;
case 45:
{ return yystate() != CLASS2 ? RegExpTT.BOUNDARY : RegExpTT.ESC_CHARACTER;
}
case 116: break;
case 121: break;
case 63:
{ yybegin(NAMED_GROUP); return RegExpTT.PYTHON_NAMED_GROUP;
}
case 122: break;
case 19:
{ if (commentMode) { yypushstate(COMMENT); return RegExpTT.COMMENT; } else return RegExpTT.CHARACTER;
}
case 117: break;
case 63:
case 123: break;
case 66:
{ return RegExpTT.HEX_CHAR;
}
case 118: break;
case 60:
{ yybegin(PY_NAMED_GROUP); return RegExpTT.PYTHON_NAMED_GROUP;
}
case 119: break;
case 124: break;
case 5:
{ return RegExpTT.GROUP_BEGIN;
}
case 120: break;
case 125: break;
case 8:
{ yypushstate(CLASS2); return RegExpTT.CLASS_BEGIN;
}
case 121: break;
case 54:
case 126: break;
case 55:
{ return RegExpTT.NON_CAPT_GROUP;
}
case 122: break;
case 127: break;
case 9:
{ return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN;
}
case 123: break;
case 49:
case 128: break;
case 36:
{ yybegin(YYINITIAL); return RegExpTT.QUOTE;
}
case 129: break;
case 50:
{ return RegExpTT.BAD_HEX_VALUE;
}
case 124: break;
case 130: break;
case 2:
{ yypopstate(); return RegExpTT.COMMENT;
}
case 125: break;
case 48:
case 131: break;
case 49:
{ yypushstate(QUOTED); return RegExpTT.QUOTE_BEGIN;
}
case 126: break;
case 132: break;
case 26:
{ yybegin(CLASS2); return RegExpTT.CHARACTER;
}
case 127: break;
case 133: break;
case 15:
{ return RegExpTT.PLUS;
}
case 128: break;
case 134: break;
case 20:
{ if (allowDanglingMetacharacters) {
yypopstate(); yypushback(1);
@@ -906,11 +921,11 @@ class _RegExLexer implements FlexLexer {
return RegExpTT.BAD_CHARACTER;
}
}
case 129: break;
case 135: break;
case 31:
{ yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER;
}
case 130: break;
case 136: break;
default:
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
@@ -18,15 +18,17 @@ package org.intellij.lang.regexp.psi;
import org.jetbrains.annotations.Nullable;
public interface RegExpGroup extends RegExpAtom {
boolean isCapturing();
boolean isSimple();
boolean isCapturing();
@Nullable
RegExpPattern getPattern();
boolean isSimple();
boolean isPythonNamedGroup();
@Nullable
RegExpPattern getPattern();
@Nullable
String getGroupName();
boolean isPythonNamedGroup();
boolean isRubyNamedGroup();
@Nullable
String getGroupName();
}
@@ -24,38 +24,43 @@ import org.intellij.lang.regexp.psi.RegExpGroup;
import org.intellij.lang.regexp.psi.RegExpPattern;
public class RegExpGroupImpl extends RegExpElementImpl implements RegExpGroup {
public RegExpGroupImpl(ASTNode astNode) {
super(astNode);
}
public RegExpGroupImpl(ASTNode astNode) {
super(astNode);
}
public void accept(RegExpElementVisitor visitor) {
visitor.visitRegExpGroup(this);
}
public void accept(RegExpElementVisitor visitor) {
visitor.visitRegExpGroup(this);
}
public boolean isCapturing() {
final ASTNode node = getNode().getFirstChildNode();
return node != null && node.getElementType() == RegExpTT.GROUP_BEGIN;
}
public boolean isCapturing() {
final ASTNode node = getNode().getFirstChildNode();
return node != null && node.getElementType() == RegExpTT.GROUP_BEGIN;
}
public boolean isSimple() {
final ASTNode node = getNode().getFirstChildNode();
return node != null && (node.getElementType() == RegExpTT.GROUP_BEGIN || node.getElementType() == RegExpTT.NON_CAPT_GROUP);
}
public boolean isSimple() {
final ASTNode node = getNode().getFirstChildNode();
return node != null && (node.getElementType() == RegExpTT.GROUP_BEGIN || node.getElementType() == RegExpTT.NON_CAPT_GROUP);
}
public RegExpPattern getPattern() {
final ASTNode node = getNode().findChildByType(RegExpElementTypes.PATTERN);
return node != null ? (RegExpPattern)node.getPsi() : null;
}
public RegExpPattern getPattern() {
final ASTNode node = getNode().findChildByType(RegExpElementTypes.PATTERN);
return node != null ? (RegExpPattern)node.getPsi() : null;
}
public boolean isPythonNamedGroup() {
return getNode().findChildByType(RegExpTT.PYTHON_NAMED_GROUP) != null;
}
public boolean isPythonNamedGroup() {
return getNode().findChildByType(RegExpTT.PYTHON_NAMED_GROUP) != null;
}
public String getGroupName() {
if (!isPythonNamedGroup()) {
return null;
}
final ASTNode nameNode = getNode().findChildByType(RegExpTT.NAME);
return nameNode != null ? nameNode.getText() : null;
public boolean isRubyNamedGroup() {
return getNode().findChildByType(RegExpTT.RUBY_NAMED_GROUP) != null ||
getNode().findChildByType(RegExpTT.RUBY_QUOTED_NAMED_GROUP) != null;
}
public String getGroupName() {
if (!isPythonNamedGroup()) {
return null;
}
final ASTNode nameNode = getNode().findChildByType(RegExpTT.NAME);
return nameNode != null ? nameNode.getText() : null;
}
}
@@ -62,7 +62,8 @@ import com.intellij.psi.StringEscapesTokenTypes;
%state PROP
%xstate OPTIONS
%xstate COMMENT
%xstate PY_NAMED_GROUP
%xstate NAMED_GROUP
%xstate QUOTED_NAMED_GROUP
%xstate PY_NAMED_GROUP_REF
%xstate PY_COND_REF
@@ -208,10 +209,13 @@ HEX_CHAR=[0-9a-fA-F]
"(?<=" { return RegExpTT.POS_LOOKBEHIND; }
"(?<!" { return RegExpTT.NEG_LOOKBEHIND; }
"(?#" [^)]+ ")" { return RegExpTT.COMMENT; }
"(?P<" { yybegin(PY_NAMED_GROUP); return RegExpTT.PYTHON_NAMED_GROUP; }
"(?P<" { yybegin(NAMED_GROUP); return RegExpTT.PYTHON_NAMED_GROUP; }
"(?P=" { yybegin(PY_NAMED_GROUP_REF); return RegExpTT.PYTHON_NAMED_GROUP_REF; }
"(?(" { yybegin(PY_COND_REF); return RegExpTT.PYTHON_COND_REF; }
"(?<" { yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP; }
"(?'" { yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP; }
"(?" { yybegin(OPTIONS); return RegExpTT.SET_OPTIONS; }
}
@@ -225,9 +229,16 @@ HEX_CHAR=[0-9a-fA-F]
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
}
<PY_NAMED_GROUP> {
<NAMED_GROUP> {
[:letter:]([:letter:]|_|[:digit:])* { return RegExpTT.NAME; }
">" { yybegin(YYINITIAL); return RegExpTT.GT; }
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
}
<QUOTED_NAMED_GROUP> {
[:letter:]([:letter:]|_|[:digit:])* { return RegExpTT.NAME; }
"'" { yybegin(YYINITIAL); return RegExpTT.QUOTE; }
{ANY} { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; }
}
<PY_NAMED_GROUP_REF> {
@@ -34,144 +34,152 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class RegExpAnnotator extends RegExpElementVisitor implements Annotator {
private AnnotationHolder myHolder;
private AnnotationHolder myHolder;
public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) {
assert myHolder == null : "unsupported concurrent annotator invocation";
try {
myHolder = holder;
psiElement.accept(this);
} finally {
myHolder = null;
}
public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) {
assert myHolder == null : "unsupported concurrent annotator invocation";
try {
myHolder = holder;
psiElement.accept(this);
}
finally {
myHolder = null;
}
}
public void visitRegExpCharRange(RegExpCharRange range) {
final RegExpCharRange.Endpoint from = range.getFrom();
final RegExpCharRange.Endpoint to = range.getTo();
final boolean a = from instanceof RegExpChar;
final boolean b = to instanceof RegExpChar;
if (a && b) {
final Character t = ((RegExpChar)to).getValue();
final Character f = ((RegExpChar)from).getValue();
if (t != null && f != null) {
if (t < f) {
myHolder.createErrorAnnotation(range, "Illegal character range (to < from)");
} else if (t == f) {
myHolder.createWarningAnnotation(range, "Redundant character range");
}
public void visitRegExpCharRange(RegExpCharRange range) {
final RegExpCharRange.Endpoint from = range.getFrom();
final RegExpCharRange.Endpoint to = range.getTo();
final boolean a = from instanceof RegExpChar;
final boolean b = to instanceof RegExpChar;
if (a && b) {
final Character t = ((RegExpChar)to).getValue();
final Character f = ((RegExpChar)from).getValue();
if (t != null && f != null) {
if (t < f) {
myHolder.createErrorAnnotation(range, "Illegal character range (to < from)");
}
else if (t == f) {
myHolder.createWarningAnnotation(range, "Redundant character range");
}
}
}
else if (a != b) {
myHolder.createErrorAnnotation(range, "Character class (e.g. '\\\\w') may not be used inside character range");
}
else if (from.getText().equals(to.getText())) {
myHolder.createWarningAnnotation(range, "Redundant character range");
}
}
public void visitRegExpChar(final RegExpChar ch) {
final Character value = ch.getValue();
if (value == null) {
switch (ch.getType()) {
case CHAR:
myHolder.createErrorAnnotation(ch, "Illegal/unsupported escape sequence");
break;
case HEX:
myHolder.createErrorAnnotation(ch, "Illegal hexadecimal escape sequence");
break;
case OCT:
myHolder.createErrorAnnotation(ch, "Illegal octal escape sequence");
break;
case UNICODE:
myHolder.createErrorAnnotation(ch, "Illegal unicode escape sequence");
break;
case INVALID:
// produces a parser error. already handled by IDEA and possibly suppressed by IntelliLang
break;
}
}
else {
final String text = ch.getUnescapedText();
if (text.startsWith("\\") && isRedundantEscape(ch, text)) {
final ASTNode astNode = ch.getNode().getFirstChildNode();
if (astNode != null && astNode.getElementType() == RegExpTT.REDUNDANT_ESCAPE) {
final Annotation a = myHolder.createInformationAnnotation(ch, "Redundant character escape");
registerFix(a, new RemoveRedundantEscapeAction(ch));
}
}
}
}
private static boolean isRedundantEscape(RegExpChar ch, String text) {
if (text.length() <= 1) return false;
RegExpLanguageHost host = findRegExpHost(ch);
if (host != null) {
final char c = text.charAt(1);
final boolean needsEscaping = host.characterNeedsEscaping(c);
return !needsEscaping;
}
else {
return !("\\]".equals(text) || "\\}".equals(text));
}
}
@Nullable
private static RegExpLanguageHost findRegExpHost(PsiElement element) {
PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(element.getProject()).getInjectionHost(element);
if (host instanceof RegExpLanguageHost) {
return (RegExpLanguageHost)host;
}
return null;
}
public void visitRegExpProperty(RegExpProperty property) {
final ASTNode category = property.getCategoryNode();
if (category != null && !RegExpPropertyImpl.isValidCategory(category.getText())) {
final Annotation a = myHolder.createErrorAnnotation(category, "Unknown character category");
if (a != null) {
// IDEA-9381
a.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
}
}
public void visitRegExpBackref(final RegExpBackref backref) {
final RegExpGroup group = backref.resolve();
if (group == null) {
final Annotation a = myHolder.createErrorAnnotation(backref, "Unresolved backreference");
if (a != null) {
// IDEA-9381
a.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
}
else if (PsiTreeUtil.isAncestor(group, backref, true)) {
myHolder.createWarningAnnotation(backref, "Backreference is nested into the capturing group it refers to");
}
}
public void visitRegExpGroup(RegExpGroup group) {
final RegExpPattern pattern = group.getPattern();
if (pattern != null) {
final RegExpBranch[] branches = pattern.getBranches();
if (isEmpty(branches)) {
// catches "()" as well as "(|)"
myHolder.createWarningAnnotation(group, "Empty group");
}
else if (branches.length == 1) {
final RegExpAtom[] atoms = branches[0].getAtoms();
if (atoms.length == 1 && atoms[0] instanceof RegExpGroup) {
if (group.isSimple()) {
final RegExpGroup innerGroup = (RegExpGroup)atoms[0];
if (group.isCapturing() == innerGroup.isCapturing()) {
myHolder.createWarningAnnotation(group, "Redundant group nesting");
}
} else if (a != b) {
myHolder.createErrorAnnotation(range, "Character class (e.g. '\\\\w') may not be used inside character range");
} else if (from.getText().equals(to.getText())) {
myHolder.createWarningAnnotation(range, "Redundant character range");
}
}
}
}
public void visitRegExpChar(final RegExpChar ch) {
final Character value = ch.getValue();
if (value == null) {
switch (ch.getType()) {
case CHAR:
myHolder.createErrorAnnotation(ch, "Illegal/unsupported escape sequence");
break;
case HEX:
myHolder.createErrorAnnotation(ch, "Illegal hexadecimal escape sequence");
break;
case OCT:
myHolder.createErrorAnnotation(ch, "Illegal octal escape sequence");
break;
case UNICODE:
myHolder.createErrorAnnotation(ch, "Illegal unicode escape sequence");
break;
case INVALID:
// produces a parser error. already handled by IDEA and possibly suppressed by IntelliLang
break;
}
} else {
final String text = ch.getUnescapedText();
if (text.startsWith("\\") && isRedundantEscape(ch, text)) {
final ASTNode astNode = ch.getNode().getFirstChildNode();
if (astNode != null && astNode.getElementType() == RegExpTT.REDUNDANT_ESCAPE) {
final Annotation a = myHolder.createInformationAnnotation(ch, "Redundant character escape");
registerFix(a, new RemoveRedundantEscapeAction(ch));
}
}
}
}
private static boolean isRedundantEscape(RegExpChar ch, String text) {
if (text.length() <= 1) return false;
RegExpLanguageHost host = findRegExpHost(ch);
if (host != null) {
final char c = text.charAt(1);
final boolean needsEscaping = host.characterNeedsEscaping(c);
return !needsEscaping;
}
else {
return!("\\]".equals(text) || "\\}".equals(text));
}
}
@Nullable
private static RegExpLanguageHost findRegExpHost(PsiElement element) {
PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(element.getProject()).getInjectionHost(element);
if (host instanceof RegExpLanguageHost) {
return (RegExpLanguageHost) host;
}
return null;
}
public void visitRegExpProperty(RegExpProperty property) {
final ASTNode category = property.getCategoryNode();
if (category != null && !RegExpPropertyImpl.isValidCategory(category.getText())) {
final Annotation a = myHolder.createErrorAnnotation(category, "Unknown character category");
if (a != null) {
// IDEA-9381
a.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
}
}
public void visitRegExpBackref(final RegExpBackref backref) {
final RegExpGroup group = backref.resolve();
if (group == null) {
final Annotation a = myHolder.createErrorAnnotation(backref, "Unresolved backreference");
if (a != null) {
// IDEA-9381
a.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
} else if (PsiTreeUtil.isAncestor(group, backref, true)) {
myHolder.createWarningAnnotation(backref, "Backreference is nested into the capturing group it refers to");
}
}
public void visitRegExpGroup(RegExpGroup group) {
final RegExpPattern pattern = group.getPattern();
if (pattern != null) {
final RegExpBranch[] branches = pattern.getBranches();
if (isEmpty(branches)) {
// catches "()" as well as "(|)"
myHolder.createWarningAnnotation(group, "Empty group");
} else if (branches.length == 1) {
final RegExpAtom[] atoms = branches[0].getAtoms();
if (atoms.length == 1 && atoms[0] instanceof RegExpGroup) {
if (group.isSimple()) {
final RegExpGroup innerGroup = (RegExpGroup)atoms[0];
if (group.isCapturing() == innerGroup.isCapturing()) {
myHolder.createWarningAnnotation(group, "Redundant group nesting");
}
}
}
}
}
if (group.isPythonNamedGroup()) {
RegExpLanguageHost host = findRegExpHost(group);
if (host == null || !host.supportsPythonNamedGroups()) {
myHolder.createErrorAnnotation(group, "This named group syntax is not supported");
}
}
if (group.isPythonNamedGroup() || group.isRubyNamedGroup()) {
RegExpLanguageHost host = findRegExpHost(group);
if (host == null || (group.isPythonNamedGroup() && !host.supportsPythonNamedGroups()) ||
(group.isRubyNamedGroup() && !host.supportsRubyNamedGroups())) {
myHolder.createErrorAnnotation(group, "This named group syntax is not supported");
}
}
}
@Override
public void visitRegExpPyNamedGroupRef(RegExpPyNamedGroupRef groupRef) {
@@ -182,85 +190,91 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
}
final RegExpGroup group = groupRef.resolve();
if (group == null) {
final Annotation a = myHolder.createErrorAnnotation(groupRef, "Unresolved backreference");
if (a != null) {
// IDEA-9381
a.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
} else if (PsiTreeUtil.isAncestor(group, groupRef, true)) {
final Annotation a = myHolder.createErrorAnnotation(groupRef, "Unresolved backreference");
if (a != null) {
// IDEA-9381
a.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
}
}
else if (PsiTreeUtil.isAncestor(group, groupRef, true)) {
myHolder.createWarningAnnotation(groupRef, "Group reference is nested into the named group it refers to");
}
}
@Override
public void visitComment(PsiComment comment) {
if (comment.getText().startsWith("(?#")) {
RegExpLanguageHost host = findRegExpHost(comment);
if (host == null || !host.supportsPerl5EmbeddedComments()) {
myHolder.createErrorAnnotation(comment, "Embedded comments are not supported");
}
}
public void visitComment(PsiComment comment) {
if (comment.getText().startsWith("(?#")) {
RegExpLanguageHost host = findRegExpHost(comment);
if (host == null || !host.supportsPerl5EmbeddedComments()) {
myHolder.createErrorAnnotation(comment, "Embedded comments are not supported");
}
}
}
@Override
public void visitRegExpPyCondRef(RegExpPyCondRef condRef) {
RegExpLanguageHost host = findRegExpHost(condRef);
if (host == null || !host.supportsPythonConditionalRefs()) {
myHolder.createErrorAnnotation(condRef, "Conditional references are not supported");
myHolder.createErrorAnnotation(condRef, "Conditional references are not supported");
}
}
private static boolean isEmpty(RegExpBranch[] branches) {
for (RegExpBranch branch : branches) {
if (branch.getAtoms().length > 0) {
return false;
}
}
return true;
for (RegExpBranch branch : branches) {
if (branch.getAtoms().length > 0) {
return false;
}
}
return true;
}
public void visitRegExpQuantifier(RegExpQuantifier quantifier) {
final RegExpQuantifier.Count count = quantifier.getCount();
if (!(count instanceof RegExpQuantifier.SimpleCount)) {
final int min = count.getMin();
final int max = count.getMax();
if (max < min) {
myHolder.createErrorAnnotation(quantifier, "Illegal repetition range");
} else if (max == min) {
if (max == 1) { // TODO: is this safe when reluctant or possesive modifier is present?
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Single repetition");
registerFix(a, new SimplifyQuantifierAction(quantifier, null));
} else {
final ASTNode node = quantifier.getNode();
if (node.findChildByType(RegExpTT.COMMA) != null) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Fixed repetition range");
registerFix(a, new SimplifyQuantifierAction(quantifier, "{" + max + "}"));
}
}
} else if (min == 0 && max == 1) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Repetition range replaceable by '?'");
registerFix(a, new SimplifyQuantifierAction(quantifier, "?"));
} else if (min == 0 && max == Integer.MAX_VALUE) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Repetition range replaceable by '*'");
registerFix(a, new SimplifyQuantifierAction(quantifier, "*"));
} else if (min == 1 && max == Integer.MAX_VALUE) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Repetition range replaceable by '+'");
registerFix(a, new SimplifyQuantifierAction(quantifier, "+"));
}
public void visitRegExpQuantifier(RegExpQuantifier quantifier) {
final RegExpQuantifier.Count count = quantifier.getCount();
if (!(count instanceof RegExpQuantifier.SimpleCount)) {
final int min = count.getMin();
final int max = count.getMax();
if (max < min) {
myHolder.createErrorAnnotation(quantifier, "Illegal repetition range");
}
else if (max == min) {
if (max == 1) { // TODO: is this safe when reluctant or possesive modifier is present?
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Single repetition");
registerFix(a, new SimplifyQuantifierAction(quantifier, null));
}
if (quantifier.getType() == RegExpQuantifier.Type.POSSESSIVE) {
RegExpLanguageHost host = findRegExpHost(quantifier);
if (host != null && !host.supportsPossessiveQuantifiers()) {
myHolder.createErrorAnnotation(quantifier, "Nested quantifier in regexp");
}
else {
final ASTNode node = quantifier.getNode();
if (node.findChildByType(RegExpTT.COMMA) != null) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Fixed repetition range");
registerFix(a, new SimplifyQuantifierAction(quantifier, "{" + max + "}"));
}
}
}
else if (min == 0 && max == 1) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Repetition range replaceable by '?'");
registerFix(a, new SimplifyQuantifierAction(quantifier, "?"));
}
else if (min == 0 && max == Integer.MAX_VALUE) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Repetition range replaceable by '*'");
registerFix(a, new SimplifyQuantifierAction(quantifier, "*"));
}
else if (min == 1 && max == Integer.MAX_VALUE) {
final Annotation a = myHolder.createInformationAnnotation(quantifier, "Repetition range replaceable by '+'");
registerFix(a, new SimplifyQuantifierAction(quantifier, "+"));
}
}
if (quantifier.getType() == RegExpQuantifier.Type.POSSESSIVE) {
RegExpLanguageHost host = findRegExpHost(quantifier);
if (host != null && !host.supportsPossessiveQuantifiers()) {
myHolder.createErrorAnnotation(quantifier, "Nested quantifier in regexp");
}
}
}
private static void registerFix(Annotation a, IntentionAction action) {
if (a != null) {
// IDEA-9381
a.registerFix(action);
}
private static void registerFix(Annotation a, IntentionAction action) {
if (a != null) {
// IDEA-9381
a.registerFix(action);
}
}
}
@@ -336,18 +336,23 @@ public class ClsMethodImpl extends ClsRepositoryPsiElement<PsiMethodStub> implem
}
@NotNull
public PsiElement getNavigationElement() {
public PsiMethod getSourceMirrorMethod() {
PsiClass sourceClassMirror = ((ClsClassImpl)getParent()).getSourceMirrorClass();
if (sourceClassMirror == null) return this;
final PsiMethod[] methodsByName = sourceClassMirror.findMethodsByName(getName(), false);
for (PsiMethod sourceMethod : methodsByName) {
if (MethodSignatureUtil.areParametersErasureEqual(this, sourceMethod) && sourceMethod != this) {
return sourceMethod.getNavigationElement();
for (PsiMethod sourceMethod : sourceClassMirror.findMethodsByName(getName(), false)) {
if (MethodSignatureUtil.areParametersErasureEqual(this, sourceMethod)) {
return sourceMethod;
}
}
return this;
}
@NotNull
public PsiElement getNavigationElement() {
final PsiMethod method = getSourceMirrorMethod();
return method != this ? method.getNavigationElement() : this;
}
public PsiTypeParameterList getTypeParameterList() {
return getStub().findChildStubByType(JavaStubElementTypes.TYPE_PARAMETER_LIST).getPsi();
}
@@ -63,7 +63,7 @@ public class ClsParameterImpl extends ClsRepositoryPsiElement<PsiParameterStub>
}
ClsMethodImpl method = (ClsMethodImpl)getDeclarationScope();
PsiMethod sourceMethod = (PsiMethod)method.getNavigationElement();
PsiMethod sourceMethod = method.getSourceMirrorMethod();
if (sourceMethod == method) {
final PsiParameterStubImpl parameterStub = (PsiParameterStubImpl) getStub();
if (!parameterStub.isAutoGeneratedName()) {
@@ -40,6 +40,7 @@ public class RunContentDescriptor {
* Used to hack {@link com.intellij.execution.runners.RestartAction}
*/
private Content myContent;
private Runnable myRestarter;
public RunContentDescriptor(final ExecutionConsole executionConsole,
final ProcessHandler processHandler, final JComponent component, final String displayName, final Icon icon) {
@@ -108,4 +109,12 @@ public class RunContentDescriptor {
public Content getAttachedContent() {
return myContent;
}
public void setRestarter(Runnable runnable) {
myRestarter = runnable;
}
public Runnable getRestarter() {
return myRestarter;
}
}
@@ -126,7 +126,7 @@ public class CompletionLookupArranger extends LookupArranger {
for (int i = 0; i < weight1.length; i++) {
final Comparable w1 = weight1[i];
final Comparable w2 = weight2[i];
final Comparable w2 = i < weight2.length ? weight2[i]:null;
if (w1 != null || w2 != null) {
if (w1 == null) return 1;
if (w2 == null) return -1;
@@ -22,6 +22,7 @@ import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
@@ -48,16 +49,27 @@ public class CustomTemplateCallback {
private LiveTemplateBuilder.Marker myEndOffsetMarker;
private final Map<Object, LiveTemplateBuilder.Marker> myCheckpoints = new HashMap<Object, LiveTemplateBuilder.Marker>();
private final boolean myInInjectedFragment;
private FileType myFileType;
private LiveTemplateBuilder myBuilder = new LiveTemplateBuilder();
private int myOffset = 0;
public CustomTemplateCallback(Editor editor, PsiFile file) {
myEditor = editor;
myFile = file;
myProject = file.getProject();
myTemplateManager = TemplateManagerImpl.getInstance(myProject);
int caretOffset = editor.getCaretModel().getOffset();
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = InjectedLanguageUtil.findElementAtNoCommit(file, caretOffset);
myFile = element != null ? element.getContainingFile() : file;
myInInjectedFragment = InjectedLanguageManager.getInstance(myProject).isInjectedFragment(myFile);
myEditor = myInInjectedFragment ? InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, file) : editor;
fixInitialState();
}
@@ -151,11 +163,6 @@ public class CustomTemplateCallback {
}
public void startAllExpandedTemplates() {
/*myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
final CodeStyleManager style = CodeStyleManager.getInstance(myProject);
if (myGlobalMarker != null) {
style.reformatText(myFile, myGlobalMarker.getStartOffset(), myGlobalMarker.getEndOffset());
}*/
if (myBuilder.getText().length() == 0) {
return;
}
@@ -164,6 +171,7 @@ public class CustomTemplateCallback {
myBuilder.insertVariableSegment(myOffset, TemplateImpl.END);
}
TemplateImpl template = myBuilder.buildTemplate();
template.setToReformat(!myInInjectedFragment);
myTemplateManager.startTemplate(myEditor, template, false, myBuilder.getPredefinedValues(), null);
myBuilder = new LiveTemplateBuilder();
myEndOffsetMarker = null;
@@ -74,7 +74,6 @@ public class LiveTemplateBuilder {
last = occurence.myOffset;
}
template.addTextSegment(myText.substring(last));
template.setToReformat(true);
return template;
}
@@ -88,7 +88,7 @@ public class ExecutionHelper {
return;
}
for (final Exception exception : exceptionList) {
String[] messages = new String[]{exception.getMessage()};
String[] messages = StringUtil.splitByLines(exception.getMessage());
if (messages.length == 0) messages = new String[]{"Unknown Error"};
errorTreeView.addMessage(MessageCategory.ERROR, messages, file, -1, -1, null);
}
@@ -51,7 +51,6 @@ import java.awt.*;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
/**
* @author oleg
@@ -21,6 +21,7 @@ import com.intellij.execution.Executor;
import com.intellij.execution.RunCanceledByUserException;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.project.DumbAware;
@@ -57,6 +58,14 @@ public class RestartAction extends AnAction implements DumbAware {
public void actionPerformed(final AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
doRestart(dataContext);
}
public void restart() {
doRestart(DataManager.getInstance().getDataContext(myDescriptor.getComponent()));
}
private void doRestart(final DataContext dataContext) {
final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
try {
final ExecutionEnvironment old = myEnvironment;
@@ -199,6 +199,11 @@ public class RunContentBuilder implements LogConsoleManager, Disposable {
final RestartAction restartAction = new RestartAction(myExecutor, myRunner, getProcessHandler(), myRerunIcon, contentDescriptor, myEnvironment);
restartAction.registerShortcut(component);
actionGroup.add(restartAction);
contentDescriptor.setRestarter(new Runnable() {
public void run() {
restartAction.restart();
}
});
if (myExecutionResult instanceof DefaultExecutionResult) {
final AnAction[] actions = ((DefaultExecutionResult)myExecutionResult).getRestartActions();
@@ -392,7 +392,7 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
}
}
private static RunContentDescriptor getRunContentDescriptorByContent(final Content content) {
public static RunContentDescriptor getRunContentDescriptorByContent(final Content content) {
return content.getUserData(DESCRIPTOR_KEY);
}
@@ -78,6 +78,7 @@ public abstract class ChooseByNameBase{
protected final Project myProject;
protected final ChooseByNameModel myModel;
protected final String myInitialText;
private boolean myPreselectInitialText;
private final Reference<PsiElement> myContext;
protected Component myPreviouslyFocusedComponent;
@@ -147,6 +148,14 @@ public abstract class ChooseByNameBase{
myContext = new WeakReference<PsiElement>(context);
}
public boolean isPreselectInitialText() {
return myPreselectInitialText;
}
public void setPreselectInitialText(boolean preselectInitialText) {
myPreselectInitialText = preselectInitialText;
}
/**
* Set tool area. The method may be called only before invoke.
* @param toolArea a tool area component
@@ -321,6 +330,9 @@ public abstract class ChooseByNameBase{
myFuture = new ArrayList<Pair<String, Integer>>();
myTextField = new MyTextField();
myTextField.setText(myInitialText);
if (myPreselectInitialText) {
myTextField.select(0, myInitialText.length());
}
final ActionMap actionMap = new ActionMap();
actionMap.setParent(myTextField.getActionMap());
@@ -40,6 +40,9 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP
private ChooseByNamePopup(final Project project, final ChooseByNameModel model, final ChooseByNamePopup oldPopup,
final PsiElement context, @Nullable final String predefinedText) {
super(project, model, oldPopup != null ? oldPopup.getEnteredText() : predefinedText, context);
if (oldPopup == null && predefinedText != null) {
setPreselectInitialText(true);
}
if (oldPopup != null) { //inherit old focus owner
myOldFocusOwner = oldPopup.myPreviouslyFocusedComponent;
}
@@ -131,7 +131,7 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel {
};
}
protected String getActionId(AnAction anAction) {
protected String getActionId(final @NotNull AnAction anAction) {
return myActionManager.getId(anAction);
}
@@ -219,13 +219,15 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel {
private void collectActions(String id, Map<AnAction, String> result, ActionGroup group, final String containingGroupName){
final AnAction[] actions = group.getChildren(null);
for (AnAction action : actions) {
if (action instanceof ActionGroup) {
final ActionGroup actionGroup = (ActionGroup)action;
final String groupName = actionGroup.getTemplatePresentation().getText();
collectActions(id, result, actionGroup, groupName != null ? groupName : containingGroupName);
} else if (getActionId(action) == id) {
final String groupName = group.getTemplatePresentation().getText();
result.put(action, groupName != null && groupName.length() > 0 ? groupName : containingGroupName);
if (action != null) {
if (action instanceof ActionGroup) {
final ActionGroup actionGroup = (ActionGroup)action;
final String groupName = actionGroup.getTemplatePresentation().getText();
collectActions(id, result, actionGroup, groupName != null ? groupName : containingGroupName);
} else if (getActionId(action) == id) {
final String groupName = group.getTemplatePresentation().getText();
result.put(action, groupName != null && groupName.length() > 0 ? groupName : containingGroupName);
}
}
}
}
@@ -27,6 +27,7 @@ import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
@@ -104,11 +105,30 @@ public class SdkConfigurationUtil {
@Nullable
public static Sdk setupSdk(final VirtualFile homeDir, final SdkType sdkType, final boolean silent) {
return setupSdk(homeDir, sdkType, silent, null, null);
}
@Nullable
public static Sdk setupSdk(final VirtualFile homeDir, final SdkType sdkType, final boolean silent,
@Nullable final SdkAdditionalData additionalData,
@Nullable final String customSdkSuggestedName) {
final Sdk[] sdks = ProjectJdkTable.getInstance().getAllJdks();
final List<Sdk> sdksList = Arrays.asList(sdks);
final ProjectJdkImpl projectJdk;
try {
final String sdkName = createUniqueSdkName(sdkType, homeDir.getPath(), Arrays.asList(sdks));
final String sdkName = customSdkSuggestedName == null
? createUniqueSdkName(sdkType, homeDir.getPath(), sdksList)
: createUniqueSdkName(customSdkSuggestedName, sdksList);
projectJdk = new ProjectJdkImpl(sdkName, sdkType);
if (additionalData != null) {
// additional initialization.
// E.g. some ruby sdks must be initialized before
// setupSdkPaths() method invocation
projectJdk.setSdkAdditionalData(additionalData);
}
projectJdk.setHomePath(homeDir.getPath());
sdkType.setupSdkPaths(projectJdk);
}
@@ -195,11 +215,14 @@ public class SdkConfigurationUtil {
}
public static String createUniqueSdkName(SdkType type, String home, final Collection<Sdk> sdks) {
return createUniqueSdkName(type.suggestSdkName(null, home), sdks);
}
public static String createUniqueSdkName(final String suggestedName, final Collection<Sdk> sdks) {
final Set<String> names = new HashSet<String>();
for (Sdk jdk : sdks) {
names.add(jdk.getName());
}
final String suggestedName = type.suggestSdkName(null, home);
String newSdkName = suggestedName;
int i = 0;
while (names.contains(newSdkName)) {
@@ -33,7 +33,7 @@ import org.jetbrains.annotations.NotNull;
public abstract class LightElement extends PsiElementBase {
protected final PsiManager myManager;
private final Language myLanguage;
protected volatile PsiElement myNavigationElement = this;
private volatile PsiElement myNavigationElement = this;
protected LightElement(PsiManager manager, final Language language) {
myManager = manager;
@@ -97,8 +97,9 @@ public abstract class LightElement extends PsiElementBase {
}
public boolean isValid() {
if (myNavigationElement != this) {
return myNavigationElement.isValid();
final PsiElement navElement = getNavigationElement();
if (navElement != this) {
return navElement.isValid();
}
return true;
@@ -19,6 +19,8 @@ package com.intellij.unscramble;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
@@ -33,6 +35,8 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.EditorSettings;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
@@ -48,6 +52,8 @@ import java.awt.datatransfer.Transferable;
* @author yole
*/
public class AnalyzeStacktraceUtil {
public static ExtensionPointName<Filter> EP_NAME = ExtensionPointName.create("com.intellij.analyzeStacktraceFilter");
private AnalyzeStacktraceUtil() {
}
@@ -82,7 +88,12 @@ public class AnalyzeStacktraceUtil {
}
public static ConsoleView addConsole(Project project, @Nullable ConsoleFactory consoleFactory, final String tabTitle) {
final ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();
final TextConsoleBuilder builder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
for(Filter filter: Extensions.getExtensions(EP_NAME, project)) {
builder.addFilter(filter);
}
final ConsoleView consoleView = builder.getConsole();
final DefaultActionGroup toolbarActions = new DefaultActionGroup();
JComponent consoleComponent = consoleFactory != null
? consoleFactory.createConsoleComponent(consoleView, toolbarActions)
@@ -320,6 +320,7 @@ public class PluginManager {
if (!shouldLoadPlugins()) return true;
}
final List<String> pluginIds = pluginId == null ? null : StringUtil.split(pluginId, ",");
final boolean checkModuleDependencies = !ourAvailableModules.isEmpty() && !ourAvailableModules.contains("com.intellij.modules.all");
if (checkModuleDependencies && !hasModuleDependencies(descriptor)) {
@@ -333,8 +334,8 @@ public class PluginManager {
shouldLoad = loadPluginCategory.equals(descriptor.getCategory());
}
else {
if (pluginId != null) {
shouldLoad = pluginId.equals(idString);
if (pluginIds != null) {
shouldLoad = pluginIds.contains(idString);
if (!shouldLoad) {
Map<PluginId,IdeaPluginDescriptor> map = new HashMap<PluginId, IdeaPluginDescriptor>();
for (final IdeaPluginDescriptor pluginDescriptor : loaded) {
@@ -99,7 +99,7 @@ public abstract class FileTextFieldImpl implements FileLookup, Disposable, FileT
for (KeyStroke eachListStroke : listKeys) {
final String listActionID = (String)listMap.get(eachListStroke);
if ("selectNextRow".equals(listActionID) || "selectPreviousRow".equals(listActionID)) {
final String textActionID = (String)field.getInputMap().get(eachListStroke);
final Object textActionID = field.getInputMap().get(eachListStroke);
if (textActionID != null) {
final Action textAction = field.getActionMap().get(textActionID);
if (textAction != null) {
@@ -86,6 +86,7 @@ public class InlineProgressIndicator extends ProgressIndicatorBase implements Di
textAndProgress.add(myText, BorderLayout.CENTER);
final NonOpaquePanel progressWrapper = new NonOpaquePanel(new GridBagLayout());
progressWrapper.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
final GridBagConstraints c = new GridBagConstraints();
c.weightx = 1;
c.weighty = 1;
@@ -58,7 +58,7 @@ public class TextPanel extends JComponent {
@Override
protected void paintComponent(final Graphics g) {
final String s = getText();
String s = getText();
if (s != null) {
final Rectangle bounds = getBounds();
final Insets insets = getInsets();
@@ -68,12 +68,28 @@ public class TextPanel extends JComponent {
UIUtil.applyRenderingHints(g2);
final FontMetrics fm = g2.getFontMetrics();
final int sWidth = fm.stringWidth(s);
int x = insets.left;
if (myAlignment == JComponent.CENTER_ALIGNMENT || myAlignment == JComponent.RIGHT_ALIGNMENT) {
final int sWidth = g2.getFontMetrics().stringWidth(s);
x = myAlignment == JComponent.CENTER_ALIGNMENT ? (bounds.width - sWidth) / 2 : bounds.width - insets.right - sWidth;
}
final Rectangle textR = new Rectangle();
final Rectangle iconR = new Rectangle();
final Rectangle viewR = new Rectangle(bounds);
textR.x = textR.y = textR.width = textR.height = 0;
viewR.width -= insets.left;
viewR.width -= insets.right;
if (sWidth > (bounds.width - insets.left - insets.right)) {
s = SwingUtilities
.layoutCompoundLabel(fm, s, null, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.TRAILING,
bounds, iconR, textR, 0);
}
final int y = UIUtil.getStringY(s, bounds, g2);
if (SystemInfo.isMac && myDecorate) {
g2.setColor(new Color(215, 215, 215));
@@ -436,4 +436,6 @@
<extensionPoint name="typeName" beanClass="com.intellij.ide.TypeNameEP"/>
<extensionPoint name="typeIcon" beanClass="com.intellij.ide.TypeIconEP"/>
<extensionPoint name="analyzeStacktraceFilter" interface="com.intellij.execution.filters.Filter" area="IDEA_PROJECT"/>
</extensionPoints>
@@ -6,8 +6,7 @@
<group id="AddAllToFavorites" class="com.intellij.ide.favoritesTreeView.actions.AddAllToFavoritesActionGroup" popup="true"/>
<action id="AddNewFavoritesList" class="com.intellij.ide.favoritesTreeView.actions.AddNewFavoritesListAction"/>
<group id="SendToFavoritesGroup" class="com.intellij.ide.favoritesTreeView.actions.SendToFavoritesGroup" popup="true"/>
<group id="MoveModuleToGroup" class="com.intellij.ide.projectView.impl.MoveModuleToGroupTopLevel" popup="true"/>
<action id="RunConfiguration" class="com.intellij.execution.actions.RunConfigurationAction"/>
<action id="ChooseRunConfiguration" class="com.intellij.execution.actions.ChooseRunConfigurationAction" text="Run..." icon="/general/toolWindowRun.png"/>
<action id="ChooseDebugConfiguration" class="com.intellij.execution.actions.ChooseDebugConfigurationAction" text="Debug..." icon="/general/debug.png"/>
@@ -392,7 +391,6 @@
</group>
<group id="ProjectViewPopupMenuSettingsGroup">
<reference ref="MoveModuleToGroup"/>
</group>
<group id="ProjectViewPopupMenu">
@@ -53,7 +53,7 @@ public abstract class ParsingTestCase extends LightPlatformTestCase {
return false;
}
protected void doTest(boolean checkResult) throws Exception{
protected void doTest(boolean checkResult) throws Exception {
String name = getTestName(false);
String text = loadFile(name + "." + myFileExt);
myFile = createPsiFile(name, text);
@@ -67,7 +67,7 @@ public abstract class ParsingTestCase extends LightPlatformTestCase {
}
}
protected void doTest(String suffix) throws Exception{
protected void doTest(String suffix) throws IOException {
String name = getTestName(false);
String text = loadFile(name + "." + myFileExt);
myFile = createPsiFile(name, text);
@@ -80,11 +80,11 @@ public abstract class ParsingTestCase extends LightPlatformTestCase {
return createFile(name + "." + myFileExt, text);
}
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws Exception {
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
doCheckResult(myFullDataPath, file, targetDataName, includeRanges());
}
public static void doCheckResult(String myFullDataPath, PsiFile file, String targetDataName, boolean printRanges) throws Exception {
public static void doCheckResult(String myFullDataPath, PsiFile file, String targetDataName, boolean printRanges) throws IOException {
final PsiElement[] psiRoots = file.getPsiRoots();
if(psiRoots.length > 1){
for (int i = 0; i < psiRoots.length; i++) {
@@ -97,11 +97,11 @@ public abstract class ParsingTestCase extends LightPlatformTestCase {
}
}
protected void checkResult(@TestDataFile @NonNls String targetDataName, final String text) throws Exception {
protected void checkResult(@TestDataFile @NonNls String targetDataName, final String text) throws IOException {
doCheckResult(myFullDataPath, targetDataName, text);
}
private static void doCheckResult(String myFullDataPath, String targetDataName, String text) throws Exception {
private static void doCheckResult(String myFullDataPath, String targetDataName, String text) throws IOException {
try {
text = text.trim();
String expectedText = doLoadFile(myFullDataPath, targetDataName);
@@ -124,7 +124,7 @@ public abstract class ParsingTestCase extends LightPlatformTestCase {
return DebugUtil.psiToString(file, false, printRanges);
}
protected String loadFile(@NonNls @TestDataFile String name) throws Exception {
protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
return doLoadFile(myFullDataPath, name);
}
@@ -34,11 +34,13 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.util.*;
import java.util.regex.Pattern;
//TeamCity inherits StringUtil: do not add private constructors!!!
public class StringUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.text.StringUtil");
@NonNls private static final String VOWELS = "aeiouy";
private static final Pattern EOL_SPLIT_PATTERN = Pattern.compile(" *(\r|\n|\r\n)+ *");
public static String replace(@NonNls @NotNull String text, @NonNls @NotNull String oldS, @NonNls @Nullable String newS) {
return replace(text, oldS, newS, false);
@@ -1600,4 +1602,14 @@ public class StringUtil {
public static String tail(@NotNull String s, final int idx) {
return idx >= s.length() ? "" : s.substring(idx, s.length());
}
/**
* Splits string by lines.
*
* @param string String to split
* @return array of strings
*/
public static String[] splitByLines(final String string) {
return EOL_SPLIT_PATTERN.split(string);
}
}
+1 -1
View File
@@ -19,7 +19,7 @@
<orderEntry type="module" module-name="properties" />
<orderEntry type="module" module-name="execution-openapi" />
<orderEntry type="module" module-name="idea-ui" />
<orderEntry type="module" module-name="dom-openapi" />
<orderEntry type="module" module-name="dom-openapi" exported="" />
<orderEntry type="module" module-name="xml-openapi" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="idea-tests" scope="TEST" />
@@ -53,13 +53,13 @@ public class AntDomExtender extends DomExtender<AntDomElement>{
}
else {
final Map<String, Class> tasks = reflected.getTaskDefinitions();
final Class taskClass = (Class)tasks.get(tagName);
final Class taskClass = tasks.get(tagName);
if (taskClass != null) {
parentElementIntrospector = getIntrospector(taskClass);
}
else {
final Map<String, Class> dataTypes = reflected.getDataTypeDefinitions();
final Class dataClass = (Class)dataTypes.get(tagName);
final Class dataClass = dataTypes.get(tagName);
if (dataClass != null) {
parentElementIntrospector = getIntrospector(dataClass);
}
@@ -83,9 +83,9 @@ public class AntDomExtender extends DomExtender<AntDomElement>{
}
}
else {
final Enumeration nested = parentElementIntrospector.getNestedElements();
final Enumeration<String> nested = parentElementIntrospector.getNestedElements();
while (nested.hasMoreElements()) {
registerChild(registrar, genericInfo, (String)nested.nextElement());
registerChild(registrar, genericInfo, nested.nextElement());
}
}
}
@@ -113,7 +113,7 @@ public class AntDomExtender extends DomExtender<AntDomElement>{
}
@Nullable
private static AntIntrospector getIntrospector(Class c) {
public static AntIntrospector getIntrospector(Class c) {
try {
return AntIntrospector.getInstance(c);
}
@@ -99,7 +99,7 @@ public final class AntIntrospector {
return types;
}
public Enumeration getNestedElements() {
public Enumeration<String> getNestedElements() {
return invokeMethod("getNestedElements", false);
}
@@ -26,6 +26,7 @@ import com.intellij.testFramework.ParsingTestCase;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
public class CustomTypesTest extends ParsingTestCase {
@@ -63,7 +64,7 @@ public class CustomTypesTest extends ParsingTestCase {
return result;
}
protected String loadFile(String name) throws Exception {
protected String loadFile(String name) throws IOException {
String fullName = getTestDataPath() + File.separatorChar + name;
String text = new String(FileUtil.loadFileText(new File(fullName))).trim();
text = StringUtil.convertLineSeparators(text);
@@ -27,9 +27,9 @@ import java.io.File;
* The application wide settings for the git
*/
@State(
name = "PasswordSafe",
name = "Git.Application.Settings",
storages = {@Storage(
id = "PasswordSafe",
id = "Git.Application.Settings",
file = "$APP_CONFIG$/vcs.xml")})
public class GitVcsApplicationSettings implements PersistentStateComponent<GitVcsApplicationSettings.State> {
/**
@@ -24,6 +24,8 @@ import org.codehaus.groovy.tools.javac.JavaAwareResolveVisitor;
import org.codehaus.groovy.tools.javac.JavaStubGenerator;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -341,10 +343,16 @@ public class GroovycRunner {
final Enumeration resources = super.getResources(name);
final ArrayList list = Collections.list(resources);
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
String file = ((URL)iterator.next()).getFile();
System.out.println("Enumerated:" + file);
if (file.startsWith(finalOutput) || file.startsWith("/" + finalOutput)) {
iterator.remove();
final URL url = (URL)iterator.next();
try {
final String file = new File(new URI(url.toString())).getCanonicalPath();
System.out.println("Enumerated:" + file);
if (file.startsWith(finalOutput) || file.startsWith("/" + finalOutput)) {
iterator.remove();
}
}
catch (Exception ignored) {
System.out.println("Invalid URI syntax: " + url.toString());
}
}
return Collections.enumeration(list);
@@ -71,7 +71,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.util.GroovyUtils;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
@@ -304,9 +303,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler {
printer.println(PathUtil.getLocalPath(outputDir));
printer.println(GroovycRunner.FINAL_OUTPUTPATH);
final URL finalUrl = VfsUtil.convertToURL(finalOutputDir.getUrl());
LOG.assertTrue(finalUrl != null, finalOutputDir.getPath());
printer.println(finalUrl.getFile());
printer.println(FileUtil.toSystemDependentName(finalOutputDir.getPath()));
printer.close();
@@ -77,7 +77,7 @@ public class CustomMembersGenerator implements GdslMembersHolderConsumer {
public CustomMembersHolder getMembersHolder() {
// Add non-code members holder
if (myClassText.length() > 0) {
addMemberHolder(NonCodeMembersHolder.fromText(myClassText.toString(), myProject));
addMemberHolder(NonCodeMembersHolder.fromText(myClassText.toString(), myPlace.containingFile));
}
return myDepot;
}
@@ -24,9 +24,9 @@ import org.jetbrains.plugins.groovy.dsl.GdslMembersHolderConsumer;
import org.jetbrains.plugins.groovy.dsl.holders.DelegatedMembersHolder;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
@@ -147,10 +147,11 @@ public class GroovyDslDefaultMembers implements GdslMembersProvider {
@Nullable
private static String getInvokedMethodName(GrCall call) {
final GrExpression expr = call instanceof GrApplicationStatement ? ((GrApplicationStatement)call).getFunExpression() :
call instanceof GrMethodCallExpression ? ((GrMethodCallExpression)call).getInvokedExpression() : null;
if (expr instanceof GrReferenceExpression) {
return ((GrReferenceExpression)expr).getName();
if (call instanceof GrMethodCall) {
final GrExpression expr = ((GrMethodCall)call).getInvokedExpression();
if (expr instanceof GrReferenceExpression) {
return ((GrReferenceExpression)expr).getName();
}
}
return null;
}
@@ -15,11 +15,8 @@
*/
package org.jetbrains.plugins.groovy.dsl.holders;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.ResolveState;
import com.intellij.psi.*;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.CachedValue;
@@ -38,13 +35,13 @@ public class NonCodeMembersHolder implements CustomMembersHolder {
private final GrTypeDefinition myPsiClass;
private static final Key<CachedValue<ConcurrentFactoryMap<String, NonCodeMembersHolder>>> CACHED_HOLDERS = Key.create("CACHED_HOLDERS");
public static NonCodeMembersHolder fromText(@NotNull String classText, final Project project) {
return CachedValuesManager.getManager(project).getCachedValue(project, CACHED_HOLDERS, new CachedValueProvider<ConcurrentFactoryMap<String, NonCodeMembersHolder>>() {
public static NonCodeMembersHolder fromText(@NotNull String classText, final PsiFile place) {
return CachedValuesManager.getManager(place.getProject()).getCachedValue(place, CACHED_HOLDERS, new CachedValueProvider<ConcurrentFactoryMap<String, NonCodeMembersHolder>>() {
public Result<ConcurrentFactoryMap<String, NonCodeMembersHolder>> compute() {
final ConcurrentFactoryMap<String, NonCodeMembersHolder> map = new ConcurrentFactoryMap<String, NonCodeMembersHolder>() {
@Override
protected NonCodeMembersHolder create(String key) {
return new NonCodeMembersHolder(key, project);
return new NonCodeMembersHolder(key, place);
}
};
return Result.create(map, PsiModificationTracker.MODIFICATION_COUNT);
@@ -52,9 +49,9 @@ public class NonCodeMembersHolder implements CustomMembersHolder {
}, false).get(classText);
}
private NonCodeMembersHolder(@NotNull String classText, Project project) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project);
myPsiClass = factory.createGroovyFile("class GroovyEnhanced {\n" + classText + "}", false, null).getTypeDefinitions()[0];
private NonCodeMembersHolder(@NotNull String classText, PsiElement place) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(place.getProject());
myPsiClass = factory.createGroovyFile("class GroovyEnhanced {\n" + classText + "}", false, place).getTypeDefinitions()[0];
}
public boolean processMembers(PsiScopeProcessor processor) {
@@ -22,10 +22,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArrayInitializer;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import java.util.ArrayList;
import java.util.Arrays;
@@ -39,9 +38,7 @@ public class PsiElementCategory implements PsiEnhancerCategory {
@Nullable
public static PsiElement bind(PsiElement element) {
PsiElement elem = element instanceof GrMethodCallExpression ? ((GrMethodCallExpression)element).getInvokedExpression() :
element instanceof GrApplicationStatement ? ((GrApplicationStatement)element).getFunExpression() :
element;
PsiElement elem = element instanceof GrMethodCall ? ((GrMethodCall)element).getInvokedExpression() : element;
final PsiReference ref = elem.getReference();
return ref == null ? null : ref.resolve();
}
@@ -0,0 +1,59 @@
package org.jetbrains.plugins.groovy.gant;
import com.intellij.lang.ant.dom.AntDomExtender;
import com.intellij.lang.ant.psi.impl.AntIntrospector;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightMethodBuilder;
import com.intellij.psi.scope.PsiScopeProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import java.util.Enumeration;
/**
* @author peter
*/
class AntBuilderMethod extends LightMethodBuilder {
private final PsiFile myPlace;
@Nullable private final Class myAntClass;
public AntBuilderMethod(PsiFile place, String name, PsiType closureType, @Nullable Class antClass) {
super(place.getManager(), GroovyFileType.GROOVY_LANGUAGE, name);
myPlace = place;
myAntClass = antClass;
setModifiers(PsiModifier.PUBLIC);
addParameter("args", CommonClassNames.JAVA_UTIL_MAP);
setBaseIcon(GantIcons.ANT_TASK);
addParameter(new GrLightParameter("body", closureType, this).setOptional(true));
}
@NotNull
@Override
public PsiElement getNavigationElement() {
if (myAntClass != null) {
final PsiClass psiClass = JavaPsiFacade.getInstance(getProject()).findClass(myAntClass.getName(), myPlace.getResolveScope());
if (psiClass != null) {
return psiClass;
}
}
return this;
}
public boolean processNestedElements(PsiScopeProcessor processor) {
final AntIntrospector introspector = AntDomExtender.getIntrospector(myAntClass);
if (introspector != null) {
final Enumeration<String> nested = introspector.getNestedElements();
while (nested.hasMoreElements()) {
final AntBuilderMethod method =
new AntBuilderMethod(myPlace, nested.nextElement(), getParameterList().getParameters()[1].getType(), null);
if (!ResolveUtil.processElement(processor, method)) {
return false;
}
}
}
return true;
}
}
@@ -27,18 +27,15 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightMethodBuilder;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.PathUtil;
import com.intellij.util.lang.UrlClassLoader;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.extensions.GroovyScriptType;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
import java.net.URL;
import java.util.*;
@@ -79,29 +76,12 @@ public class AntTasksProvider {
final Set<LightMethodBuilder> result = new HashSet<LightMethodBuilder>();
final Project project = file.getProject();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final GlobalSearchScope scope = file.getResolveScope();
final PsiType closureType = JavaPsiFacade.getElementFactory(project).createTypeFromText(GrClosableBlock.GROOVY_LANG_CLOSURE,
file);
final PsiType closureType = JavaPsiFacade.getElementFactory(project).createTypeFromText(GrClosableBlock.GROOVY_LANG_CLOSURE, file);
for (String name : antObjects.keySet()) {
final PsiClass psiClass = facade.findClass(antObjects.get(name).getName(), scope);
final LightMethodBuilder tdMethod =
new LightMethodBuilder(PsiManager.getInstance(project), GroovyFileType.GROOVY_LANGUAGE, name).
setModifiers(PsiModifier.PUBLIC).
addParameter("args", CommonClassNames.JAVA_UTIL_MAP).
setBaseIcon(GantIcons.ANT_TASK);
tdMethod.addParameter(new GrLightParameter("body", closureType, tdMethod).setOptional(true));
if (psiClass != null) {
tdMethod.setNavigationElement(psiClass);
}
result.add(tdMethod);
result.add(new AntBuilderMethod(file, name, closureType, antObjects.get(name)));
}
return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(file.getProject()));
return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project));
}
}, false);
}
@@ -15,10 +15,7 @@
*/
package org.jetbrains.plugins.groovy.gant;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightMethodBuilder;
import com.intellij.psi.impl.light.LightVariableBuilder;
import com.intellij.psi.scope.PsiScopeProcessor;
@@ -26,6 +23,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersProcessor;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
@@ -45,13 +43,35 @@ public class GantMemberContributor implements NonCodeMembersProcessor {
return true;
}
PsiFile file = place.getContainingFile();
if (!GantUtils.isGantScriptFile(file)) {
GrClosableBlock closure = PsiTreeUtil.getContextOfType(place, GrClosableBlock.class, true);
if (closure == null) {
return true;
}
final GrClosableBlock closure = PsiTreeUtil.getContextOfType(place, GrClosableBlock.class, true);
if (closure == null) {
boolean antTasksProcessed = false;
while (closure != null) {
final PsiElement parent = closure.getParent();
if (parent instanceof GrMethodCall) {
final PsiMethod method = ((GrMethodCall)parent).resolveMethod();
if (method instanceof AntBuilderMethod) {
antTasksProcessed = true;
if (!processAntTasks(processor, place)) {
return false;
}
if (!((AntBuilderMethod)method).processNestedElements(processor)) {
return false;
}
break;
}
}
closure = PsiTreeUtil.getContextOfType(closure, GrClosableBlock.class, true);
}
// ------- gant-specific
PsiFile file = place.getContainingFile();
if (!GantUtils.isGantScriptFile(file)) {
return true;
}
@@ -66,7 +86,7 @@ public class GantMemberContributor implements NonCodeMembersProcessor {
}
}
return processAntTasks(processor, place);
return antTasksProcessed || processAntTasks(processor, place);
}
@@ -17,17 +17,16 @@
package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
/**
* @author ilyas
*/
public interface GrApplicationStatement extends GrStatement, GrCall {
public interface GrApplicationStatement extends GrStatement, GrMethodCall {
/**
* @deprecated use {@link GrMethodCall#getInvokedExpression()}
*/
GrExpression getFunExpression();
GrExpression[] getArguments();
GrArgumentList getArgumentList();
//PsiElement addNamedArgument(GrNamedArgument argument);
}
@@ -0,0 +1,19 @@
package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
/**
* @author peter
*/
public interface GrMethodCall extends GrCall {
GrExpression getInvokedExpression();
@Nullable
GrArgumentList getArgumentList();
@Nullable
PsiMethod resolveMethod();
}
@@ -20,13 +20,12 @@ import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
/**
* @author ilyas
*/
public interface GrMethodCallExpression extends GrCallExpression {
GrExpression getInvokedExpression();
public interface GrMethodCallExpression extends GrCallExpression, GrMethodCall {
GrExpression replaceClosureArgument(@NotNull GrClosableBlock closure, @NotNull GrExpression newExpr) throws IncorrectOperationException;
@@ -468,6 +468,11 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile {
}
};
public GlobalSearchScope getFileResolveScope() {
final PsiElement context = getContext();
if (context instanceof GroovyFile) {
return context.getResolveScope();
}
final VirtualFile vFile = getOriginalFile().getVirtualFile();
if (vFile == null) {
return GlobalSearchScope.allScope(getProject());
@@ -39,7 +39,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.arithmetic.*;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrAndExpressionImpl;
@@ -137,18 +136,17 @@ public class PsiImplUtil {
if (qualifier == null) {
GrClosableBlock closure = PsiTreeUtil.getParentOfType(refExpr, GrClosableBlock.class);
while (closure != null) {
GrExpression funExpr = null;
PsiElement parent = closure.getParent();
if (parent instanceof GrApplicationStatement) {
funExpr = ((GrApplicationStatement) parent).getFunExpression();
} else if (parent instanceof GrMethodCallExpression) {
funExpr = ((GrMethodCallExpression) parent).getInvokedExpression();
if (parent instanceof GrMethodCall) {
GrExpression funExpr = ((GrMethodCall)parent).getInvokedExpression();
if (funExpr instanceof GrReferenceExpression) {
qualifier = ((GrReferenceExpression) funExpr).getQualifierExpression();
if (qualifier != null) {
return qualifier;
}
}
}
if (funExpr instanceof GrReferenceExpression) {
qualifier = ((GrReferenceExpression) funExpr).getQualifierExpression();
if (qualifier != null) break;
}
closure = PsiTreeUtil.getParentOfType(closure, GrClosableBlock.class);
}
@@ -354,4 +352,14 @@ public class PsiImplUtil {
return method.getName().equals(MAIN_METHOD) &&
method.hasModifierProperty(PsiModifier.STATIC);
}
public static PsiMethod resolveMethod(GrMethodCall expression) {
final GrExpression methodExpr = expression.getInvokedExpression();
if (methodExpr instanceof GrReferenceExpression) {
final PsiElement resolved = ((GrReferenceExpression) methodExpr).resolve();
return resolved instanceof PsiMethod ? (PsiMethod) resolved : null;
}
return null;
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@@ -26,6 +27,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplic
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCommandArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
/**
* @author ilyas
@@ -45,7 +47,7 @@ public class GrApplicationStatementImpl extends GrExpressionImpl implements GrAp
}
public GrExpression getFunExpression() {
return findChildByClass(GrExpression.class);
return getInvokedExpression();
}
public GrExpression[] getArguments() {
@@ -54,6 +56,15 @@ public class GrApplicationStatementImpl extends GrExpressionImpl implements GrAp
return list.getExpressionArguments();
}
public PsiMethod resolveMethod() {
return PsiImplUtil.resolveMethod(this);
}
@Override
public GrExpression getInvokedExpression() {
return findNotNullChildByClass(GrExpression.class);
}
public GrCommandArgumentList getArgumentList() {
return findChildByClass(GrCommandArgumentList.class);
}
@@ -35,6 +35,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceExpressionImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
@@ -181,13 +182,7 @@ public class GrMethodCallExpressionImpl extends GrCallExpressionImpl implements
}
public PsiMethod resolveMethod() {
final GrExpression methodExpr = getInvokedExpression();
if (methodExpr instanceof GrReferenceExpression) {
final PsiElement resolved = ((GrReferenceExpression) methodExpr).resolve();
return resolved instanceof PsiMethod ? (PsiMethod) resolved : null;
}
return null;
return PsiImplUtil.resolveMethod(this);
}
@NotNull
@@ -19,7 +19,6 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -54,18 +53,6 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
this(parameters, returnType, PsiSubstitutor.EMPTY);
}
public GrClosureSignatureImpl(@NotNull GrClosableBlock block) {
this(block.getAllParameters(), block.getReturnType());
}
public GrClosureSignatureImpl(@NotNull PsiMethod method) {
this(method, PsiSubstitutor.EMPTY);
}
public GrClosureSignatureImpl(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutor) {
this(method.getParameterList().getParameters(), PsiUtil.getSmartReturnType(method), substitutor);
}
GrClosureSignatureImpl(@NotNull GrClosureParameter[] params, @Nullable PsiType returnType, boolean isVarArgs) {
myParameters = params;
myReturnType = returnType;
@@ -99,7 +86,7 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
public GrClosureSignature curry(int count) {
if (count > myParameters.length) {
if (isVarargs()) {
return new GrClosureSignatureImpl(GrClosureParameter.EMPTY_ARRAY, myReturnType);
return new DerivedClosureSignature();
}
else {
return null;
@@ -107,22 +94,22 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
}
GrClosureParameter[] newParams = new GrClosureParameter[myParameters.length - count];
System.arraycopy(myParameters, count, newParams, 0, newParams.length);
return new GrClosureSignatureImpl(newParams, myReturnType, myIsVarargs);
return new DerivedClosureSignature(newParams, null, myIsVarargs);
}
public boolean isValid() {
for (GrClosureParameter parameter : myParameters) {
if (!parameter.isValid()) return false;
}
return myReturnType == null || myReturnType.isValid();
final PsiType returnType = getReturnType();
return returnType == null || returnType.isValid();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GrClosureSignature) {
return Comparing.equal(myParameters, ((GrClosureSignature)obj).getParameters()) &&
Comparing.equal(myIsVarargs, ((GrClosureSignature)obj).isVarargs()) &&
Comparing.equal(myReturnType, ((GrClosureSignature)obj).getReturnType());
Comparing.equal(myIsVarargs, ((GrClosureSignature)obj).isVarargs());
}
return super.equals(obj);
}
@@ -152,5 +139,20 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
}
return null; //todo
}
private class DerivedClosureSignature extends GrClosureSignatureImpl {
DerivedClosureSignature() {
super(GrClosureParameter.EMPTY_ARRAY, null);
}
DerivedClosureSignature(@NotNull GrClosureParameter[] params, @Nullable PsiType returnType, boolean isVarArgs) {
super(params, returnType, isVarArgs);
}
@Override
public PsiType getReturnType() {
return GrClosureSignatureImpl.this.getReturnType();
}
}
}
@@ -35,6 +35,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.ArrayList;
import java.util.Arrays;
@@ -48,12 +49,22 @@ public class GrClosureSignatureUtil {
private GrClosureSignatureUtil() {
}
public static GrClosureSignature createSignature(GrClosableBlock block) {
return new GrClosureSignatureImpl(block);
public static GrClosureSignature createSignature(final GrClosableBlock block) {
return new GrClosureSignatureImpl(block.getAllParameters(), null) {
@Override
public PsiType getReturnType() {
return block.getReturnType();
}
};
}
public static GrClosureSignature createSignature(PsiMethod method, PsiSubstitutor substitutor) {
return new GrClosureSignatureImpl(method, substitutor);
public static GrClosureSignature createSignature(final PsiMethod method, PsiSubstitutor substitutor) {
return new GrClosureSignatureImpl(method.getParameterList().getParameters(), null, substitutor) {
@Override
public PsiType getReturnType() {
return PsiUtil.getSmartReturnType(method);
}
};
}
public static GrClosureSignature createSignature(PsiParameter[] parameters, PsiType returnType) {
@@ -292,7 +292,7 @@ public class GrCodeReferenceElementImpl extends GrReferenceElementImpl implement
}
if (kind == CLASS) return;
PsiPackage[] subpackages = aPackage.getSubPackages();
PsiPackage[] subpackages = aPackage.getSubPackages(getResolveScope());
for (PsiPackage subpackage : subpackages) {
consumer.consume(subpackage);
}
@@ -385,7 +385,7 @@ public class GrCodeReferenceElementImpl extends GrReferenceElementImpl implement
}
if (kind == CLASS_OR_PACKAGE) {
for (final PsiPackage subpackage : ((PsiPackage) qualifierResolved).getSubPackages()) {
for (final PsiPackage subpackage : ((PsiPackage) qualifierResolved).getSubPackages(ref.getResolveScope())) {
if (refName.equals(subpackage.getName()))
return new GroovyResolveResult[]{new GroovyResolveResultImpl(subpackage, true)};
}
@@ -410,7 +410,7 @@ public class GrCodeReferenceElementImpl extends GrReferenceElementImpl implement
if (kind == CLASS_OR_PACKAGE) {
PsiPackage defaultPackage = JavaPsiFacade.getInstance(ref.getProject()).findPackage("");
if (defaultPackage != null) {
for (final PsiPackage subpackage : defaultPackage.getSubPackages()) {
for (final PsiPackage subpackage : defaultPackage.getSubPackages(ref.getResolveScope())) {
if (refName.equals(subpackage.getName()))
return new GroovyResolveResult[]{new GroovyResolveResultImpl(subpackage, true)};
}
@@ -106,6 +106,29 @@ def foo() {
checkVariants "ant.patt<caret>t", "patternset"
}
public void testTagsInsideTags() throws Exception {
myFixture.configureByText "a.groovy", """
AntBuilder ant
ant.zip {
patternset {
includ<caret>
}
}"""
myFixture.completeBasic()
assertSameElements myFixture.lookupElementStrings, "include", "includesfile"
}
public void testTagsInsideTagsInGantTarget() throws Exception {
checkVariants """
target(aaa: "") {
zip {
patternset {
includ<caret>
}
}
}""", "include", "includesfile", "includeTargets", "includeTool"
}
static final def GANT_JARS = ["gant.jar", "ant.jar", "ant-junit.jar", "ant-launcher.jar", "commons.jar"]
}
@@ -126,7 +149,7 @@ class GantProjectDescriptor implements LightProjectDescriptor {
modifiableModel.addRoot(fs.findFileByPath("$TestUtils.mockGroovyLibraryHome/$TestUtils.GROOVY_JAR!/"), OrderRootType.CLASSES);
GantReferenceCompletionTest.GANT_JARS.each {
modifiableModel.addRoot(fs.findFileByPath("${TestUtils.absoluteTestDataPath}mockGantLib/$it!/"), OrderRootType.CLASSES);
modifiableModel.addRoot(fs.findFileByPath("${TestUtils.absoluteTestDataPath}mockGantLib/lib/$it!/"), OrderRootType.CLASSES);
}
modifiableModel.commit();
}
@@ -0,0 +1,53 @@
package org.jetbrains.plugins.groovy.gant.completion;
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.plugins.groovy.gant.GantSettings
import org.jetbrains.plugins.groovy.util.SdkHomeConfigurable.SdkHomeBean
import org.jetbrains.plugins.groovy.util.TestUtils
/**
* @author peter
*/
public class StandaloneGantCompletionTest extends LightCodeInsightFixtureTestCase {
@Override
protected String getBasePath() {
return TestUtils.getTestDataPath() + "gant/completion";
}
@Override protected void setUp() {
super.setUp();
final SdkHomeBean state = new SdkHomeBean();
state.SDK_HOME = FileUtil.toSystemIndependentName("${TestUtils.absoluteTestDataPath}mockGantLib");
GantSettings.getInstance(getProject()).loadState state
}
@Override protected void tearDown() {
GantSettings.getInstance(getProject()).loadState new SdkHomeBean()
super.tearDown();
}
void checkVariants(String text, String... items) {
myFixture.configureByText "a.gant", text
myFixture.completeBasic()
assertSameElements myFixture.lookupElementStrings, items
}
public void testDep() throws Throwable {
checkVariants """
target(aaa: "") {
dep<caret>
}
""", "depends", "dependset"
}
public void testPatternset() throws Exception {
checkVariants "ant.patt<caret>t", "patternset"
}
static final def GANT_JARS = ["gant.jar", "ant.jar", "ant-junit.jar", "ant-launcher.jar", "commons.jar"]
}
@@ -20,7 +20,11 @@ public class MethodCallInSuperTest extends JDialog {
private JComponent myRootComponent;
public MethodCallInSuperTest() {
super(JOptionPane.getRootFrame(), "", true);
super(getNullFrame(), "", true);
getContentPane().add(myRootComponent);
}
private static JFrame getNullFrame() {
return null;
}
}
+5
View File
@@ -345,6 +345,7 @@
</group>
<action id="ModuleSettings" class="com.intellij.ide.actions.ShowModulePropertiesAction"/>
<group id="MoveModuleToGroup" class="com.intellij.ide.projectView.impl.MoveModuleToGroupTopLevel" popup="true"/>
<reference ref="AnalyzeMenu">
<add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="ReplaceInPath"/>
@@ -381,6 +382,10 @@
</action>
<reference ref="MoveModuleToGroup">
<add-to-group group-id="ProjectViewPopupMenuSettingsGroup" anchor="first"/>
</reference>
<reference ref="ModuleSettings">
<add-to-group group-id="ProjectViewPopupMenuSettingsGroup" anchor="first"/>
</reference>
@@ -28,4 +28,9 @@ public class FilterToken extends Token {
public String getSuffix() {
return mySuffix;
}
@Override
public String toString() {
return "|" + mySuffix;
}
}
@@ -18,5 +18,9 @@ package com.intellij.codeInsight.template.zencoding;
/**
* @author Eugene.Kudelevsky
*/
class MarkerToken extends Token {
public class MarkerToken extends Token {
@Override
public String toString() {
return "&";
}
}
@@ -18,7 +18,7 @@ package com.intellij.codeInsight.template.zencoding;
/**
* @author Eugene.Kudelevsky
*/
class NumberToken extends Token {
public class NumberToken extends Token {
private final int myNumber;
NumberToken(int number) {
@@ -28,4 +28,9 @@ class NumberToken extends Token {
public int getNumber() {
return myNumber;
}
@Override
public String toString() {
return Integer.toString(myNumber);
}
}
@@ -18,7 +18,7 @@ package com.intellij.codeInsight.template.zencoding;
/**
* @author Eugene.Kudelevsky
*/
class OperationToken extends Token {
public class OperationToken extends Token {
private final char mySign;
OperationToken(char sign) {
@@ -28,4 +28,9 @@ class OperationToken extends Token {
public char getSign() {
return mySign;
}
@Override
public String toString() {
return Character.toString(mySign);
}
}
@@ -39,4 +39,9 @@ public class TemplateToken extends Token {
public TemplateImpl getTemplate() {
return myTemplate;
}
@Override
public String toString() {
return "TEMPLATE";
}
}
@@ -18,5 +18,5 @@ package com.intellij.codeInsight.template.zencoding;
/**
* @author Eugene.Kudelevsky
*/
class Token {
public class Token {
}
@@ -304,7 +304,6 @@ class XmlZenCodingInterpreter {
return new XmlZenCodingFilterImpl().buildAttributesString(attribute2value, numberInIteration);
}
private static void invokeTemplate(TemplateToken token,
final CustomTemplateCallback callback,
final int numberInIteration,
@@ -372,7 +371,7 @@ class XmlZenCodingInterpreter {
return new XmlZenCodingFilterImpl().toString(tag, context);
}
private static void fail() {
LOG.error("Input string was checked incorrectly during isApplicable() invokation");
private void fail() {
LOG.error("Input string was checked incorrectly during isApplicable() invokation: " + myTokens.toString());
}
}
@@ -336,15 +336,13 @@ public class XmlZenCodingTemplate extends ZenCodingTemplate {
int lineStart = editor.getDocument().getLineStartOffset(line);
int elementStart;
do {
elementStart = element != null ? element.getTextRange().getStartOffset() : 0;
elementStart = element.getTextRange().getStartOffset();
int startOffset = elementStart > lineStart ? elementStart : lineStart;
String key = computeKey(editor, startOffset);
if (checkTemplateKey(key, callback)) {
return key;
}
if (element != null) {
element = element.getParent();
}
element = element.getParent();
}
while (element != null && elementStart > lineStart);
return null;