Skip to content
This repository
Browse code

Fixed most resolving of mixins and variables for issue #1 (still some…

… problems with Scss imports).
  • Loading branch information...
commit dbd0b7d82cd7a07450553c56b151496f0557e351 1 parent b7a7b81
ronniekk authored
1  META-INF/plugin.xml
@@ -269,5 +269,6 @@
269 269
 
270 270
     <extensions defaultExtensionNs="com.intellij">
271 271
         <projectConfigurable id="com.github.cssxfire.ProjectSettingsConfigurable" implementation="com.github.cssxfire.ProjectSettingsConfigurable"/>
  272
+        <gotoDeclarationHandler id="com.github.cssxfire.resolve.GotoDeclarationProvider" implementation="com.github.cssxfire.resolve.GotoDeclarationResolver"/>
272 273
     </extensions>
273 274
 </idea-plugin>
113  src/com/github/cssxfire/CssUtils.java
@@ -16,8 +16,7 @@
16 16
 
17 17
 package com.github.cssxfire;
18 18
 
19  
-import com.github.cssxfire.resolve.CssRulesetReference;
20  
-import com.github.cssxfire.resolve.CssXFireReferenceProvider;
  19
+import com.github.cssxfire.resolve.GotoDeclarationResolver;
21 20
 import com.intellij.lang.ASTNode;
22 21
 import com.intellij.lang.Language;
23 22
 import com.intellij.lang.css.CSSLanguage;
@@ -27,7 +26,9 @@
27 26
 import com.intellij.openapi.fileTypes.PlainTextFileType;
28 27
 import com.intellij.openapi.project.Project;
29 28
 import com.intellij.openapi.util.Ref;
30  
-import com.intellij.psi.*;
  29
+import com.intellij.psi.PsiElement;
  30
+import com.intellij.psi.PsiFile;
  31
+import com.intellij.psi.PsiFileFactory;
31 32
 import com.intellij.psi.css.*;
32 33
 import com.intellij.psi.search.PsiElementProcessor;
33 34
 import com.intellij.psi.search.PsiSearchHelper;
@@ -37,8 +38,6 @@
37 38
 
38 39
 import java.util.Arrays;
39 40
 import java.util.Collection;
40  
-import java.util.HashSet;
41  
-import java.util.Set;
42 41
 
43 42
 /**
44 43
  * Created by IntelliJ IDEA.
@@ -120,65 +119,29 @@ public static boolean processParents(@NotNull PsiElement element, @NotNull PsiEl
120 119
         return true;
121 120
     }
122 121
 
123  
-    /**
124  
-     * Recursion guard
125  
-     */
126  
-    private static ThreadLocal<Set<PsiElement>> processed = new ThreadLocal<Set<PsiElement>>() {
127  
-        @Override
128  
-        protected Set<PsiElement> initialValue() {
129  
-            return new HashSet<PsiElement>();
130  
-        }
131  
-    };
132  
-
133 122
     @Nullable
134  
-    public static CssTermList resolveVariableAssignment(@NotNull CssDeclaration cssDeclaration) {
  123
+    public static PsiElement resolveVariableAssignment(@NotNull CssDeclaration cssDeclaration) {
135 124
         CssTermList termList = PsiTreeUtil.getChildOfType(cssDeclaration, CssTermList.class);
136  
-        try {
137  
-            CssTermList result = null;
138  
-            while (termList != null) {
139  
-                if (!processed.get().add(termList)) {
140  
-                    return null; // infinite recursion detected
141  
-                }
142  
-                CssTerm[] terms = PsiTreeUtil.getChildrenOfType(termList, CssTerm.class);
143  
-                if (terms == null || terms.length != 1) {
144  
-                    return null; // not an explicit variable reference
145  
-                }
146  
-                termList = resolveTermList(termList);
147  
-                if (termList == null) {
148  
-                    return result;
149  
-                }
150  
-                result = termList;
151  
-            }
152  
-            return result;
153  
-        } finally {
154  
-            processed.get().clear();
  125
+        if (termList == null) {
  126
+            return null;
  127
+        }
  128
+        CssTerm[] terms = PsiTreeUtil.getChildrenOfType(termList, CssTerm.class);
  129
+        if (terms == null || terms.length != 1) {
  130
+            return null; // not an explicit variable reference
155 131
         }
156  
-    }
157 132
 
158  
-    @Nullable
159  
-    private static CssTermList resolveTermList(@NotNull CssTermList termList) {
160  
-        final Ref<CssTermList> result = new Ref<CssTermList>(null);
161  
-        PsiTreeUtil.processElements(termList, new PsiElementProcessor() {
162  
-            public boolean execute(@NotNull PsiElement psiElement) {
163  
-                if (psiElement instanceof CssTerm) {
164  
-                    return true; // CssIdentifierReference - skip
165  
-                }
166  
-                for (PsiReference reference : psiElement.getReferences()) {
167  
-                    PsiElement referenced = reference.resolve();
168  
-                    if (referenced != null) {
169  
-                        for (PsiElement child : referenced.getChildren()) {
170  
-                            if (child instanceof CssTermList) {
171  
-                                result.set((CssTermList) child);
172  
-                                return false;
173  
-                            }
174  
-                        }
175  
-                    }
  133
+        final Ref<PsiElement> resolved = new Ref<PsiElement>(null);
  134
+        PsiTreeUtil.processElements(terms[0], new PsiElementProcessor() {
  135
+            public boolean execute(@NotNull PsiElement element) {
  136
+                PsiElement[] targets = GotoDeclarationResolver.INSTANCE.getGotoDeclarationTargets(element, null);
  137
+                if (targets != null && targets.length == 1) {
  138
+                    resolved.set(targets[0]);
  139
+                    return false;
176 140
                 }
177 141
                 return true;
178 142
             }
179 143
         });
180  
-
181  
-        return result.get();
  144
+        return resolved.get();
182 145
     }
183 146
 
184 147
     public static boolean processCssDeclarations(@Nullable CssBlock block, final PsiElementProcessor<CssDeclaration> declarationProcessor) {
@@ -196,21 +159,12 @@ public static boolean processCssDeclarations(@Nullable CssBlock block, final Psi
196 159
         if (isDynamicCssLanguage(block) && ProjectSettings.getInstance(block.getProject()).isResolveMixins()) {
197 160
             return PsiTreeUtil.processElements(block, new PsiElementProcessor() {
198 161
                 public boolean execute(@NotNull PsiElement element) {
199  
-                    PsiReference[] references = getFromElementAndMyProviders(element);
200  
-                    for (PsiReference reference : references) {
201  
-                        if (reference instanceof CssRulesetReference) {
202  
-                            PsiElement resolved = reference.resolve();
203  
-                            if (resolved instanceof CssRuleset) {
204  
-                                if (!processCssDeclarations(((CssRuleset) resolved).getBlock(), declarationProcessor)) {
205  
-                                    return false;
206  
-                                }
207  
-                            }
208  
-                        } else if (reference != null && "org.jetbrains.plugins.scss.references.SCSSMixinReference".equals(reference.getClass().getName())) {
209  
-                            PsiElement resolved = reference.resolve();
210  
-                            if (resolved != null) {
211  
-                                if (!processCssDeclarations(PsiTreeUtil.getChildOfType(resolved, CssBlock.class), declarationProcessor)) {
212  
-                                    return false;
213  
-                                }
  162
+                    PsiElement[] targets = GotoDeclarationResolver.INSTANCE.getGotoDeclarationTargets(element, null);
  163
+                    if (targets != null && targets.length == 1) {
  164
+                        PsiElement resolved = targets[0];
  165
+                        if (resolved instanceof CssRuleset) {
  166
+                            if (!processCssDeclarations(((CssRuleset) resolved).getBlock(), declarationProcessor)) {
  167
+                                return false;
214 168
                             }
215 169
                         }
216 170
                     }
@@ -221,23 +175,6 @@ public boolean execute(@NotNull PsiElement element) {
221 175
         return true;
222 176
     }
223 177
 
224  
-    private static PsiReference[] getFromElementAndMyProviders(@NotNull PsiElement element) {
225  
-        PsiReference[] original = element.getReferences();
226  
-        PsiReference[] extras = CssXFireReferenceProvider.getReferencesByElement(element);
227  
-        if (original.length == 0) {
228  
-            return extras;
229  
-        }
230  
-        if (extras.length == 0) {
231  
-            return original;
232  
-        }
233  
-
234  
-        PsiReference[] all = new PsiReference[original.length + extras.length];
235  
-        System.arraycopy(original, 0, all, 0, original.length);
236  
-        System.arraycopy(extras, 0, all, original.length, extras.length);
237  
-
238  
-        return all;
239  
-    }
240  
-
241 178
     /**
242 179
      * Checks if the element is contained in a Less or Sass language file
243 180
      *
84  src/com/github/cssxfire/resolve/CssPluginsFacade.java
... ...
@@ -0,0 +1,84 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.intellij.openapi.fileTypes.FileType;
  20
+import com.intellij.openapi.fileTypes.FileTypeManager;
  21
+import com.intellij.openapi.fileTypes.PlainTextFileType;
  22
+import com.intellij.psi.PsiElement;
  23
+import com.intellij.psi.PsiFile;
  24
+import org.jetbrains.annotations.NotNull;
  25
+
  26
+import java.lang.reflect.Constructor;
  27
+
  28
+/**
  29
+ * Created by IntelliJ IDEA.
  30
+ * User: Ronnie
  31
+ */
  32
+public class CssPluginsFacade {
  33
+    private static final FileType LESS_FILE_TYPE = FileTypeManager.getInstance().getStdFileType("LESS");
  34
+    private static final FileType SASS_FILE_TYPE = FileTypeManager.getInstance().getStdFileType("SASS");
  35
+    private static final FileType SCSS_FILE_TYPE = FileTypeManager.getInstance().getStdFileType("SCSS");
  36
+    private static final CssResolveProcessor NOP_PROCESSOR = new CssResolveProcessor(null) {
  37
+        @Override
  38
+        public boolean executeInternal(@NotNull PsiElement element) {
  39
+            return false;
  40
+        }
  41
+    };
  42
+
  43
+    @NotNull
  44
+    public static CssResolveProcessor getVariableProcessor(@NotNull PsiElement element, String name) {
  45
+        return createResolveProcessor(element, name, "Variable");
  46
+    }
  47
+    
  48
+    @NotNull
  49
+    public static CssResolveProcessor getMixinProcessor(@NotNull PsiElement element, String name) {
  50
+        return createResolveProcessor(element, name, "Mixin");
  51
+    }
  52
+    
  53
+    @NotNull
  54
+    private static CssResolveProcessor createResolveProcessor(@NotNull PsiElement element, String name, String type) {
  55
+        PsiFile file = element.getContainingFile();
  56
+        if (file == null) {
  57
+            return NOP_PROCESSOR;
  58
+        }
  59
+        
  60
+        FileType fileType = file.getFileType();
  61
+        if (fileType == PlainTextFileType.INSTANCE) {
  62
+            return NOP_PROCESSOR;
  63
+        }
  64
+
  65
+        String prefix = null;
  66
+        if (fileType == LESS_FILE_TYPE) {
  67
+            prefix = "Less";
  68
+        } else if (fileType == SCSS_FILE_TYPE) {
  69
+            prefix = "Scss";
  70
+        }
  71
+
  72
+        if (prefix != null) {
  73
+            try {
  74
+                Class<? extends CssResolveProcessor> clazz = (Class<? extends CssResolveProcessor>) Class.forName(CssPluginsFacade.class.getPackage().getName() + "." + prefix + type + "Processor");
  75
+                Constructor<? extends CssResolveProcessor> constructor = clazz.getConstructor(String.class);
  76
+                return constructor.newInstance(name);
  77
+            } catch (Exception e) {
  78
+                e.printStackTrace();
  79
+            }
  80
+        }
  81
+
  82
+        return NOP_PROCESSOR;
  83
+    }
  84
+}
77  src/com/github/cssxfire/resolve/CssResolveProcessor.java
... ...
@@ -0,0 +1,77 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.intellij.openapi.util.Ref;
  20
+import com.intellij.psi.PsiElement;
  21
+import com.intellij.psi.css.CssImport;
  22
+import com.intellij.psi.search.PsiElementProcessor;
  23
+import org.jetbrains.annotations.NotNull;
  24
+import org.jetbrains.annotations.Nullable;
  25
+
  26
+import java.util.HashSet;
  27
+import java.util.Iterator;
  28
+import java.util.Set;
  29
+
  30
+/**
  31
+ * Created by IntelliJ IDEA.
  32
+ * User: Ronnie
  33
+ */
  34
+public abstract class CssResolveProcessor implements PsiElementProcessor {
  35
+    protected final String name;
  36
+    protected final Ref<PsiElement> result = new Ref<PsiElement>(null);
  37
+    private final Set<CssImport> imports = new HashSet<CssImport>();
  38
+
  39
+    protected CssResolveProcessor(String name) {
  40
+        this.name = name;
  41
+    }
  42
+
  43
+    public String getName() {
  44
+        return name;
  45
+    }
  46
+
  47
+    @Nullable
  48
+    public PsiElement getResult() {
  49
+        return result.get();
  50
+    }
  51
+
  52
+    @Nullable
  53
+    public synchronized CssImport popImport() {
  54
+        if (imports.isEmpty()) {
  55
+            return null;
  56
+        }
  57
+        Iterator<CssImport> iterator = imports.iterator();
  58
+        CssImport next = iterator.next();
  59
+        iterator.remove();
  60
+        return next;
  61
+    }
  62
+
  63
+    public boolean executeInScope(@NotNull PsiElement base) {
  64
+        return true;
  65
+    }
  66
+    
  67
+    public abstract boolean executeInternal(@NotNull PsiElement element);
  68
+
  69
+    public boolean execute(@NotNull PsiElement element) {
  70
+        if (element instanceof CssImport) {
  71
+            CssImport cssImport = (CssImport) element;
  72
+            imports.add(cssImport);
  73
+            return true;
  74
+        }
  75
+        return executeInternal(element);
  76
+    }
  77
+}
135  src/com/github/cssxfire/resolve/CssResolveUtils.java
... ...
@@ -0,0 +1,135 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.github.cssxfire.CssUtils;
  20
+import com.intellij.openapi.util.Key;
  21
+import com.intellij.openapi.vfs.VirtualFile;
  22
+import com.intellij.psi.PsiElement;
  23
+import com.intellij.psi.PsiFile;
  24
+import com.intellij.psi.ResolveState;
  25
+import com.intellij.psi.css.CssImport;
  26
+import com.intellij.psi.search.GlobalSearchScope;
  27
+import com.intellij.psi.search.TextOccurenceProcessor;
  28
+import com.intellij.psi.search.UsageSearchContext;
  29
+import com.intellij.psi.util.PsiTreeUtil;
  30
+import org.jetbrains.annotations.NotNull;
  31
+import org.jetbrains.annotations.Nullable;
  32
+
  33
+import java.util.Collection;
  34
+import java.util.HashSet;
  35
+
  36
+/**
  37
+ * <p>
  38
+ *     Resolving of a variable (or mixin) is performed in the following manner:
  39
+ *     <ol>
  40
+ *         <li>Process all declarations in current file.</li>
  41
+ *         <li>Process all files imported by current file, and their imports until there are no more imports.</li>
  42
+ *         <li>Search for files that are importing current file and repeat from step 1 (with <i>current</i> file being the <i>importing file</i>).</li>
  43
+ *     </ol>
  44
+ *     As soon as the declaration is found the processing is stopped, unless the declaration is an assignment of another reference. In this
  45
+ *     case the entire process is started all over for the new reference.
  46
+ * </p>
  47
+ * <p>
  48
+ * Created by IntelliJ IDEA.
  49
+ * User: Ronnie
  50
+ * </p>
  51
+ */
  52
+public class CssResolveUtils {
  53
+    private static final Key<Collection<String>> PROCESSED_PATHS = new Key<Collection<String>>("PROCESSED_PATHS");
  54
+    
  55
+    @Nullable
  56
+    public static PsiElement resolveVariable(@NotNull PsiElement base, @NotNull String name) {
  57
+        CssResolveProcessor processor = CssPluginsFacade.getVariableProcessor(base, name);
  58
+        if (processor.executeInScope(base)) {
  59
+            processFile(base.getContainingFile(), processor, ResolveState.initial().put(PROCESSED_PATHS, new HashSet<String>()));
  60
+        }
  61
+        return processor.getResult();
  62
+    }
  63
+    
  64
+    @Nullable
  65
+    public static PsiElement resolveMixin(@NotNull PsiElement base, @NotNull String name) {
  66
+        CssResolveProcessor processor = CssPluginsFacade.getMixinProcessor(base, name);
  67
+        if (processor.executeInScope(base)) {
  68
+            processFile(base.getContainingFile(), processor, ResolveState.initial().put(PROCESSED_PATHS, new HashSet<String>()));
  69
+        }
  70
+        return processor.getResult();
  71
+    }
  72
+    
  73
+    private static boolean processFile(final PsiFile file, final CssResolveProcessor processor, final ResolveState state) {
  74
+        if (!pushPath(file, state)) {
  75
+            // Already visited, skip
  76
+            return true;
  77
+        }
  78
+
  79
+        // Process declarations in file
  80
+        if (!PsiTreeUtil.processElements(file, processor)) {
  81
+            return false;
  82
+        }
  83
+
  84
+        // Process imports in file
  85
+        CssImport cssImport;
  86
+        while ((cssImport = processor.popImport()) != null) {
  87
+            PsiElement resolvedImport = cssImport.resolve();
  88
+            if (resolvedImport instanceof PsiFile) {
  89
+                if (!processFile((PsiFile) resolvedImport, processor, state)) {
  90
+                    return false;
  91
+                }
  92
+            }
  93
+        }
  94
+
  95
+        // Recurse on files importing this file
  96
+        CssUtils.getPsiSearchHelper(file.getProject()).processElementsWithWord(new TextOccurenceProcessor() {
  97
+            public boolean execute(PsiElement element, int offsetInElement) {
  98
+                if (element.getParent().getParent() instanceof CssImport) {
  99
+                    CssImport cssImport = (CssImport) element.getParent().getParent();
  100
+                    String uri = cssImport.getUriString();
  101
+                    if (uri.endsWith(file.getName())) {
  102
+                        PsiElement importedFile = cssImport.resolve();
  103
+                        if (file == importedFile) {
  104
+                            if (!processFile(cssImport.getContainingFile(), processor, state)) {
  105
+                                return false;
  106
+                            }
  107
+                        }
  108
+                    }
  109
+                }
  110
+                return true;
  111
+            }
  112
+        }, GlobalSearchScope.projectScope(file.getProject()), file.getName(), (short) (UsageSearchContext.IN_CODE | UsageSearchContext.IN_STRINGS), true);
  113
+
  114
+        return true;
  115
+    }
  116
+
  117
+    private static final Object LOCK = new Object();
  118
+
  119
+    private static boolean pushPath(@NotNull PsiFile file, @NotNull ResolveState state) {
  120
+        synchronized (LOCK) {
  121
+            if (!CssUtils.isDynamicCssLanguage(file)) {
  122
+                return false;
  123
+            }
  124
+            Collection<String> paths = state.get(PROCESSED_PATHS);
  125
+            if (paths == null) {
  126
+                return false;
  127
+            }
  128
+            VirtualFile virtualFile = file.getVirtualFile();
  129
+            if (virtualFile != null && paths.add(virtualFile.getPath())) {
  130
+                return true;
  131
+            }
  132
+            return false;
  133
+        }
  134
+    }
  135
+}
77  src/com/github/cssxfire/resolve/CssRulesetProcessor.java
... ...
@@ -1,77 +0,0 @@
1  
-/*
2  
- * Copyright 2012 Ronnie Kolehmainen
3  
- *
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
7  
- *
8  
- *     http://www.apache.org/licenses/LICENSE-2.0
9  
- *
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.
15  
- */
16  
-
17  
-package com.github.cssxfire.resolve;
18  
-
19  
-import com.intellij.openapi.util.Key;
20  
-import com.intellij.psi.PsiElement;
21  
-import com.intellij.psi.ResolveState;
22  
-import com.intellij.psi.css.CssRuleset;
23  
-import com.intellij.psi.css.CssSelectorList;
24  
-import com.intellij.psi.scope.PsiScopeProcessor;
25  
-import org.jetbrains.annotations.Nullable;
26  
-
27  
-import java.util.HashSet;
28  
-import java.util.Set;
29  
-
30  
-/**
31  
- * Created by IntelliJ IDEA.
32  
- * User: Ronnie
33  
- */
34  
-public class CssRulesetProcessor implements PsiScopeProcessor {
35  
-    /**
36  
-     * The name we are looking for, or null if we want all names
37  
-     */
38  
-    private final String name;
39  
-
40  
-    private final Set<CssRuleset> candidates = new HashSet<CssRuleset>();
41  
-
42  
-    public CssRulesetProcessor(String name) {
43  
-        this.name = name;
44  
-    }
45  
-
46  
-    public boolean execute(PsiElement element, ResolveState state) {
47  
-        if (element instanceof CssRuleset) {
48  
-            CssRuleset ruleset = (CssRuleset) element;
49  
-            CssSelectorList selectorList = ruleset.getSelectorList();
50  
-            if (selectorList.getSelectors().length == 1) {
51  
-                String text = selectorList.getText();
52  
-                if (text.startsWith(".")) {
53  
-                    text = text.substring(1);
54  
-                    if (name == null || name.equals(text)) {
55  
-                        candidates.add(ruleset);
56  
-                        if (name != null) {
57  
-                            return false;
58  
-                        }
59  
-                    }
60  
-                }
61  
-            }
62  
-        }
63  
-        return true;
64  
-    }
65  
-
66  
-    public CssRuleset[] getCandidates() {
67  
-        return candidates.toArray(new CssRuleset[candidates.size()]);
68  
-    }
69  
-
70  
-    public <T> T getHint(Key<T> hintKey) {
71  
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
72  
-    }
73  
-
74  
-    public void handleEvent(Event event, @Nullable Object associated) {
75  
-        //To change body of implemented methods use File | Settings | File Templates.
76  
-    }
77  
-}
137  src/com/github/cssxfire/resolve/CssRulesetReference.java
... ...
@@ -1,137 +0,0 @@
1  
-/*
2  
- * Copyright 2012 Ronnie Kolehmainen
3  
- *
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
7  
- *
8  
- *     http://www.apache.org/licenses/LICENSE-2.0
9  
- *
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.
15  
- */
16  
-
17  
-package com.github.cssxfire.resolve;
18  
-
19  
-import com.intellij.codeInsight.lookup.LookupElement;
20  
-import com.intellij.codeInsight.lookup.LookupElementBuilder;
21  
-import com.intellij.openapi.util.TextRange;
22  
-import com.intellij.psi.*;
23  
-import com.intellij.psi.css.CssRuleset;
24  
-import com.intellij.psi.css.CssRulesetList;
25  
-import com.intellij.psi.css.CssStylesheet;
26  
-import com.intellij.psi.impl.source.resolve.reference.impl.PsiPolyVariantCachingReference;
27  
-import com.intellij.psi.scope.PsiScopeProcessor;
28  
-import com.intellij.psi.util.PsiTreeUtil;
29  
-import com.intellij.util.IncorrectOperationException;
30  
-import org.jetbrains.annotations.NotNull;
31  
-
32  
-import java.util.ArrayList;
33  
-import java.util.List;
34  
-
35  
-/**
36  
- * Created by IntelliJ IDEA.
37  
- * User: Ronnie
38  
- */
39  
-public class CssRulesetReference extends PsiPolyVariantCachingReference {
40  
-    private static final ResolveResult[] EMPTY_RESOLVE_RESULTS = new ResolveResult[0];
41  
-    /**
42  
-     * Avoid completion on mixins for now
43  
-     */
44  
-    private static final boolean COMPLETION_ENABLED = false;
45  
-
46  
-    private final PsiElement place;
47  
-    private static final LookupElement[] EMPTY_VARIANTS = new LookupElement[0];
48  
-
49  
-    public CssRulesetReference(PsiElement place) {
50  
-        this.place = place;
51  
-    }
52  
-
53  
-    protected String getName() {
54  
-        return getElement().getText();
55  
-    }
56  
-
57  
-    @NotNull
58  
-    @Override
59  
-    protected ResolveResult[] resolveInner(boolean incompleteCode) {
60  
-        CssRulesetProcessor processor = new CssRulesetProcessor(getName());
61  
-        processTopLevelRulesets(processor);
62  
-        CssRuleset[] rulesets = processor.getCandidates();
63  
-        if (rulesets.length == 0) {
64  
-            return EMPTY_RESOLVE_RESULTS;
65  
-        }
66  
-        List<ResolveResult> results = new ArrayList<ResolveResult>();
67  
-        for (CssRuleset ruleset : rulesets) {
68  
-            results.add(new PsiElementResolveResult(ruleset));
69  
-        }
70  
-        return results.toArray(new ResolveResult[results.size()]);
71  
-    }
72  
-
73  
-    public PsiElement getElement() {
74  
-        return place;
75  
-    }
76  
-
77  
-    public TextRange getRangeInElement() {
78  
-        return new TextRange(0, getName().length());
79  
-    }
80  
-
81  
-    @NotNull
82  
-    public String getCanonicalText() {
83  
-        return getElement().getText();
84  
-    }
85  
-
86  
-    public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
87  
-        return null;
88  
-    }
89  
-
90  
-    public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
91  
-        return null;
92  
-    }
93  
-
94  
-    @NotNull
95  
-    public Object[] getVariants() {
96  
-        if (COMPLETION_ENABLED) {
97  
-            CssRulesetProcessor processor = new CssRulesetProcessor(null);
98  
-            processTopLevelRulesets(processor);
99  
-            return toLookupElements(processor.getCandidates());
100  
-        }
101  
-        return EMPTY_VARIANTS;
102  
-    }
103  
-
104  
-    private LookupElement[] toLookupElements(CssRuleset[] candidates) {
105  
-        LookupElement[] results = new LookupElement[candidates.length];
106  
-        for (int i = 0; i < candidates.length; i++) {
107  
-            String name = candidates[i].getSelectorList().getText();
108  
-            results[i] = LookupElementBuilder.create(candidates[i], name.substring(1)).setPresentableText(name);
109  
-        }
110  
-        return results;
111  
-    }
112  
-
113  
-    /**
114  
-     * Process all top-level rulesets with given processor
115  
-     *
116  
-     * @param processor the processor
117  
-     */
118  
-    private void processTopLevelRulesets(PsiScopeProcessor processor) {
119  
-        PsiFile file = getElement().getContainingFile();
120  
-        CssStylesheet[] styleSheets = PsiTreeUtil.getChildrenOfType(file, CssStylesheet.class);
121  
-        if (styleSheets != null) {
122  
-            for (CssStylesheet styleSheet : styleSheets) {
123  
-                CssRulesetList rulesetList = PsiTreeUtil.getChildOfType(styleSheet, CssRulesetList.class);
124  
-                if (rulesetList != null) {
125  
-                    CssRuleset[] rulesets = PsiTreeUtil.getChildrenOfType(rulesetList, CssRuleset.class);
126  
-                    if (rulesets != null) {
127  
-                        for (CssRuleset ruleset : rulesets) {
128  
-                            if (!processor.execute(ruleset, ResolveState.initial())) {
129  
-                                return;
130  
-                            }
131  
-                        }
132  
-                    }
133  
-                }
134  
-            }
135  
-        }
136  
-    }
137  
-}
56  src/com/github/cssxfire/resolve/CssXFireReferenceProvider.java
... ...
@@ -1,56 +0,0 @@
1  
-/*
2  
- * Copyright 2012 Ronnie Kolehmainen
3  
- *
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
7  
- *
8  
- *     http://www.apache.org/licenses/LICENSE-2.0
9  
- *
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.
15  
- */
16  
-
17  
-package com.github.cssxfire.resolve;
18  
-
19  
-import com.intellij.psi.PsiElement;
20  
-import com.intellij.psi.PsiReference;
21  
-import com.intellij.psi.PsiReferenceProvider;
22  
-import com.intellij.psi.css.CssRuleset;
23  
-import com.intellij.psi.util.PsiTreeUtil;
24  
-import com.intellij.psi.xml.XmlToken;
25  
-import com.intellij.util.ProcessingContext;
26  
-import org.jetbrains.annotations.NotNull;
27  
-
28  
-/**
29  
- * Created by IntelliJ IDEA.
30  
- * User: Ronnie
31  
- */
32  
-public class CssXFireReferenceProvider extends PsiReferenceProvider {
33  
-    private static final CssXFireReferenceProvider INSTANCE = new CssXFireReferenceProvider();
34  
-    private static final ProcessingContext MY_PROCESSING_CONTEXT = new ProcessingContext();
35  
-
36  
-
37  
-    @NotNull
38  
-    public static PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement) {
39  
-        return INSTANCE.getReferencesByElement(psiElement, MY_PROCESSING_CONTEXT);
40  
-    }
41  
-
42  
-    @NotNull
43  
-    @Override
44  
-    public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {
45  
-        if (psiElement instanceof XmlToken) {
46  
-            if (PsiTreeUtil.getParentOfType(psiElement, CssRuleset.class) == null) {
47  
-                return PsiReference.EMPTY_ARRAY;
48  
-            }
49  
-            PsiElement prevSibling = psiElement.getPrevSibling();
50  
-            if (prevSibling != null && ".".equals(prevSibling.getText())) {
51  
-                return new PsiReference[]{new CssRulesetReference(psiElement)};
52  
-            }
53  
-        }
54  
-        return PsiReference.EMPTY_ARRAY;
55  
-    }
56  
-}
87  src/com/github/cssxfire/resolve/GotoDeclarationResolver.java
... ...
@@ -0,0 +1,87 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.github.cssxfire.CssUtils;
  20
+import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
  21
+import com.intellij.openapi.actionSystem.DataContext;
  22
+import com.intellij.openapi.editor.Editor;
  23
+import com.intellij.psi.PsiElement;
  24
+import com.intellij.psi.PsiReference;
  25
+import com.intellij.psi.xml.XmlToken;
  26
+
  27
+/**
  28
+ * Created by IntelliJ IDEA.
  29
+ * User: Ronnie
  30
+ */
  31
+public class GotoDeclarationResolver implements GotoDeclarationHandler {
  32
+    public static final GotoDeclarationResolver INSTANCE = new GotoDeclarationResolver();
  33
+    public static final PsiElement[] EMPTY_TARGETS = new PsiElement[0];
  34
+
  35
+    public PsiElement[] getGotoDeclarationTargets(PsiElement element, Editor editor) {
  36
+        return getGotoDeclarationTargets(element, 0, editor);
  37
+    }
  38
+
  39
+    public PsiElement[] getGotoDeclarationTargets(PsiElement element, int i, Editor editor) {
  40
+        if (CssUtils.isDynamicCssLanguage(element)) {
  41
+            if (element instanceof XmlToken) {
  42
+                PsiElement parent = element.getParent();
  43
+                if (parent != null) {
  44
+                    String text = element.getText();
  45
+                    if (text.startsWith("$") || text.startsWith("@")) {
  46
+                        // Scss & Less variables
  47
+                        PsiReference[] references = parent.getReferences();
  48
+                        if (references.length == 1){// && references[0].resolve() == null) {
  49
+                            PsiElement resolved = CssResolveUtils.resolveVariable(element, text);
  50
+                            System.out.println("resolved = " + resolved);
  51
+                            if (resolved != null) {
  52
+                                return new PsiElement[] {resolved};
  53
+                            }
  54
+                        }
  55
+                    } else {
  56
+                        if (parent.getText().startsWith("@include")) {
  57
+                            // Scss mixin
  58
+                            PsiElement resolved = CssResolveUtils.resolveMixin(element, text);
  59
+                            System.out.println("resolved = " + resolved);
  60
+                            if (resolved != null) {
  61
+                                return new PsiElement[]{resolved};
  62
+                            }
  63
+                        } else {
  64
+                            PsiElement prevSibling = element.getPrevSibling();
  65
+                            if (prevSibling != null) {
  66
+                                text = prevSibling.getText() + text;
  67
+                                if (text.startsWith(".") || text.startsWith("#")) {
  68
+                                    // Less mixin
  69
+                                    PsiElement resolved = CssResolveUtils.resolveMixin(element, text);
  70
+                                    System.out.println("resolved = " + resolved);
  71
+                                    if (resolved != null) {
  72
+                                        return new PsiElement[]{resolved};
  73
+                                    }
  74
+                                }
  75
+                            }
  76
+                        }
  77
+                    }
  78
+                }
  79
+            }
  80
+        }
  81
+        return EMPTY_TARGETS;
  82
+    }
  83
+
  84
+    public String getActionText(DataContext dataContext) {
  85
+        return "My action text";
  86
+    }
  87
+}
59  src/com/github/cssxfire/resolve/LessMixinProcessor.java
... ...
@@ -0,0 +1,59 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.intellij.psi.PsiElement;
  20
+import com.intellij.psi.PsiErrorElement;
  21
+import com.intellij.psi.css.CssRuleset;
  22
+import com.intellij.psi.css.CssSelectorList;
  23
+import com.intellij.psi.util.PsiTreeUtil;
  24
+import org.jetbrains.annotations.NotNull;
  25
+
  26
+/**
  27
+ * Created by IntelliJ IDEA.
  28
+ * User: Ronnie
  29
+ */
  30
+public class LessMixinProcessor extends CssResolveProcessor {
  31
+    public LessMixinProcessor(String name) {
  32
+        super(name);
  33
+    }
  34
+
  35
+    @Override
  36
+    public boolean executeInScope(@NotNull PsiElement base) {
  37
+        return super.executeInScope(base);
  38
+    }
  39
+
  40
+    @Override
  41
+    public boolean executeInternal(@NotNull PsiElement element) {
  42
+        if (element instanceof CssRuleset) {
  43
+            CssRuleset ruleset = (CssRuleset) element;
  44
+            CssSelectorList selectorList = ruleset.getSelectorList();
  45
+            if (selectorList.getSelectors().length == 1) {
  46
+                String text = selectorList.getText();
  47
+                if (text != null && text.equals(name)) {
  48
+                    if (PsiTreeUtil.findChildOfType(ruleset, PsiErrorElement.class) != null) {
  49
+                        // Error in PSI tree - skip
  50
+                        return true;
  51
+                    }
  52
+                    result.set(ruleset);
  53
+                    return false;
  54
+                }
  55
+            }
  56
+        }
  57
+        return true;
  58
+    }
  59
+}
88  src/com/github/cssxfire/resolve/LessVariableProcessor.java
... ...
@@ -0,0 +1,88 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.intellij.psi.PsiElement;
  20
+import com.intellij.psi.PsiErrorElement;
  21
+import com.intellij.psi.css.CssBlock;
  22
+import com.intellij.psi.css.CssRuleset;
  23
+import com.intellij.psi.util.PsiTreeUtil;
  24
+import org.jetbrains.annotations.NotNull;
  25
+import org.jetbrains.annotations.Nullable;
  26
+import org.jetbrains.plugins.less.psi.LESSVariableDeclarationImpl;
  27
+
  28
+/**
  29
+ * Created by IntelliJ IDEA.
  30
+ * User: Ronnie
  31
+ */
  32
+public class LessVariableProcessor extends CssResolveProcessor {
  33
+    public LessVariableProcessor(String name) {
  34
+        super(name);
  35
+    }
  36
+
  37
+    @Override
  38
+    public boolean executeInScope(@NotNull PsiElement base) {
  39
+        // If the variable is used within a mixin, check if the variable is declared as mixin parameter or
  40
+        // locally within the same block
  41
+        CssRuleset ruleset = PsiTreeUtil.getParentOfType(base, CssRuleset.class);
  42
+        if (ruleset != null) {
  43
+            if (!executeChildren(ruleset)) {
  44
+                return false;
  45
+            }
  46
+            CssBlock[] blocks = PsiTreeUtil.getChildrenOfType(ruleset, CssBlock.class);
  47
+            if (blocks != null) {
  48
+                for (CssBlock block : blocks) {
  49
+                    if (!executeChildren(block)) {
  50
+                        return false;
  51
+                    }
  52
+                }
  53
+            }
  54
+        }
  55
+        return super.executeInScope(base);
  56
+    }
  57
+    
  58
+    private boolean executeChildren(@Nullable PsiElement element) {
  59
+        if (element == null) {
  60
+            return true;
  61
+        }
  62
+        LESSVariableDeclarationImpl[] localVariableDeclarations = PsiTreeUtil.getChildrenOfType(element, LESSVariableDeclarationImpl.class);
  63
+        if (localVariableDeclarations != null) {
  64
+            for (LESSVariableDeclarationImpl localVariableDeclaration : localVariableDeclarations) {
  65
+                if (!executeInternal(localVariableDeclaration)) {
  66
+                    return false;
  67
+                }
  68
+            }
  69
+        }
  70
+        return true;
  71
+    }
  72
+
  73
+    public boolean executeInternal(@NotNull PsiElement element) {
  74
+        if (element instanceof LESSVariableDeclarationImpl) {
  75
+            LESSVariableDeclarationImpl lessVariableDeclaration = (LESSVariableDeclarationImpl) element;
  76
+            PsiElement namedElement = lessVariableDeclaration.getNamedElement();
  77
+            if (namedElement != null && namedElement.getText().equals(name)) {
  78
+                if (PsiTreeUtil.findChildOfType(lessVariableDeclaration.getParent(), PsiErrorElement.class) != null) {
  79
+                    // Error in PSI tree - skip
  80
+                    return true;
  81
+                }
  82
+                result.set(element);
  83
+                return false;
  84
+            }
  85
+        }
  86
+        return true;
  87
+    }
  88
+}
55  src/com/github/cssxfire/resolve/ScssMixinProcessor.java
... ...
@@ -0,0 +1,55 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  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.
  15
+ */
  16
+
  17
+package com.github.cssxfire.resolve;
  18
+
  19
+import com.intellij.psi.PsiElement;
  20
+import com.intellij.psi.PsiErrorElement;
  21
+import com.intellij.psi.util.PsiTreeUtil;
  22
+import org.jetbrains.annotations.NotNull;
  23
+import org.jetbrains.plugins.scss.psi.SCSSMixinDeclarationImpl;
  24
+
  25
+/**
  26
+ * Created by IntelliJ IDEA.
  27
+ * User: Ronnie
  28
+ */
  29
+public class ScssMixinProcessor extends CssResolveProcessor {
  30
+    public ScssMixinProcessor(String name) {
  31
+        super(name);
  32
+    }
  33
+
  34
+    @Override
  35
+    public boolean executeInScope(@NotNull PsiElement base) {
  36
+        return super.executeInScope(base);
  37
+    }
  38
+
  39
+    @Override
  40
+    public boolean executeInternal(@NotNull PsiElement element) {
  41
+        if (element.getParent() instanceof SCSSMixinDeclarationImpl) {
  42
+            SCSSMixinDeclarationImpl scssMixinDeclaration = (SCSSMixinDeclarationImpl) element.getParent();
  43
+            PsiElement namedElement = scssMixinDeclaration.getNamedElement();
  44
+            if (namedElement != null && namedElement.getText().equals(name)) {
  45
+                if (PsiTreeUtil.findChildOfType(scssMixinDeclaration, PsiErrorElement.class) != null) {
  46
+                    // Error in PSI tree - skip
  47
+                    return true;
  48
+                }
  49
+                result.set(scssMixinDeclaration);
  50
+                return false;
  51
+            }
  52
+        }
  53
+        return true;
  54
+    }
  55
+}
88  src/com/github/cssxfire/resolve/ScssVariableProcessor.java
... ...
@@ -0,0 +1,88 @@
  1
+/*
  2
+ * Copyright 2012 Ronnie Kolehmainen
  3
+ *
  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
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,