mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-14 18:05:27 +07:00
RegExp: conditionals support for Ruby and improved conditionals support for PHP (RUBY-27316, WI-51955)
GitOrigin-RevId: 4790db7eeaf658d724ba7c70ed138c9472bf8f2c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0954a97685
commit
eccf815b89
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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>
|
||||
@@ -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)(')')
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user