update copyrights
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / daemon / impl / TextEditorHighlightingPassRegistrarImpl.java
blob01ef689aba40585f5858ae972632a77c83e152fe
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 com.intellij.codeInsight.daemon.impl;
19 import com.intellij.codeHighlighting.DirtyScopeTrackingHighlightingPassFactory;
20 import com.intellij.codeHighlighting.Pass;
21 import com.intellij.codeHighlighting.TextEditorHighlightingPass;
22 import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory;
23 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
24 import com.intellij.openapi.editor.Document;
25 import com.intellij.openapi.editor.Editor;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.psi.PsiCompiledElement;
28 import com.intellij.psi.PsiDocumentManager;
29 import com.intellij.psi.PsiFile;
30 import com.intellij.util.ArrayUtil;
31 import gnu.trove.*;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Set;
39 /**
40 * User: anna
41 * Date: 19-Apr-2006
43 public class TextEditorHighlightingPassRegistrarImpl extends TextEditorHighlightingPassRegistrarEx {
44 private final TIntObjectHashMap<PassConfig> myRegisteredPassFactories = new TIntObjectHashMap<PassConfig>();
45 private final List<DirtyScopeTrackingHighlightingPassFactory> myDirtyScopeTrackingFactories = new ArrayList<DirtyScopeTrackingHighlightingPassFactory>();
46 private int nextAvailableId = Pass.LAST_PASS + 1;
47 private boolean checkedForCycles;
48 private final Project myProject;
50 public TextEditorHighlightingPassRegistrarImpl(Project project) {
51 myProject = project;
54 private static class PassConfig {
55 private final TextEditorHighlightingPassFactory passFactory;
56 private final int[] startingPredecessorIds;
57 private final int[] completionPredecessorIds;
58 private final boolean runIntentionsPassAfter;
60 private PassConfig(@NotNull TextEditorHighlightingPassFactory passFactory,
61 boolean runIntentionsPassAfter,
62 @NotNull int[] completionPredecessorIds,
63 @NotNull int[] startingPredecessorIds) {
64 this.runIntentionsPassAfter = runIntentionsPassAfter;
65 this.completionPredecessorIds = completionPredecessorIds;
66 this.startingPredecessorIds = startingPredecessorIds;
67 this.passFactory = passFactory;
71 public void registerTextEditorHighlightingPass(TextEditorHighlightingPassFactory factory, int anchor, int anchorPass) {
72 Anchor anc = Anchor.FIRST;
73 switch (anchor) {
74 case FIRST : anc = Anchor.FIRST;
75 break;
76 case LAST : anc = Anchor.LAST;
77 break;
78 case BEFORE : anc = Anchor.BEFORE;
79 break;
80 case AFTER : anc = Anchor.AFTER;
81 break;
83 registerTextEditorHighlightingPass(factory, anc, anchorPass, true, true);
86 public synchronized int registerTextEditorHighlightingPass(@NotNull TextEditorHighlightingPassFactory factory,
87 @Nullable int[] runAfterCompletionOf,
88 @Nullable int[] runAfterOfStartingOf,
89 boolean runIntentionsPassAfter,
90 int forcedPassId) {
91 assert !checkedForCycles;
92 PassConfig info = new PassConfig(factory, runIntentionsPassAfter,
93 runAfterCompletionOf == null || runAfterCompletionOf.length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : runAfterCompletionOf,
94 runAfterOfStartingOf == null || runAfterOfStartingOf.length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : runAfterOfStartingOf);
95 int passId = forcedPassId == -1 ? nextAvailableId++ : forcedPassId;
96 PassConfig registered = myRegisteredPassFactories.get(passId);
97 assert registered == null: "Pass id "+passId +" has already been registered: "+ registered.passFactory;
98 myRegisteredPassFactories.put(passId, info);
99 if (factory instanceof DirtyScopeTrackingHighlightingPassFactory) {
100 myDirtyScopeTrackingFactories.add((DirtyScopeTrackingHighlightingPassFactory) factory);
102 return passId;
105 @NotNull
106 public List<TextEditorHighlightingPass> instantiatePasses(@NotNull final PsiFile psiFile, @NotNull final Editor editor, @NotNull final int[] passesToIgnore) {
107 final int[] nextId = new int[1];
108 synchronized (this) {
109 nextId[0] = nextAvailableId;
110 if (!checkedForCycles) {
111 checkedForCycles = true;
112 checkForCycles();
115 PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
116 PsiFile fileFromDoc = documentManager.getPsiFile(editor.getDocument());
117 if (!(fileFromDoc instanceof PsiCompiledElement)) {
118 assert fileFromDoc == psiFile : "Files are different: " + psiFile + ";" + fileFromDoc;
119 Document documentFromFile = documentManager.getDocument(psiFile);
120 assert documentFromFile == editor.getDocument() : "Documents are different: " + editor.getDocument() + ";" + documentFromFile;
122 final TIntObjectHashMap<TextEditorHighlightingPass> id2Pass = new TIntObjectHashMap<TextEditorHighlightingPass>();
123 final TIntArrayList ignoredPasses = new TIntArrayList();
124 myRegisteredPassFactories.forEachKey(new TIntProcedure() {
125 public boolean execute(int passId) {
126 if (ArrayUtil.find(passesToIgnore, passId) != -1) return true;
127 PassConfig passConfig = myRegisteredPassFactories.get(passId);
128 TextEditorHighlightingPassFactory factory = passConfig.passFactory;
129 final TextEditorHighlightingPass pass = factory.createHighlightingPass(psiFile, editor);
131 if (pass == null) {
132 ignoredPasses.add(passId);
134 else {
135 TIntArrayList ids = new TIntArrayList(passConfig.completionPredecessorIds.length);
136 for (int id : passConfig.completionPredecessorIds) {
137 if (myRegisteredPassFactories.containsKey(id)) ids.add(id);
139 pass.setCompletionPredecessorIds(ids.isEmpty() ? ArrayUtil.EMPTY_INT_ARRAY : ids.toNativeArray());
140 ids = new TIntArrayList(passConfig.startingPredecessorIds.length);
141 for (int id : passConfig.startingPredecessorIds) {
142 if (myRegisteredPassFactories.containsKey(id)) ids.add(id);
144 pass.setStartingPredecessorIds(ids.isEmpty() ? ArrayUtil.EMPTY_INT_ARRAY : ids.toNativeArray());
145 pass.setId(passId);
146 id2Pass.put(passId, pass);
148 return true;
152 final FileStatusMap statusMap = ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(myProject)).getFileStatusMap();
153 ignoredPasses.forEach(new TIntProcedure() {
154 public boolean execute(int passId) {
155 statusMap.markFileUpToDate(editor.getDocument(), psiFile, passId);
156 return true;
160 //sort is mainly for tests which expect passes to be run sequentially and produce correct results
161 return topoSort(id2Pass);
164 private static List<TextEditorHighlightingPass> topoSort(final TIntObjectHashMap<TextEditorHighlightingPass> id2Pass) {
165 final Set<TextEditorHighlightingPass> topPasses = new THashSet<TextEditorHighlightingPass>(id2Pass.size());
166 id2Pass.forEachValue(new TObjectProcedure<TextEditorHighlightingPass>() {
167 public boolean execute(TextEditorHighlightingPass object) {
168 topPasses.add(object);
169 return true;
172 id2Pass.forEachValue(new TObjectProcedure<TextEditorHighlightingPass>() {
173 public boolean execute(TextEditorHighlightingPass pass) {
174 for (int id : pass.getCompletionPredecessorIds()) {
175 TextEditorHighlightingPass pred = id2Pass.get(id);
176 if (pred != null) { //can be null if filtered out by passesToIgnore
177 topPasses.remove(pred);
180 for (int id : pass.getStartingPredecessorIds()) {
181 TextEditorHighlightingPass pred = id2Pass.get(id);
182 if (pred != null) { //can be null if filtered out by passesToIgnore
183 topPasses.remove(pred);
186 return true;
189 List<TextEditorHighlightingPass> result = new ArrayList<TextEditorHighlightingPass>();
190 for (TextEditorHighlightingPass topPass : topPasses) {
191 layout(topPass, result, id2Pass);
193 return result;
196 private static void layout(@NotNull final TextEditorHighlightingPass pass,
197 @NotNull final List<TextEditorHighlightingPass> result,
198 @NotNull final TIntObjectHashMap<TextEditorHighlightingPass> id2Pass) {
199 if (result.contains(pass)) return;
200 for (int id : pass.getCompletionPredecessorIds()) {
201 TextEditorHighlightingPass pred = id2Pass.get(id);
202 if (pred != null) {
203 layout(pred, result, id2Pass);
206 for (int id : pass.getStartingPredecessorIds()) {
207 TextEditorHighlightingPass pred = id2Pass.get(id);
208 if (pred != null) {
209 layout(pred, result, id2Pass);
212 result.add(pass);
215 private void checkForCycles() {
216 final TIntObjectHashMap<TIntHashSet> transitivePredecessors = new TIntObjectHashMap<TIntHashSet>();
218 myRegisteredPassFactories.forEachEntry(new TIntObjectProcedure<PassConfig>() {
219 public boolean execute(int passId, PassConfig config) {
220 TIntHashSet allPredecessors = new TIntHashSet(config.completionPredecessorIds);
221 allPredecessors.addAll(config.startingPredecessorIds);
222 transitivePredecessors.put(passId, allPredecessors);
223 allPredecessors.forEach(new TIntProcedure() {
224 public boolean execute(int predecessorId) {
225 PassConfig predecessor = myRegisteredPassFactories.get(predecessorId);
226 if (predecessor == null) return true;
227 TIntHashSet transitives = transitivePredecessors.get(predecessorId);
228 if (transitives == null) {
229 transitives = new TIntHashSet();
230 transitivePredecessors.put(predecessorId, transitives);
232 transitives.addAll(predecessor.completionPredecessorIds);
233 transitives.addAll(predecessor.startingPredecessorIds);
234 return true;
237 return true;
240 transitivePredecessors.forEachKey(new TIntProcedure() {
241 public boolean execute(int passId) {
242 if (transitivePredecessors.get(passId).contains(passId)) {
243 throw new IllegalArgumentException("There is a cycle introduced involving pass " + myRegisteredPassFactories.get(passId).passFactory);
245 return true;
250 public List<DirtyScopeTrackingHighlightingPassFactory> getDirtyScopeTrackingFactories() {
251 return myDirtyScopeTrackingFactories;