Problem 17716. another Throwable: PsiImplUtil.getParameterIndex
[fedora-idea.git] / plugins / IntelliLang / src / org / intellij / plugins / intelliLang / inject / InjectorUtils.java
blob7069c6c48dd2707914403ada54bdf8c0f2947745
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.intellij.plugins.intelliLang.inject;
19 import com.intellij.lang.Language;
20 import com.intellij.lang.injection.MultiHostRegistrar;
21 import com.intellij.openapi.extensions.Extensions;
22 import com.intellij.openapi.util.TextRange;
23 import com.intellij.openapi.util.Trinity;
24 import com.intellij.openapi.util.Condition;
25 import com.intellij.openapi.util.text.StringUtil;
26 import com.intellij.psi.PsiElement;
27 import com.intellij.psi.PsiFile;
28 import com.intellij.psi.PsiLanguageInjectionHost;
29 import com.intellij.psi.PsiLiteralExpression;
30 import com.intellij.psi.xml.XmlElement;
31 import com.intellij.util.NotNullFunction;
32 import com.intellij.util.containers.ContainerUtil;
33 import com.intellij.xml.util.XmlUtil;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
37 import java.util.Comparator;
38 import java.util.List;
39 import java.util.Set;
41 /**
42 * @author Gregory.Shrago
44 public class InjectorUtils {
45 public static final Comparator<TextRange> RANGE_COMPARATOR = new Comparator<TextRange>() {
46 public int compare(final TextRange o1, final TextRange o2) {
47 if (o1.intersects(o2)) return 0;
48 return o1.getStartOffset() - o2.getStartOffset();
52 private InjectorUtils() {
55 public static void addPlaceSafe(MultiHostRegistrar registrar, String prefix, String suffix, PsiLanguageInjectionHost host, TextRange textRange) {
56 registrar.addPlace(prefix, suffix, host, textRange);
59 public static String getUnescapedText(final PsiElement host, final String text) {
60 if (host instanceof PsiLiteralExpression) {
61 return StringUtil.unescapeStringCharacters(text);
63 else if (host instanceof XmlElement) {
64 return XmlUtil.unescape(text);
66 else {
67 return text;
69 }// Avoid sticking text and prefix/suffix together in a way that it would form a single token.
70 // See http://www.jetbrains.net/jira/browse/IDEADEV-8302#action_111865
71 // This code assumes that for the injected language a single space character is a token separator
72 // that doesn't (significantly) change the semantics if added to the prefix/suffix
74 // NOTE: This does not work in all cases, such as string literals in JavaScript where a
75 // space character isn't a token separator. See also comments in IDEA-8561
76 public static void adjustPrefixAndSuffix(String text, StringBuilder prefix, StringBuilder suffix) {
77 if (prefix.length() > 0) {
78 if (!endsWithSpace(prefix) && !startsWithSpace(text)) {
79 prefix.append(" ");
81 else if (endsWithSpace(prefix) && startsWithSpace(text)) {
82 trim(prefix);
85 if (suffix.length() > 0) {
86 if (text.length() == 0) {
87 // avoid to stick whitespace from prefix and suffix together
88 trim(suffix);
90 else if (!startsWithSpace(suffix) && !endsWithSpace(text)) {
91 suffix.insert(0, " ");
93 else if (startsWithSpace(suffix) && endsWithSpace(text)) {
94 trim(suffix);
99 public static void trim(StringBuilder string) {
100 while (startsWithSpace(string)) string.deleteCharAt(0);
101 while (endsWithSpace(string)) string.deleteCharAt(string.length() - 1);
104 public static boolean startsWithSpace(CharSequence sequence) {
105 final int length = sequence.length();
106 return length > 0 && sequence.charAt(0) <= ' ';
109 public static boolean endsWithSpace(CharSequence sequence) {
110 final int length = sequence.length();
111 return length > 0 && sequence.charAt(length - 1) <= ' ';
114 public static void registerInjection(Language language, List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> list, PsiFile containingFile, MultiHostRegistrar registrar) {
115 // if language isn't injected when length == 0, subsequent edits will not cause the language to be injected as well.
116 // Maybe IDEA core is caching a bit too aggressively here?
117 if (language == null/* && (pair.second.getLength() > 0*/) {
118 return;
120 boolean injectionStarted = false;
121 for (Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange> trinity : list) {
122 final PsiLanguageInjectionHost host = trinity.first;
123 if (host.getContainingFile() != containingFile) continue;
125 final TextRange textRange = trinity.third;
126 final InjectedLanguage injectedLanguage = trinity.second;
128 if (!injectionStarted) {
129 registrar.startInjecting(language);
130 injectionStarted = true;
132 if (injectedLanguage.isDynamic()) {
133 // Only adjust prefix/suffix if it has been computed dynamically. Otherwise some other
134 // useful cases may break. This system is far from perfect still...
135 final StringBuilder prefix = new StringBuilder(injectedLanguage.getPrefix());
136 final StringBuilder suffix = new StringBuilder(injectedLanguage.getSuffix());
137 adjustPrefixAndSuffix(getUnescapedText(host, textRange.substring(host.getText())), prefix, suffix);
139 addPlaceSafe(registrar, prefix.toString(), suffix.toString(), host, textRange);
141 else {
142 addPlaceSafe(registrar, injectedLanguage.getPrefix(), injectedLanguage.getSuffix(), host, textRange);
145 if (injectionStarted) {
146 registrar.doneInjecting();
150 @NotNull
151 public static Set<String> getActiveInjectionSupportIds() {
152 return ContainerUtil.map2Set(getActiveInjectionSupports(), new NotNullFunction<LanguageInjectionSupport, String>() {
153 @NotNull
154 public String fun(final LanguageInjectionSupport support) {
155 return support.getId();
160 public static LanguageInjectionSupport[] getActiveInjectionSupports() {
161 return Extensions.getExtensions(LanguageInjectionSupport.EP_NAME);
164 @Nullable
165 public static LanguageInjectionSupport findInjectionSupport(final String id) {
166 return ContainerUtil.find(getActiveInjectionSupports(), new Condition<LanguageInjectionSupport>() {
167 public boolean value(final LanguageInjectionSupport support) {
168 return support.getId().equals(id);