RegExp: conditionals support for Ruby and improved conditionals support for PHP (RUBY-27316, WI-51955)

GitOrigin-RevId: 4790db7eeaf658d724ba7c70ed138c9472bf8f2c
This commit is contained in:
Bas Leijdekkers
2020-11-05 21:05:27 +01:00
committed by intellij-monorepo-bot
parent 0954a97685
commit eccf815b89
21 changed files with 849 additions and 610 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -40,6 +40,7 @@ error.illegal.unicode.escape.sequence=Illegal unicode escape sequence
error.illegal.unsupported.escape.sequence=Illegal/unsupported escape sequence
error.invalid.group.name=Invalid group name
error.look.behind.groups.are.not.supported.in.this.regex.dialect=Look-behind groups are not supported in this regex dialect
error.lookaround.conditions.in.conditionals.not.supported.in.this.regex.dialect=Lookaround conditions for conditionals are not supported in this regex dialect
error.named.group.reference.not.allowed.inside.lookbehind=Named group reference not allowed inside lookbehind
error.named.unicode.characters.are.not.allowed.in.this.regex.dialect=Named Unicode characters are not allowed in this regex dialect
error.nested.quantifier.in.regexp=Nested quantifier in regexp
@@ -49,6 +50,7 @@ error.redundant.group.nesting=Redundant group nesting
error.repetition.value.too.large=Repetition value too large
error.this.boundary.is.not.supported.in.this.regex.dialect=This boundary is not supported in this regex dialect
error.this.hex.character.syntax.is.not.supported.in.this.regex.dialect=This hex character syntax is not supported in this regex dialect
error.this.kind.group.reference.condition.not.supported.in.this.regex.dialect=This kind of group reference condition is not supported in this regex dialect
error.this.named.group.reference.syntax.is.not.supported.in.this.regex.dialect=This named group reference syntax is not supported in this regex dialect
error.this.named.group.syntax.is.not.supported.in.this.regex.dialect=This named group syntax is not supported in this regex dialect
error.unequal.min.and.max.in.counted.quantifier.not.allowed.inside.lookbehind=Unequal min and max in counted quantifier not allowed inside lookbehind

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.intellij.lang.regexp;
import com.intellij.lang.BracePair;
@@ -34,7 +20,7 @@ public class RegExpBraceMatcher implements PairedBraceMatcher {
new BracePair(RegExpTT.PYTHON_NAMED_GROUP, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.PYTHON_NAMED_GROUP_REF, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.PCRE_RECURSIVE_NAMED_GROUP_REF, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.PCRE_COND_REF, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.CONDITIONAL, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.PCRE_BRANCH_RESET, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.RUBY_NAMED_GROUP, RegExpTT.GROUP_END, true),
new BracePair(RegExpTT.RUBY_QUOTED_NAMED_GROUP, RegExpTT.GROUP_END, true),

View File

@@ -116,8 +116,7 @@ public class RegExpHighlighter extends SyntaxHighlighterBase {
ourMap.put(RegExpTT.PYTHON_NAMED_GROUP, PARENTHS);
ourMap.put(RegExpTT.PYTHON_NAMED_GROUP_REF, PARENTHS);
ourMap.put(RegExpTT.PCRE_RECURSIVE_NAMED_GROUP_REF, PARENTHS);
ourMap.put(RegExpTT.PYTHON_COND_REF, PARENTHS);
ourMap.put(RegExpTT.PCRE_COND_REF, PARENTHS);
ourMap.put(RegExpTT.CONDITIONAL, PARENTHS);
ourMap.put(RegExpTT.RUBY_NAMED_GROUP, PARENTHS);
ourMap.put(RegExpTT.RUBY_QUOTED_NAMED_GROUP, PARENTHS);
ourMap.put(RegExpTT.GROUP_BEGIN, PARENTHS);

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.intellij.lang.regexp;
import com.intellij.psi.PsiElement;
@@ -19,7 +19,20 @@ public interface RegExpLanguageHost {
boolean characterNeedsEscaping(char c);
boolean supportsPerl5EmbeddedComments();
boolean supportsPossessiveQuantifiers();
/**
* @return true, if this dialects support conditionals, i.e. the following construct: {@code (?(1)then|else)}
*/
boolean supportsPythonConditionalRefs();
/**
* @param condition a RegExpBackRef, RegExpNamedGroupRef or RegExpGroup instance.
* @return true, if this type of conditional condition is supported
*/
default boolean supportConditionalCondition(RegExpAtom condition) {
return true;
}
boolean supportsNamedGroupSyntax(RegExpGroup group);
boolean supportsNamedGroupRefSyntax(RegExpNamedGroupRef ref);
@NotNull

View File

@@ -123,6 +123,11 @@ public final class RegExpLanguageHosts extends ClassExtension<RegExpLanguageHost
return host != null && host.supportsPythonConditionalRefs();
}
public boolean supportConditionalCondition(RegExpAtom condition) {
final RegExpLanguageHost host = findRegExpHost(condition);
return host != null && host.supportConditionalCondition(condition);
}
public boolean supportsPossessiveQuantifiers(@Nullable final RegExpElement context) {
final RegExpLanguageHost host = findRegExpHost(context);
return host == null || host.supportsPossessiveQuantifiers();

View File

@@ -28,8 +28,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import static org.intellij.lang.regexp.RegExpTT.PCRE_RECURSIVE_NAMED_GROUP_REF;
public class RegExpParser implements PsiParser, LightPsiParser {
private static final TokenSet PROPERTY_TOKENS = TokenSet.create(RegExpTT.NUMBER, RegExpTT.COMMA, RegExpTT.NAME, RegExpTT.RBRACE);
private final EnumSet<RegExpCapability> myCapabilities;
@@ -407,7 +405,7 @@ public class RegExpParser implements PsiParser, LightPsiParser {
parseGroupEnd(builder);
marker.done(RegExpElementTypes.GROUP);
}
else if (type == RegExpTT.PYTHON_NAMED_GROUP_REF || type == PCRE_RECURSIVE_NAMED_GROUP_REF) {
else if (type == RegExpTT.PYTHON_NAMED_GROUP_REF || type == RegExpTT.PCRE_RECURSIVE_NAMED_GROUP_REF) {
parseNamedGroupRef(builder, marker, RegExpTT.GROUP_END);
}
else if (type == RegExpTT.RUBY_NAMED_GROUP_REF || type == RegExpTT.RUBY_NAMED_GROUP_CALL) {
@@ -416,16 +414,17 @@ public class RegExpParser implements PsiParser, LightPsiParser {
else if (type == RegExpTT.RUBY_QUOTED_NAMED_GROUP_REF || type == RegExpTT.RUBY_QUOTED_NAMED_GROUP_CALL) {
parseNamedGroupRef(builder, marker, RegExpTT.QUOTE);
}
else if (type == RegExpTT.PYTHON_COND_REF || type == RegExpTT.PCRE_COND_REF) {
else if (type == RegExpTT.CONDITIONAL) {
builder.advanceLexer();
parseCondRefName(builder, type);
checkMatches(builder, RegExpTT.GROUP_END, RegExpBundle.message("parse.error.unclosed.group.reference"));
parseCondition(builder);
parseBranch(builder);
if (builder.getTokenType() == RegExpTT.UNION) {
builder.advanceLexer();
parseBranch(builder);
}
checkMatches(builder, RegExpTT.GROUP_END, RegExpBundle.message("parse.error.unclosed.group"));
if (!checkMatches(builder, RegExpTT.GROUP_END, RegExpBundle.message("parse.error.unclosed.group"))) {
parseGroupEnd(builder);
}
marker.done(RegExpElementTypes.CONDITIONAL);
}
else if (type == RegExpTT.PROPERTY) {
@@ -447,12 +446,54 @@ public class RegExpParser implements PsiParser, LightPsiParser {
return marker;
}
protected void parseCondRefName(PsiBuilder builder, IElementType condRefType) {
if (builder.getTokenType() == RegExpTT.NAME || builder.getTokenType() == RegExpTT.NUMBER) {
private void parseCondition(PsiBuilder builder) {
final IElementType type = builder.getTokenType();
if (RegExpTT.LOOKAROUND_GROUPS.contains(type)) {
final PsiBuilder.Marker marker = builder.mark();
builder.advanceLexer();
parseGroupEnd(builder);
marker.done(RegExpElementTypes.GROUP);
}
else {
if (RegExpTT.GROUP_BEGIN == type) {
parseGroupReferenceCondition(builder, RegExpTT.GROUP_END);
}
else if (RegExpTT.QUOTED_CONDITION_BEGIN == type) {
parseGroupReferenceCondition(builder, RegExpTT.QUOTED_CONDITION_END);
}
else if (RegExpTT.ANGLE_BRACKET_CONDITION_BEGIN == type) {
parseGroupReferenceCondition(builder, RegExpTT.ANGLE_BRACKET_CONDITION_END);
}
}
}
private void parseGroupReferenceCondition(PsiBuilder builder, IElementType endToken) {
final PsiBuilder.Marker marker = builder.mark();
builder.advanceLexer();
final IElementType next = builder.getTokenType();
final Boolean named;
if (next == RegExpTT.NAME) {
builder.advanceLexer();
named = true;
}
else if (next == RegExpTT.NUMBER) {
builder.advanceLexer();
named = false;
}
else {
named = null;
builder.error(RegExpBundle.message("parse.error.group.name.or.number.expected"));
parsePattern(builder);
}
checkMatches(builder, endToken, RegExpBundle.message("parse.error.unclosed.group.reference"));
if (named == Boolean.TRUE) {
marker.done(RegExpElementTypes.NAMED_GROUP_REF);
}
else if (named == Boolean.FALSE) {
marker.done(RegExpElementTypes.BACKREF);
}
else {
marker.drop();
}
}

View File

@@ -145,10 +145,13 @@ public interface RegExpTT {
IElementType PCRE_RECURSIVE_NAMED_GROUP_REF = new RegExpElementType("PCRE_RECURSIVE_NAMED_GROUP");
/** (?P=name) */
IElementType PYTHON_NAMED_GROUP_REF = new RegExpElementType("PYTHON_NAMED_GROUP_REF");
/** (?(id/name)yes-pattern|no-pattern) */
IElementType PYTHON_COND_REF = new RegExpElementType("PYTHON_COND_REF");
/** (?(condition pattern)yes-pattern|no-pattern) */
IElementType PCRE_COND_REF = new RegExpElementType("PCRE_COND_REF");
/** (?(id/name/lookaround)yes-pattern|no-pattern) */
IElementType CONDITIONAL = new RegExpElementType("CONDITIONAL");
/** (' */
IElementType QUOTED_CONDITION_BEGIN = new RegExpElementType("QUOTED_CONDITION_BEGIN");
IElementType QUOTED_CONDITION_END = new RegExpElementType("QUOTED_CONDITION_END");
IElementType ANGLE_BRACKET_CONDITION_BEGIN = new RegExpElementType("ANGLE_BRACKET_CONDITION_BEGIN");
IElementType ANGLE_BRACKET_CONDITION_END = new RegExpElementType("ANGLE_BRACKET_CONDITION_END");
/** (?|regex) */
IElementType PCRE_BRANCH_RESET = new RegExpElementType("PCRE_BRANCH_RESET");
/** (?<name>... */
@@ -181,6 +184,7 @@ public interface RegExpTT {
TokenSet QUANTIFIERS = TokenSet.create(QUEST, PLUS, STAR, LBRACE);
TokenSet GROUPS = TokenSet.create(GROUP_BEGIN, NON_CAPT_GROUP, ATOMIC_GROUP, POS_LOOKAHEAD, NEG_LOOKAHEAD, POS_LOOKBEHIND, NEG_LOOKBEHIND, PCRE_BRANCH_RESET);
TokenSet LOOKAROUND_GROUPS = TokenSet.create(POS_LOOKAHEAD, NEG_LOOKAHEAD, POS_LOOKBEHIND, NEG_LOOKBEHIND);
TokenSet BOUNDARIES = TokenSet.create(BOUNDARY, CARET, DOLLAR);
}

View File

@@ -5,4 +5,10 @@ package org.intellij.lang.regexp.psi;
* @author yole
*/
public interface RegExpConditional extends RegExpAtom {
/**
* Returns condition of this conditional. This can be a numeric group reference, named group reference or lookaround group.
* @return a RegExpBackRef, RegExpNamedGroupRef or RegExpGroup instance.
*/
RegExpAtom getCondition();
}

View File

@@ -23,6 +23,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.SyntaxTraverser;
import com.intellij.util.IncorrectOperationException;
import org.intellij.lang.regexp.RegExpTT;
import org.intellij.lang.regexp.psi.RegExpBackref;
import org.intellij.lang.regexp.psi.RegExpElementVisitor;
import org.intellij.lang.regexp.psi.RegExpGroup;
@@ -46,6 +47,10 @@ public class RegExpBackrefImpl extends RegExpElementImpl implements RegExpBackre
@NotNull
private String getIndexNumberText() {
final ASTNode node = getNode().findChildByType(RegExpTT.NUMBER);
if (node != null) {
return node.getText();
}
final String s = getUnescapedText();
assert s.charAt(0) == '\\';
boolean pcreBackReference = s.charAt(1) == 'g';

View File

@@ -2,8 +2,8 @@
package org.intellij.lang.regexp.psi.impl;
import com.intellij.lang.ASTNode;
import org.intellij.lang.regexp.psi.RegExpElementVisitor;
import org.intellij.lang.regexp.psi.RegExpConditional;
import com.intellij.psi.PsiElement;
import org.intellij.lang.regexp.psi.*;
/**
* @author yole
@@ -17,4 +17,13 @@ public class RegExpConditionalImpl extends RegExpElementImpl implements RegExpCo
public void accept(RegExpElementVisitor visitor) {
visitor.visitRegExpConditional(this);
}
@Override
public RegExpAtom getCondition() {
final PsiElement sibling = getFirstChild().getNextSibling();
if (!(sibling instanceof RegExpBackref) && !(sibling instanceof RegExpNamedGroupRef) && !(sibling instanceof RegExpGroup)) {
return null;
}
return (RegExpAtom)sibling;
}
}

View File

@@ -128,4 +128,9 @@ public class RegExpGroupImpl extends RegExpElementImpl implements RegExpGroup {
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException();
}
@Override
public int getTextOffset() {
return getFirstChild().getNextSibling().getTextOffset();
}
}

View File

@@ -109,22 +109,24 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
%xstate QUANTIFIER
%xstate NON_QUANTIFIER
%xstate NEGATED_CLASS
%xstate QUOTED_CLASS1
%xstate QUOTED_CLASS
%xstate CLASS1
%state CLASS2
%state PROP
%state NAMED
%xstate OPTIONS
%xstate COMMENT
%xstate NAMED_GROUP
%xstate QUOTED_NAMED_GROUP
%xstate PY_NAMED_GROUP_REF
%xstate PY_COND_REF
%xstate BRACKET_EXPRESSION
%xstate MYSQL_CHAR_EXPRESSION
%xstate MYSQL_CHAR_EQ_EXPRESSION
%xstate EMBRACED_HEX
%state CONDITIONAL1
%state CONDITIONAL2
%state CLASS2
%state PROP
%state NAMED
DOT="."
LPAREN="("
RPAREN=")"
@@ -142,7 +144,7 @@ MYSQL_CHAR_NAME=[:letter:](-|[:letter:])*[:digit:]?
ANY=[^]
META1 = {ESCAPE} | {LBRACKET}
META2= {DOT} | "$" | "?" | "*" | "+" | "|" | "^" | {LBRACE} | {LPAREN} | {RPAREN}
META2 = {DOT} | "$" | "?" | "*" | "+" | "|" | "^" | {LBRACE} | {LPAREN} | {RPAREN}
CONTROL="t" | "n" | "r" | "f" | "a" | "e"
BOUNDARY="b" | "b{g}"| "B" | "A" | "z" | "Z" | "G" | "K"
@@ -335,14 +337,14 @@ BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
"^" { yybegin(CLASS1); return RegExpTT.CARET; }
}
<QUOTED_CLASS1> {
<QUOTED_CLASS> {
"\\E" { yypopstate(); return RegExpTT.QUOTE_END; }
{ANY} { states.set(states.size() - 1, CLASS2); return RegExpTT.CHARACTER; }
}
<CLASS1> {
{ESCAPE} "^" { yybegin(CLASS2); return RegExpTT.ESC_CHARACTER; }
{ESCAPE} "Q" { yypushstate(QUOTED_CLASS1); return RegExpTT.QUOTE_BEGIN; }
{ESCAPE} "Q" { yypushstate(QUOTED_CLASS); return RegExpTT.QUOTE_BEGIN; }
{ESCAPE} {RBRACKET} { yybegin(CLASS2); return allowEmptyCharacterClass ? RegExpTT.ESC_CHARACTER : RegExpTT.REDUNDANT_ESCAPE; }
{RBRACKET} { if (allowEmptyCharacterClass) { yypopstate(); return RegExpTT.CLASS_END; } yybegin(CLASS2); return RegExpTT.CHARACTER; }
{LBRACKET} / ":" { yybegin(CLASS2); if (allowPosixBracketExpressions) { yypushback(1); } else if (allowNestedCharacterClasses) { yypushstate(CLASS1); return RegExpTT.CLASS_BEGIN; } else { return RegExpTT.CHARACTER; } }
@@ -434,8 +436,8 @@ BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
"(?#" [^)]+ ")" { return RegExpTT.COMMENT; }
"(?P<" { yybegin(NAMED_GROUP); capturingGroupCount++; 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.PCRE_RECURSIVE_NAMED_GROUP_REF; }
"(?" / "(" { yybegin(CONDITIONAL1); return RegExpTT.CONDITIONAL; }
"(?&" { yybegin(NAMED_GROUP); return RegExpTT.PCRE_RECURSIVE_NAMED_GROUP_REF; }
"(?P>" { yybegin(NAMED_GROUP); return RegExpTT.PCRE_RECURSIVE_NAMED_GROUP_REF; }
"(?|" { return RegExpTT.PCRE_BRANCH_RESET; }
@@ -473,9 +475,21 @@ BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
{ANY} { yybegin(YYINITIAL); yypushback(1); }
}
<PY_COND_REF> {
<CONDITIONAL1> {
"(?=" { yybegin(YYINITIAL); return RegExpTT.POS_LOOKAHEAD; }
"(?!" { yybegin(YYINITIAL); return RegExpTT.NEG_LOOKAHEAD; }
"(?<=" { yybegin(YYINITIAL); return RegExpTT.POS_LOOKBEHIND; }
"(?<!" { yybegin(YYINITIAL); return RegExpTT.NEG_LOOKBEHIND; }
"('" { yybegin(CONDITIONAL2); return RegExpTT.QUOTED_CONDITION_BEGIN; }
"(<" { yybegin(CONDITIONAL2); return RegExpTT.ANGLE_BRACKET_CONDITION_BEGIN; }
"(" { yybegin(CONDITIONAL2); return RegExpTT.GROUP_BEGIN; }
}
<CONDITIONAL2> {
{GROUP_NAME} { return RegExpTT.NAME; }
[:digit:]+ { return RegExpTT.NUMBER; }
"')" { yybegin(YYINITIAL); return RegExpTT.QUOTED_CONDITION_END; }
">)" { yybegin(YYINITIAL); return RegExpTT.ANGLE_BRACKET_CONDITION_END; }
")" { yybegin(YYINITIAL); return RegExpTT.GROUP_END; }
{ANY} { yybegin(YYINITIAL); yypushback(1); }
}

View File

@@ -38,7 +38,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -235,21 +234,19 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
@Override
public void visitRegExpGroup(RegExpGroup group) {
final RegExpPattern pattern = group.getPattern();
if (pattern != null) {
final RegExpBranch[] branches = pattern.getBranches();
if (isEmpty(branches) && group.getNode().getLastChildNode().getElementType() == RegExpTT.GROUP_END) {
// catches "()" as well as "(|)"
myHolder.newAnnotation(HighlightSeverity.WARNING, RegExpBundle.message("error.empty.group")).create();
}
else if (branches.length == 1) {
final RegExpAtom[] atoms = branches[0].getAtoms();
if (atoms.length == 1 && atoms[0] instanceof RegExpGroup) {
final RegExpGroup.Type type = group.getType();
if (type == RegExpGroup.Type.CAPTURING_GROUP || type == RegExpGroup.Type.ATOMIC || type == RegExpGroup.Type.NON_CAPTURING) {
final RegExpGroup innerGroup = (RegExpGroup)atoms[0];
if (group.isCapturing() == innerGroup.isCapturing()) {
myHolder.newAnnotation(HighlightSeverity.WARNING, RegExpBundle.message("error.redundant.group.nesting")).create();
}
final RegExpBranch[] branches = pattern.getBranches();
if (isEmpty(branches) && group.getNode().getLastChildNode().getElementType() == RegExpTT.GROUP_END) {
// catches "()" as well as "(|)"
myHolder.newAnnotation(HighlightSeverity.WARNING, RegExpBundle.message("error.empty.group")).create();
}
else if (branches.length == 1) {
final RegExpAtom[] atoms = branches[0].getAtoms();
if (atoms.length == 1 && atoms[0] instanceof RegExpGroup) {
final RegExpGroup.Type type = group.getType();
if (type == RegExpGroup.Type.CAPTURING_GROUP || type == RegExpGroup.Type.ATOMIC || type == RegExpGroup.Type.NON_CAPTURING) {
final RegExpGroup innerGroup = (RegExpGroup)atoms[0];
if (group.isCapturing() == innerGroup.isCapturing()) {
myHolder.newAnnotation(HighlightSeverity.WARNING, RegExpBundle.message("error.redundant.group.nesting")).create();
}
}
}
@@ -293,7 +290,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
@Override
public void visitRegExpNamedGroupRef(RegExpNamedGroupRef groupRef) {
if (!myLanguageHosts.supportsNamedGroupRefSyntax(groupRef)) {
if (!(groupRef.getParent() instanceof RegExpConditional) && !myLanguageHosts.supportsNamedGroupRefSyntax(groupRef)) {
myHolder.newAnnotation(HighlightSeverity.ERROR,
RegExpBundle.message("error.this.named.group.reference.syntax.is.not.supported.in.this.regex.dialect"))
.create();
@@ -332,15 +329,29 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
myHolder.newAnnotation(HighlightSeverity.ERROR,
RegExpBundle.message("error.conditionals.are.not.supported.in.this.regex.dialect")).create();
}
final RegExpAtom condition = conditional.getCondition();
if (!myLanguageHosts.supportConditionalCondition(condition)) {
if (condition instanceof RegExpGroup) {
myHolder.newAnnotation(HighlightSeverity.ERROR,
RegExpBundle.message("error.lookaround.conditions.in.conditionals.not.supported.in.this.regex.dialect"))
.range(condition)
.create();
}
else if (condition != null) {
final ASTNode child = condition.getNode().getFirstChildNode();
final IElementType type = child.getElementType();
if (type == RegExpTT.QUOTED_CONDITION_BEGIN || type == RegExpTT.GROUP_BEGIN || type == RegExpTT.ANGLE_BRACKET_CONDITION_BEGIN) {
myHolder.newAnnotation(HighlightSeverity.ERROR,
RegExpBundle.message("error.this.kind.group.reference.condition.not.supported.in.this.regex.dialect"))
.range(condition)
.create();
}
}
}
}
private static boolean isEmpty(RegExpBranch[] branches) {
for (RegExpBranch branch : branches) {
if (branch.getAtoms().length > 0) {
return false;
}
}
return true;
return !ContainerUtil.exists(branches, branch -> branch.getAtoms().length > 0);
}
@Override

View File

@@ -2,9 +2,11 @@ REGEXP_FILE
RegExpPatternImpl: <(?(name)yes-pattern|no-pattern)>
RegExpBranchImpl: <(?(name)yes-pattern|no-pattern)>
RegExpConditionalImpl: <(?(name)yes-pattern|no-pattern)>
PsiElement(PYTHON_COND_REF)('(?(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
PsiElement(CONDITIONAL)('(?')
RegExpNamedGroupRefImpl: <(name)>
PsiElement(GROUP_BEGIN)('(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
RegExpBranchImpl: <yes-pattern>
RegExpCharImpl: <y>
PsiElement(CHARACTER)('y')

View File

@@ -2,9 +2,11 @@ REGEXP_FILE
RegExpPatternImpl: <(?(name)yes-pattern|{>
RegExpBranchImpl: <(?(name)yes-pattern|{>
RegExpConditionalImpl: <(?(name)yes-pattern|{>
PsiElement(PYTHON_COND_REF)('(?(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
PsiElement(CONDITIONAL)('(?')
RegExpNamedGroupRefImpl: <(name)>
PsiElement(GROUP_BEGIN)('(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
RegExpBranchImpl: <yes-pattern>
RegExpCharImpl: <y>
PsiElement(CHARACTER)('y')
@@ -34,4 +36,7 @@ REGEXP_FILE
<empty list>
PsiElement(LBRACE)('{')
PsiErrorElement:')' expected
<empty list>
<empty list>
RegExpPatternImpl: <>
RegExpBranchImpl: <>
<empty list>

View File

@@ -1,10 +1,12 @@
REGEXP_FILE
RegExpPatternImpl: <(?(name)yes-pattern|no_pattern|maybe-pattern>
RegExpBranchImpl: <(?(name)yes-pattern|no_pattern>
RegExpConditionalImpl: <(?(name)yes-pattern|no_pattern>
PsiElement(PYTHON_COND_REF)('(?(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
RegExpPatternImpl: <(?(name)yes-pattern|no_pattern|maybe-pattern)>
RegExpBranchImpl: <(?(name)yes-pattern|no_pattern|maybe-pattern)>
RegExpConditionalImpl: <(?(name)yes-pattern|no_pattern|maybe-pattern)>
PsiElement(CONDITIONAL)('(?')
RegExpNamedGroupRefImpl: <(name)>
PsiElement(GROUP_BEGIN)('(')
PsiElement(NAME)('name')
PsiElement(GROUP_END)(')')
RegExpBranchImpl: <yes-pattern>
RegExpCharImpl: <y>
PsiElement(CHARACTER)('y')
@@ -52,34 +54,35 @@ REGEXP_FILE
PsiElement(CHARACTER)('n')
PsiErrorElement:')' expected
<empty list>
PsiElement(UNION)('|')
RegExpBranchImpl: <maybe-pattern>
RegExpCharImpl: <m>
PsiElement(CHARACTER)('m')
RegExpCharImpl: <a>
PsiElement(CHARACTER)('a')
RegExpCharImpl: <y>
PsiElement(CHARACTER)('y')
RegExpCharImpl: <b>
PsiElement(CHARACTER)('b')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <->
PsiElement(CHARACTER)('-')
RegExpCharImpl: <p>
PsiElement(CHARACTER)('p')
RegExpCharImpl: <a>
PsiElement(CHARACTER)('a')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <r>
PsiElement(CHARACTER)('r')
RegExpCharImpl: <n>
PsiElement(CHARACTER)('n')
PsiErrorElement:Unmatched closing ')'
<empty list>
PsiElement(GROUP_END)(')')
RegExpPatternImpl: <|maybe-pattern>
RegExpBranchImpl: <>
<empty list>
PsiElement(UNION)('|')
RegExpBranchImpl: <maybe-pattern>
RegExpCharImpl: <m>
PsiElement(CHARACTER)('m')
RegExpCharImpl: <a>
PsiElement(CHARACTER)('a')
RegExpCharImpl: <y>
PsiElement(CHARACTER)('y')
RegExpCharImpl: <b>
PsiElement(CHARACTER)('b')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <->
PsiElement(CHARACTER)('-')
RegExpCharImpl: <p>
PsiElement(CHARACTER)('p')
RegExpCharImpl: <a>
PsiElement(CHARACTER)('a')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <t>
PsiElement(CHARACTER)('t')
RegExpCharImpl: <e>
PsiElement(CHARACTER)('e')
RegExpCharImpl: <r>
PsiElement(CHARACTER)('r')
RegExpCharImpl: <n>
PsiElement(CHARACTER)('n')
PsiElement(GROUP_END)(')')

View File

@@ -35,72 +35,77 @@ REGEXP_FILE
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
RegExpClosureImpl: <(?:(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*)?>
RegExpGroupImpl: <(?:(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*)>
PsiElement(NON_CAPT_GROUP)('(?:')
RegExpPatternImpl: <(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*>
RegExpBranchImpl: <(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*>
RegExpClosureImpl: <(?:\{|:)?>
RegExpGroupImpl: <(?:\{|:)>
PsiElement(NON_CAPT_GROUP)('(?:')
RegExpPatternImpl: <\{|:>
RegExpBranchImpl: <\{>
RegExpCharImpl: <\{>
PsiElement(ESC_CHARACTER)('\{')
PsiElement(UNION)('|')
RegExpBranchImpl: <:>
RegExpCharImpl: <:>
PsiElement(CHARACTER)(':')
PsiElement(GROUP_END)(')')
RegExpQuantifierImpl: <?>
PsiElement(QUEST)('?')
RegExpClosureImpl: <\s*>
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
RegExpConditionalImpl: <(?([^\s\}]+)>
PsiElement(PYTHON_COND_REF)('(?(')
PsiErrorElement:Group name or number expected
<empty list>
RegExpBranchImpl: <[^\s\}]+>
RegExpClosureImpl: <[^\s\}]+>
RegExpClassImpl: <[^\s\}]>
PsiElement(CLASS_BEGIN)('[')
PsiElement(CARET)('^')
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpCharImpl: <\}>
PsiElement(REDUNDANT_ESCAPE)('\}')
PsiElement(CLASS_END)(']')
RegExpQuantifierImpl: <+>
PsiElement(PLUS)('+')
RegExpGroupImpl: <(?:(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*)?(.*)>
PsiElement(NON_CAPT_GROUP)('(?:')
RegExpPatternImpl: <(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*)?(.*)>
RegExpBranchImpl: <(?:\{|:)?\s*(?([^\s\}]+)\s*\}?\s*)?(.*)>
RegExpClosureImpl: <(?:\{|:)?>
RegExpGroupImpl: <(?:\{|:)>
PsiElement(NON_CAPT_GROUP)('(?:')
RegExpPatternImpl: <\{|:>
RegExpBranchImpl: <\{>
RegExpCharImpl: <\{>
PsiElement(ESC_CHARACTER)('\{')
PsiElement(UNION)('|')
RegExpBranchImpl: <:>
RegExpCharImpl: <:>
PsiElement(CHARACTER)(':')
PsiElement(GROUP_END)(')')
RegExpClosureImpl: <\s*>
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
RegExpClosureImpl: <\}?>
RegExpCharImpl: <\}>
PsiElement(REDUNDANT_ESCAPE)('\}')
RegExpQuantifierImpl: <?>
PsiElement(QUEST)('?')
RegExpClosureImpl: <\s*>
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
PsiElement(GROUP_END)(')')
RegExpQuantifierImpl: <?>
PsiElement(QUEST)('?')
RegExpGroupImpl: <(.*)>
PsiElement(GROUP_BEGIN)('(')
RegExpPatternImpl: <.*>
RegExpBranchImpl: <.*>
RegExpClosureImpl: <.*>
RegExpSimpleClassImpl: <.>
PsiElement(DOT)('.')
RegExpQuantifierImpl: <?>
PsiElement(QUEST)('?')
RegExpClosureImpl: <\s*>
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
PsiElement(GROUP_END)(')')
RegExpClosureImpl: <(?([^\s\}]+)\s*\}?\s*)?>
RegExpConditionalImpl: <(?([^\s\}]+)\s*\}?\s*)>
PsiElement(CONDITIONAL)('(?')
PsiElement(GROUP_BEGIN)('(')
PsiErrorElement:Group name or number expected
<empty list>
RegExpPatternImpl: <[^\s\}]+>
RegExpBranchImpl: <[^\s\}]+>
RegExpClosureImpl: <[^\s\}]+>
RegExpClassImpl: <[^\s\}]>
PsiElement(CLASS_BEGIN)('[')
PsiElement(CARET)('^')
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpCharImpl: <\}>
PsiElement(REDUNDANT_ESCAPE)('\}')
PsiElement(CLASS_END)(']')
RegExpQuantifierImpl: <+>
PsiElement(PLUS)('+')
PsiElement(GROUP_END)(')')
RegExpBranchImpl: <\s*\}?\s*>
RegExpClosureImpl: <\s*>
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
RegExpClosureImpl: <\}?>
RegExpCharImpl: <\}>
PsiElement(REDUNDANT_ESCAPE)('\}')
RegExpQuantifierImpl: <?>
PsiElement(QUEST)('?')
RegExpClosureImpl: <\s*>
RegExpSimpleClassImpl: <\s>
PsiElement(CHAR_CLASS)('\s')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
PsiElement(GROUP_END)(')')
RegExpQuantifierImpl: <?>
PsiElement(QUEST)('?')
RegExpGroupImpl: <(.*)>
PsiElement(GROUP_BEGIN)('(')
RegExpPatternImpl: <.*>
RegExpBranchImpl: <.*>
RegExpClosureImpl: <.*>
RegExpSimpleClassImpl: <.>
PsiElement(DOT)('.')
RegExpQuantifierImpl: <*>
PsiElement(STAR)('*')
PsiElement(GROUP_END)(')')
PsiErrorElement:')' expected
<empty list>