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.
16 package com
.intellij
.codeInsight
.completion
;
18 import com
.intellij
.lang
.Language
;
19 import com
.intellij
.openapi
.application
.ApplicationManager
;
20 import com
.intellij
.openapi
.util
.Computable
;
21 import com
.intellij
.psi
.PsiElement
;
22 import com
.intellij
.psi
.PsiFile
;
23 import com
.intellij
.psi
.filters
.AndFilter
;
24 import com
.intellij
.psi
.filters
.ElementFilter
;
25 import com
.intellij
.psi
.filters
.OrFilter
;
26 import com
.intellij
.psi
.filters
.TextContainFilter
;
27 import com
.intellij
.psi
.filters
.getters
.HtmlAttributeValueGetter
;
28 import com
.intellij
.psi
.filters
.getters
.XmlAttributeValueGetter
;
29 import com
.intellij
.psi
.filters
.position
.XmlTokenTypeFilter
;
30 import com
.intellij
.psi
.util
.PsiTreeUtil
;
31 import com
.intellij
.psi
.xml
.*;
32 import com
.intellij
.util
.ArrayUtil
;
33 import org
.jetbrains
.annotations
.NonNls
;
38 * @author Maxim.Mossienko
40 @SuppressWarnings({"RefusedBequest"})
41 public class HtmlCompletionData
extends XmlCompletionData
{
42 private static CompletionData ourStyleCompletionData
;
43 private boolean myCaseInsensitive
;
44 private static CompletionData ourScriptCompletionData
;
45 private static final @NonNls String JAVASCRIPT_LANGUAGE_ID
= "JavaScript";
46 private static final @NonNls String STYLE_TAG
= "style";
47 private static final @NonNls String SCRIPT_TAG
= "script";
49 public HtmlCompletionData() {
53 protected HtmlCompletionData(boolean _caseInsensitive
) {
54 myCaseInsensitive
= _caseInsensitive
;
57 protected ElementFilter
createXmlEntityCompletionFilter() {
58 if (isCaseInsensitive()) {
61 new XmlTokenTypeFilter(XmlTokenType
.XML_DATA_CHARACTERS
),
62 new XmlTokenTypeFilter(XmlTokenType
.XML_ATTRIBUTE_VALUE_TOKEN
)
64 new TextContainFilter("&")
68 return super.createXmlEntityCompletionFilter();
71 private boolean equalNames(String str
,String str2
) {
72 if (!myCaseInsensitive
) return str
.equals(str2
);
73 return str
.equalsIgnoreCase(str2
);
76 protected boolean isCaseInsensitive() {
80 protected void setCaseInsensitive(final boolean caseInsensitive
) {
81 myCaseInsensitive
= caseInsensitive
;
84 protected XmlAttributeValueGetter
getAttributeValueGetter() {
85 return new HtmlAttributeValueGetter(!isCaseInsensitive());
88 protected ElementFilter
createTagCompletionFilter() {
89 return new ElementFilter() {
90 public boolean isAcceptable(Object element
, PsiElement context
) {
91 String name
= ((XmlTag
)context
).getName();
92 if (name
== null) return true;
94 if (element
instanceof PsiElement
&&
95 ((PsiElement
)element
).getParent() == context
) {
99 if (equalNames(name
, STYLE_TAG
) ||
100 equalNames(name
,SCRIPT_TAG
)) {
104 if ( isStyleAttributeContext((PsiElement
)element
) ) return false;
108 public boolean isClassAcceptable(Class hintClass
) {
114 protected ElementFilter
createAttributeCompletionFilter() {
115 return new ElementFilter() {
116 public boolean isAcceptable(Object element
, PsiElement context
) {
117 if (isStyleAttributeContext(context
)) return false;
121 public boolean isClassAcceptable(Class hintClass
) {
127 protected ElementFilter
createAttributeValueCompletionFilter() {
128 return new ElementFilter() {
129 public boolean isAcceptable(Object element
, PsiElement context
) {
130 if (isStyleAttributeContext(context
)) return false;
131 if ( isScriptContext((PsiElement
)element
) ) return false;
135 public boolean isClassAcceptable(Class hintClass
) {
141 private boolean isScriptContext(PsiElement element
) {
142 final Language language
= element
.getLanguage();
144 return language
.getID().equals(JAVASCRIPT_LANGUAGE_ID
);
147 private boolean isScriptTag(XmlTag tag
) {
149 String tagName
= tag
.getName();
150 if (tagName
== null) return false;
151 if (myCaseInsensitive
) return tagName
.equalsIgnoreCase(SCRIPT_TAG
);
153 return tagName
.equals(SCRIPT_TAG
);
159 private boolean isStyleTag(XmlTag tag
) {
161 String tagName
= tag
.getName();
162 if (tagName
== null) return false;
163 if (myCaseInsensitive
) return tagName
.equalsIgnoreCase(STYLE_TAG
);
165 return tagName
.equals(STYLE_TAG
);
171 public CompletionVariant
[] findVariants(final PsiElement position
, final PsiFile file
) {
172 return ApplicationManager
.getApplication().runReadAction(new Computable
<CompletionVariant
[]>() {
173 public CompletionVariant
[] compute() {
174 CompletionVariant
[] variants
= HtmlCompletionData
.super.findVariants(position
, file
);
176 if (ourStyleCompletionData
!=null && isStyleContext(position
)) {
177 final CompletionVariant
[] styleVariants
= ourStyleCompletionData
.findVariants(position
, file
);
179 variants
= ArrayUtil
.mergeArrays(variants
,styleVariants
, CompletionVariant
.class);
182 if (ourScriptCompletionData
!=null && isScriptContext(position
)) {
183 final CompletionVariant
[] scriptVariants
= ourScriptCompletionData
.findVariants(position
, file
);
185 variants
= ArrayUtil
.mergeArrays(variants
,scriptVariants
, CompletionVariant
.class);
192 private boolean isStyleAttributeContext(PsiElement position
) {
193 XmlAttribute parentOfType
= PsiTreeUtil
.getParentOfType(position
, XmlAttribute
.class, false);
195 if (parentOfType
!= null) {
196 String name
= parentOfType
.getName();
198 if (myCaseInsensitive
) return STYLE_TAG
.equalsIgnoreCase(name
);
199 return STYLE_TAG
.equals(name
); //name.endsWith("style");
205 private boolean isStyleContext(PsiElement position
) {
206 if (isStyleAttributeContext(position
)) return true;
208 return isStyleTag(PsiTreeUtil
.getParentOfType(position
, XmlTag
.class, false));
211 public void addKeywordVariants(final Set
<CompletionVariant
> set
, final PsiElement position
, final PsiFile file
) {
212 super.addKeywordVariants(set
, position
, file
);
214 ApplicationManager
.getApplication().runReadAction(new Runnable() {
216 if (ourStyleCompletionData
!= null && isStyleContext(position
)) {
217 ourStyleCompletionData
.addKeywordVariants(set
, position
, file
);
219 else if (ourScriptCompletionData
!= null && isScriptContext(position
)) {
220 ourScriptCompletionData
.addKeywordVariants(set
, position
, file
);
226 public static void setStyleCompletionData(CompletionData cssCompletionData
) {
227 ourStyleCompletionData
= cssCompletionData
;
230 public void registerVariant(CompletionVariant variant
) {
231 super.registerVariant(variant
);
232 if (isCaseInsensitive()) variant
.setCaseInsensitive(true);
235 public String
findPrefix(PsiElement insertedElement
, int offset
) {
236 XmlTag tag
= PsiTreeUtil
.getParentOfType(insertedElement
, XmlTag
.class, false);
237 String prefix
= null;
239 if (isScriptTag(tag
) &&
240 ourScriptCompletionData
!= null &&
241 !(insertedElement
.getParent() instanceof XmlAttributeValue
)) {
242 prefix
= ourScriptCompletionData
.findPrefix(insertedElement
, offset
);
243 } else if (isStyleTag(tag
) && ourStyleCompletionData
!=null) {
244 prefix
= ourStyleCompletionData
.findPrefix(insertedElement
, offset
);
247 if (prefix
== null) {
248 prefix
= super.findPrefix(insertedElement
, offset
);
250 boolean searchForEntities
=
251 insertedElement
instanceof XmlToken
&&
252 ( ((XmlToken
)insertedElement
).getTokenType() == XmlTokenType
.XML_DATA_CHARACTERS
||
253 ((XmlToken
)insertedElement
).getTokenType() == XmlTokenType
.XML_ATTRIBUTE_VALUE_TOKEN
256 if (searchForEntities
&& prefix
!= null) {
257 if (prefix
.startsWith("&")) {
258 prefix
= prefix
.substring(1);
259 } else if (prefix
.contains("&")) {
260 prefix
= prefix
.substring(prefix
.indexOf("&") + 1);
268 public static void setScriptCompletionData(CompletionData scriptCompletionData
) {
269 ourScriptCompletionData
= scriptCompletionData
;