IntelliLang: support ignore-pattern option for disabling injection for host or string concatenation

New <ignore-pattern> tag in injection configs determines whether injection to host or concatenation must be ignored.
If at least one place in concatenation is ignored then the entire concatenation must be ignored and language must not be injected.
This option is useful in case the same place can contain different languages or template content that cannot be recognized
as a valid language literal, e.g. SQL with optional Velocity templating.

GitOrigin-RevId: 6fed7e959dc02fc1efc2ed044962caaa3179bfc9
This commit is contained in:
Yuriy Artamonov
2020-01-09 17:03:39 +00:00
committed by intellij-monorepo-bot
parent 198a91a353
commit 0409f037fd
4 changed files with 74 additions and 14 deletions
@@ -38,6 +38,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static java.util.Collections.emptyList;
/**
* @author cdr
*/
@@ -337,13 +339,13 @@ public final class ConcatenationInjector implements ConcatenationAwareInjector {
@NotNull
List<Pair<PsiLanguageInjectionHost, Language>> processInjectionWithContext(BaseInjection injection, boolean settingsAvailable) {
Language language = InjectorUtils.getLanguage(injection);
if (language == null) return Collections.emptyList();
if (language == null) return emptyList();
boolean separateFiles = !injection.isSingleFile() && StringUtil.isNotEmpty(injection.getValuePattern());
Ref<Boolean> unparsableRef = Ref.create(myUnparsable);
List<Object> objects = ContextComputationProcessor.collectOperands(injection.getPrefix(), injection.getSuffix(), unparsableRef, myOperands);
if (objects.isEmpty()) return Collections.emptyList();
if (objects.isEmpty()) return emptyList();
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> result = new ArrayList<>();
int len = objects.size();
for (int i = 0; i < len; i++) {
@@ -351,7 +353,7 @@ public final class ConcatenationInjector implements ConcatenationAwareInjector {
Object o = objects.get(i);
if (o instanceof String) {
curPrefix = (String)o;
if (i == len - 1) return Collections.emptyList(); // IDEADEV-26751
if (i == len - 1) return emptyList(); // IDEADEV-26751
o = objects.get(++i);
}
String curSuffix = null;
@@ -371,6 +373,10 @@ public final class ConcatenationInjector implements ConcatenationAwareInjector {
}
else {
if (curHost instanceof PsiLiteralExpression) {
if (injection.isIgnoredPlace(curHost)) {
return emptyList();
}
List<TextRange> textBlockInjectedArea = getTextBlockInjectedArea(curHost);
List<TextRange> injectedArea = (textBlockInjectedArea == null) ? injection.getInjectedArea(curHost) : textBlockInjectedArea;
for (int j = 0, injectedAreaSize = injectedArea.size(); j < injectedAreaSize; j++) {
@@ -392,7 +398,7 @@ public final class ConcatenationInjector implements ConcatenationAwareInjector {
}
}
if (result.isEmpty()) {
return Collections.emptyList();
return emptyList();
}
List<Pair<PsiLanguageInjectionHost, Language>> res = new ArrayList<>();
if (separateFiles) {
@@ -66,9 +66,14 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
@NonNls
private String myValuePattern = "";
private Pattern myCompiledValuePattern;
@NonNls
private String myIgnorePattern = "";
private Pattern myCompiledIgnorePattern;
private boolean mySingleFile;
public BaseInjection(@NotNull final String id) {
public BaseInjection(@NotNull String id) {
mySupportId = id;
}
@@ -142,6 +147,19 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
mySuffix = suffix;
}
@Override
public boolean isIgnoredPlace(PsiElement element) {
if (myCompiledIgnorePattern == null) {
return false;
}
LiteralTextEscaper<? extends PsiLanguageInjectionHost> textEscaper = ((PsiLanguageInjectionHost)element).createLiteralTextEscaper();
StringBuilder sb = new StringBuilder();
textEscaper.decode(ElementManipulators.getValueTextRange(element), sb);
return myCompiledIgnorePattern.matcher(StringPattern.newBombedCharSequence(sb)).find();
}
@Override
@NotNull
public List<TextRange> getInjectedArea(final PsiElement element) {
@@ -224,6 +242,7 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
result = 31 * result + myPrefix.hashCode();
result = 31 * result + mySuffix.hashCode();
result = 31 * result + myValuePattern.hashCode();
result = 31 * result + myIgnorePattern.hashCode();
return result;
}
@@ -237,6 +256,7 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
myDisplayName = other.getDisplayName();
setValuePattern(other.getValuePattern());
setIgnorePattern(other.getIgnorePattern());
mySingleFile = other.mySingleFile;
myPlaces = other.getInjectionPlaces().clone();
@@ -251,6 +271,7 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
myPrefix = StringUtil.notNullize(element.getChildText("prefix"));
mySuffix = StringUtil.notNullize(element.getChildText("suffix"));
setValuePattern(element.getChildText("value-pattern"));
setIgnorePattern(element.getChildText("ignore-pattern"));
mySingleFile = element.getChild("single-file") != null;
readExternalImpl(element);
final List<Element> placeElements = element.getChildren("place");
@@ -291,6 +312,9 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
if (StringUtil.isNotEmpty(myValuePattern)) {
e.addContent(new Element("value-pattern").setText(myValuePattern));
}
if (StringUtil.isNotEmpty(myIgnorePattern)) {
e.addContent(new Element("ignore-pattern").setText(myIgnorePattern));
}
if (mySingleFile) {
e.addContent(new Element("single-file"));
}
@@ -322,9 +346,31 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
myCompiledValuePattern = null;
}
}
catch (Exception e1) {
catch (Exception ex) {
myCompiledValuePattern = null;
Logger.getInstance(getClass().getName()).info("Invalid pattern", e1);
Logger.getInstance(getClass().getName()).info("Invalid value-pattern", ex);
}
}
@NotNull
public String getIgnorePattern() {
return myIgnorePattern;
}
public void setIgnorePattern(@RegExp @Nullable String pattern) {
try {
if (pattern != null && pattern.length() > 0) {
myIgnorePattern = pattern;
myCompiledIgnorePattern = Pattern.compile(pattern, Pattern.DOTALL);
}
else {
myIgnorePattern = "";
myCompiledIgnorePattern = null;
}
}
catch (Exception ex) {
myCompiledIgnorePattern = null;
Logger.getInstance(getClass().getName()).info("Invalid ignore-pattern", ex);
}
}
@@ -348,7 +394,6 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
return myCompiledValuePattern == null;
}
private static List<TextRange> getMatchingRanges(Matcher matcher, final int length) {
final List<TextRange> list = new SmartList<>();
int start = 0;
@@ -46,6 +46,12 @@ public interface Injection {
@NotNull
List<TextRange> getInjectedArea(PsiElement element);
/**
* Determines whether injection to host or concatenation must be ignored. If at least one place in concatenation is ignored then
* the entire concatenation must be ignored and language must not be injected.
*/
boolean isIgnoredPlace(PsiElement element);
/**
* Determines how the injection would like being displayed (e.g. attributes
* return a qualified TAG-NAME/@ATT-NAME combination name instead of just
@@ -115,14 +115,17 @@ public final class XmlLanguageInjector implements MultiHostInjector {
xmlTag.acceptChildren(new PsiElementVisitor() {
@Override
public void visitElement(@NotNull final PsiElement element) {
public void visitElement(@NotNull PsiElement element) {
if (element instanceof XmlText) {
if (!(element instanceof PsiLanguageInjectionHost) || element.getTextLength() == 0) return;
final List<TextRange> list = injection.getInjectedArea(element);
final InjectedLanguage l =
InjectedLanguage.create(injection.getInjectedLanguageId(), injection.getPrefix(), injection.getSuffix(), false);
for (TextRange textRange : list) {
result.add(Trinity.create((PsiLanguageInjectionHost)element, l, textRange));
if (!injection.isIgnoredPlace(element)) {
List<TextRange> list = injection.getInjectedArea(element);
InjectedLanguage l =
InjectedLanguage.create(injection.getInjectedLanguageId(), injection.getPrefix(), injection.getSuffix(), false);
for (TextRange textRange : list) {
result.add(Trinity.create((PsiLanguageInjectionHost)element, l, textRange));
}
}
}
else if (element instanceof XmlTag) {