mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
RegExp: replace in injected context fixes (IDEA-186023, IDEA-191355)
document based replacement avoids problems when using the Edit Injected Fragment intention context manipulator handles escaping in injected context
This commit is contained in:
@@ -1,29 +1,13 @@
|
||||
/*
|
||||
* 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-2018 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.inspection;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import org.intellij.lang.regexp.RegExpTT;
|
||||
import org.intellij.lang.regexp.psi.RegExpChar;
|
||||
import org.intellij.lang.regexp.psi.RegExpElementVisitor;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -67,8 +51,7 @@ public class OctalEscapeInspection extends LocalInspectionTool {
|
||||
static String buildReplacementText(RegExpChar aChar) {
|
||||
final int value = aChar.getValue();
|
||||
final String hex = Integer.toHexString(value);
|
||||
final String result = (hex.length() == 1 ? "\\x0" : "\\x") + hex;
|
||||
return RegExpReplacementUtil.escapeForContext(result, aChar);
|
||||
return (hex.length() == 1 ? "\\x0" : "\\x") + hex;
|
||||
}
|
||||
|
||||
private static class ReplaceWithHexEscapeFix implements LocalQuickFix {
|
||||
@@ -98,11 +81,7 @@ public class OctalEscapeInspection extends LocalInspectionTool {
|
||||
if (!(element instanceof RegExpChar)) {
|
||||
return;
|
||||
}
|
||||
final ASTNode node = element.getNode();
|
||||
final RegExpChar aChar = (RegExpChar)element;
|
||||
final ASTNode anchor = node.getFirstChildNode();
|
||||
node.addLeaf(RegExpTT.HEX_CHAR, buildReplacementText(aChar), anchor);
|
||||
node.removeChild(anchor);
|
||||
RegExpReplacementUtil.replaceInContext(element, buildReplacementText((RegExpChar)element));
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-24
@@ -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-2018 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.inspection;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
@@ -85,20 +71,15 @@ public class RedundantEscapeInspection extends LocalInspectionTool {
|
||||
if (!(element instanceof RegExpChar)) {
|
||||
return;
|
||||
}
|
||||
final RegExpChar aChar = (RegExpChar)element;
|
||||
final ASTNode node = aChar.getNode().getFirstChildNode();
|
||||
final ASTNode parent = node.getTreeParent();
|
||||
parent.addLeaf(RegExpTT.CHARACTER, replacement(aChar), node);
|
||||
parent.removeChild(node);
|
||||
RegExpReplacementUtil.replaceInContext(element, replacement((RegExpChar)element));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String replacement(RegExpChar aChar) {
|
||||
final int codePoint = aChar.getValue();
|
||||
final String s = Character.isSupplementaryCodePoint(codePoint)
|
||||
? Character.toString(Character.highSurrogate(codePoint)) + Character.toString(Character.lowSurrogate(codePoint))
|
||||
: Character.toString((char)codePoint);
|
||||
return RegExpReplacementUtil.escapeForContext(s, aChar);
|
||||
return Character.isSupplementaryCodePoint(codePoint)
|
||||
? Character.toString(Character.highSurrogate(codePoint)) + Character.toString(Character.lowSurrogate(codePoint))
|
||||
: Character.toString((char)codePoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
/*
|
||||
* 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-2018 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.inspection;
|
||||
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.ElementManipulator;
|
||||
import com.intellij.psi.ElementManipulators;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.xml.XmlElement;
|
||||
import com.intellij.xml.util.XmlStringUtil;
|
||||
import org.intellij.lang.regexp.psi.RegExpElement;
|
||||
import org.intellij.lang.regexp.psi.impl.RegExpElementImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
@@ -32,28 +19,29 @@ public class RegExpReplacementUtil {
|
||||
|
||||
private RegExpReplacementUtil() {}
|
||||
|
||||
public static String escapeForContext(String text, RegExpElement element) {
|
||||
final PsiElement context = element.getContainingFile().getContext();
|
||||
ElementManipulator<PsiElement> manipulator = context == null ? null : ElementManipulators.getManipulator(context);
|
||||
if (manipulator != null) {
|
||||
// use element manipulator to process escape sequences correctly for all supported languages
|
||||
PsiElement copy = context.copy(); // create a copy to avoid original element modifications
|
||||
PsiElement newElement = manipulator.handleContentChange(copy, text);
|
||||
if (newElement != null) {
|
||||
String newElementText = newElement.getText();
|
||||
TextRange newRange = manipulator.getRangeInElement(newElement);
|
||||
return newElementText.substring(newRange.getStartOffset(), newRange.getEndOffset());
|
||||
public static void replaceInContext(@NotNull PsiElement element, @NotNull String text) {
|
||||
final PsiFile file = element.getContainingFile();
|
||||
final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(element.getProject());
|
||||
if (injectedLanguageManager.isInjectedFragment(file)) {
|
||||
final PsiElement context = file.getContext();
|
||||
ElementManipulator<PsiElement> manipulator = context == null ? null : ElementManipulators.getManipulator(context);
|
||||
if (manipulator != null) {
|
||||
// use element manipulator to process escape sequences correctly for all supported languages
|
||||
final TextRange range = manipulator.getRangeInElement(context);
|
||||
if (manipulator.handleContentChange(context, range.cutOut(element.getTextRange()), text) != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (RegExpElementImpl.isLiteralExpression(context)) {
|
||||
text = StringUtil.escapeStringCharacters(text);
|
||||
}
|
||||
else if (context instanceof XmlElement) {
|
||||
text = XmlStringUtil.escapeString(text);
|
||||
}
|
||||
}
|
||||
if (RegExpElementImpl.isLiteralExpression(context)) {
|
||||
// otherwise, just pretend it is a Java-style string
|
||||
return StringUtil.escapeStringCharacters(text);
|
||||
}
|
||||
else if (context instanceof XmlElement) {
|
||||
return XmlStringUtil.escapeString(text);
|
||||
}
|
||||
else {
|
||||
return text;
|
||||
}
|
||||
final Document document = PsiDocumentManager.getInstance(element.getProject()).getDocument(file);
|
||||
assert document != null;
|
||||
final TextRange replaceRange = element.getTextRange();
|
||||
document.replaceString(replaceRange.getStartOffset(), replaceRange.getEndOffset(), text);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-24
@@ -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-2018 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.inspection;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
@@ -104,15 +90,10 @@ public class SingleCharAlternationInspection extends LocalInspectionTool {
|
||||
return;
|
||||
}
|
||||
final RegExpPattern pattern = (RegExpPattern)element;
|
||||
final String text = buildReplacementText(pattern);
|
||||
final RegExpBranch branch = RegExpFactory.createBranchFromText(text, element);
|
||||
final PsiElement parent = pattern.getParent();
|
||||
if (parent instanceof RegExpGroup && ((RegExpGroup)parent).getType() == RegExpGroup.Type.NON_CAPTURING) {
|
||||
parent.replace(branch.getAtoms()[0]);
|
||||
}
|
||||
else {
|
||||
pattern.replace(branch.getAtoms()[0]);
|
||||
}
|
||||
final PsiElement victim =
|
||||
(parent instanceof RegExpGroup && ((RegExpGroup)parent).getType() == RegExpGroup.Type.NON_CAPTURING) ? parent : pattern;
|
||||
RegExpReplacementUtil.replaceInContext(victim, buildReplacementText(pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +152,6 @@ public class SingleCharAlternationInspection extends LocalInspectionTool {
|
||||
}
|
||||
}
|
||||
text.append("]");
|
||||
return RegExpReplacementUtil.escapeForContext(text.toString(), pattern);
|
||||
return text.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.impl.source.tree.CompositeElement;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.intellij.lang.regexp.RegExpLanguage;
|
||||
import org.intellij.lang.regexp.psi.RegExpElement;
|
||||
@@ -84,13 +84,15 @@ public abstract class RegExpElementImpl extends ASTWrapperPsiElement implements
|
||||
if (astNode == null) {
|
||||
return false;
|
||||
}
|
||||
ASTNode child = null;
|
||||
if (astNode instanceof CompositeElement) { // in some languages token nodes are wrapped within a single-child composite
|
||||
ASTNode[] children = astNode.getChildren(null);
|
||||
if (children.length != 1) return false;
|
||||
astNode = children[0];
|
||||
if (children.length == 1) {
|
||||
child = children[0];
|
||||
}
|
||||
}
|
||||
final IElementType elementType = astNode.getElementType();
|
||||
final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(context.getLanguage());
|
||||
return parserDefinition.getStringLiteralElements().contains(elementType);
|
||||
final TokenSet literalElements = parserDefinition.getStringLiteralElements();
|
||||
return literalElements.contains(astNode.getElementType()) || child != null && literalElements.contains(child.getElementType());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-19
@@ -1,22 +1,7 @@
|
||||
/*
|
||||
* 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-2018 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.inspection;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -38,7 +23,6 @@ public class SingleCharAlternationInspectionTest extends RegExpInspectionTestCas
|
||||
}
|
||||
|
||||
public void testQuickfix() {
|
||||
PsiTestUtil.disablePsiTextConsistencyChecks(getTestRootDisposable());
|
||||
quickfixTest("<warning descr=\"Single character alternation in RegExp\">x|y|z</warning>", "[xyz]", "Replace with '[xyz]'");
|
||||
}
|
||||
|
||||
@@ -47,12 +31,10 @@ public class SingleCharAlternationInspectionTest extends RegExpInspectionTestCas
|
||||
}
|
||||
|
||||
public void testRemoveEscaping() {
|
||||
PsiTestUtil.disablePsiTextConsistencyChecks(getTestRootDisposable());
|
||||
quickfixTest("<warning descr=\"Single character alternation in RegExp\">\\^|\\å|\\{|\\\\|\\[</warning>", "[\\^å{\\\\\\[]", "Replace with '[\\^å{\\\\\\[]'");
|
||||
}
|
||||
|
||||
public void testEscapes() {
|
||||
PsiTestUtil.disablePsiTextConsistencyChecks(getTestRootDisposable());
|
||||
quickfixTest("(<warning descr=\"Single character alternation in RegExp\">\\.|<caret>\\[|]|\\(|\\)|\\{|}|\\^|\\?|\\*|\\||\\+|-|\\$</warning>)ab",
|
||||
"([.\\[\\](){}^?*|+\\-$])ab", "Replace with '[.\\[\\](){}^?*|+\\-$]'");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user