mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
call applyInformation for all EditorBoundPass for each editor
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeHighlighting;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The pass which should be applied to every editor, even if there are many for this document.
|
||||
*
|
||||
* Ordinary {@link TextEditorHighlightingPass} is document-bound,
|
||||
* i.e. after the pass finishes the markup is stored in the document.
|
||||
* For example, there is no point to recalculate syntax errors for each splitted editor of the same document.
|
||||
* This pass however is for editor-specific markup, e.g. code folding.
|
||||
*/
|
||||
public abstract class EditorBoundHighlightingPass extends TextEditorHighlightingPass {
|
||||
@NotNull protected final Editor myEditor;
|
||||
@NotNull protected final PsiFile myFile;
|
||||
|
||||
protected EditorBoundHighlightingPass(@NotNull Editor editor,
|
||||
@NotNull PsiFile psiFile,
|
||||
boolean runIntentionPassAfter) {
|
||||
super(psiFile.getProject(), editor.getDocument(), runIntentionPassAfter);
|
||||
myEditor = editor;
|
||||
myFile = psiFile;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.intellij.codeInsight.daemon.impl;
|
||||
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
|
||||
import com.intellij.codeHighlighting.EditorBoundHighlightingPass;
|
||||
import com.intellij.codeInsight.folding.CodeFoldingManager;
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -24,30 +24,22 @@ import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class CodeFoldingPass extends TextEditorHighlightingPass implements PossiblyDumbAware {
|
||||
class CodeFoldingPass extends EditorBoundHighlightingPass implements PossiblyDumbAware {
|
||||
private static final Key<Boolean> THE_FIRST_TIME = Key.create("FirstFoldingPass");
|
||||
private Runnable myRunnable;
|
||||
private final Editor myEditor;
|
||||
private final PsiFile myFile;
|
||||
private volatile Runnable myRunnable;
|
||||
|
||||
CodeFoldingPass(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
super(project, editor.getDocument(), false);
|
||||
myEditor = editor;
|
||||
myFile = file;
|
||||
CodeFoldingPass(@NotNull Editor editor, @NotNull PsiFile file) {
|
||||
super(editor, file, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCollectInformation(@NotNull ProgressIndicator progress) {
|
||||
final boolean firstTime = isFirstTime(myFile, myEditor, THE_FIRST_TIME);
|
||||
Runnable runnable = CodeFoldingManager.getInstance(myProject).updateFoldRegionsAsync(myEditor, firstTime);
|
||||
synchronized (this) {
|
||||
myRunnable = runnable;
|
||||
}
|
||||
myRunnable = CodeFoldingManager.getInstance(myProject).updateFoldRegionsAsync(myEditor, firstTime);
|
||||
}
|
||||
|
||||
static boolean isFirstTime(PsiFile file, Editor editor, Key<Boolean> key) {
|
||||
@@ -61,10 +53,7 @@ class CodeFoldingPass extends TextEditorHighlightingPass implements PossiblyDumb
|
||||
|
||||
@Override
|
||||
public void doApplyInformationToEditor() {
|
||||
Runnable runnable;
|
||||
synchronized (this) {
|
||||
runnable = myRunnable;
|
||||
}
|
||||
Runnable runnable = myRunnable;
|
||||
if (runnable != null){
|
||||
try {
|
||||
runnable.run();
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,6 @@ public class CodeFoldingPassFactory extends AbstractProjectComponent implements
|
||||
@Override
|
||||
@NotNull
|
||||
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
|
||||
return new CodeFoldingPass(myProject, editor, file);
|
||||
return new CodeFoldingPass(editor, file);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-33
@@ -27,7 +27,6 @@ import com.intellij.codeInsight.daemon.ReferenceImporter;
|
||||
import com.intellij.codeInsight.hint.HintManager;
|
||||
import com.intellij.codeInsight.intention.impl.FileLevelIntentionComponent;
|
||||
import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
|
||||
import com.intellij.concurrency.Job;
|
||||
import com.intellij.ide.PowerSaveMode;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.Disposable;
|
||||
@@ -81,7 +80,7 @@ import java.util.*;
|
||||
/**
|
||||
* This class also controls the auto-reparse and auto-hints.
|
||||
*/
|
||||
public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOMExternalizable, NamedComponent {
|
||||
public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOMExternalizable, NamedComponent, Disposable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl");
|
||||
|
||||
private static final Key<List<LineMarkerInfo>> MARKERS_IN_EDITOR_DOCUMENT_KEY = Key.create("MARKERS_IN_EDITOR_DOCUMENT");
|
||||
@@ -115,41 +114,27 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
|
||||
public DaemonCodeAnalyzerImpl(@NotNull Project project,
|
||||
@NotNull DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings,
|
||||
@NotNull EditorTracker editorTracker,
|
||||
@NotNull final NamedScopeManager namedScopeManager,
|
||||
@NotNull final DependencyValidationManager dependencyValidationManager) {
|
||||
@SuppressWarnings("UnusedParameters") @NotNull final NamedScopeManager namedScopeManager,
|
||||
@SuppressWarnings("UnusedParameters") @NotNull final DependencyValidationManager dependencyValidationManager) {
|
||||
myProject = project;
|
||||
|
||||
mySettings = daemonCodeAnalyzerSettings;
|
||||
myEditorTracker = editorTracker;
|
||||
myLastSettings = ((DaemonCodeAnalyzerSettingsImpl)daemonCodeAnalyzerSettings).clone();
|
||||
|
||||
myFileStatusMap = new FileStatusMap(myProject);
|
||||
myPassExecutorService = new PassExecutorService(myProject) {
|
||||
@Override
|
||||
protected void afterApplyInformationToEditor(final TextEditorHighlightingPass pass,
|
||||
@NotNull final FileEditor fileEditor,
|
||||
final ProgressIndicator updateProgress) {
|
||||
if (fileEditor instanceof TextEditor) {
|
||||
log(updateProgress, pass, "Apply ");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDisposed() {
|
||||
return myDisposed || super.isDisposed();
|
||||
}
|
||||
};
|
||||
Disposer.register(project, myPassExecutorService);
|
||||
Disposer.register(project, myFileStatusMap);
|
||||
myFileStatusMap = new FileStatusMap(project);
|
||||
myPassExecutorService = new PassExecutorService(project);
|
||||
Disposer.register(this, myPassExecutorService);
|
||||
Disposer.register(this, myFileStatusMap);
|
||||
DaemonProgressIndicator.setDebug(LOG.isDebugEnabled());
|
||||
|
||||
assert !myInitialized : "Double Initializing";
|
||||
Disposer.register(myProject, new StatusBarUpdater(myProject));
|
||||
Disposer.register(this, new StatusBarUpdater(project));
|
||||
|
||||
myInitialized = true;
|
||||
myDisposed = false;
|
||||
myFileStatusMap.markAllFilesDirty();
|
||||
Disposer.register(project, new Disposable() {
|
||||
Disposer.register(this, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
assert myInitialized : "Disposing not initialized component";
|
||||
@@ -163,6 +148,11 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
public static List<HighlightInfo> getHighlights(@NotNull Document document, HighlightSeverity minSeverity, @NotNull Project project) {
|
||||
@@ -270,6 +260,17 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
|
||||
@NotNull int[] toIgnore,
|
||||
boolean canChangeDocument,
|
||||
@Nullable Runnable callbackWhileWaiting) throws ProcessCanceledException {
|
||||
return runPasses(file, document, Collections.singletonList(textEditor), toIgnore, canChangeDocument, callbackWhileWaiting);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
public List<HighlightInfo> runPasses(@NotNull PsiFile file,
|
||||
@NotNull Document document,
|
||||
@NotNull List<TextEditor> textEditors,
|
||||
@NotNull int[] toIgnore,
|
||||
boolean canChangeDocument,
|
||||
@Nullable Runnable callbackWhileWaiting) throws ProcessCanceledException {
|
||||
assert myInitialized;
|
||||
assert !myDisposed;
|
||||
Application application = ApplicationManager.getApplication();
|
||||
@@ -291,13 +292,16 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
|
||||
}
|
||||
fileStatusMap.allowDirt(canChangeDocument);
|
||||
|
||||
TextEditorBackgroundHighlighter highlighter = (TextEditorBackgroundHighlighter)textEditor.getBackgroundHighlighter();
|
||||
final List<TextEditorHighlightingPass> passes = highlighter.getPasses(toIgnore);
|
||||
HighlightingPass[] array = passes.toArray(new HighlightingPass[passes.size()]);
|
||||
assert array.length != 0 : "Highlighting is disabled for the file " + file;
|
||||
|
||||
Map<FileEditor, HighlightingPass[]> map = new HashMap<FileEditor, HighlightingPass[]>();
|
||||
for (TextEditor textEditor : textEditors) {
|
||||
TextEditorBackgroundHighlighter highlighter = (TextEditorBackgroundHighlighter)textEditor.getBackgroundHighlighter();
|
||||
final List<TextEditorHighlightingPass> passes = highlighter.getPasses(toIgnore);
|
||||
HighlightingPass[] array = passes.toArray(new HighlightingPass[passes.size()]);
|
||||
assert array.length != 0 : "Highlighting is disabled for the file " + file;
|
||||
map.put(textEditor, array);
|
||||
}
|
||||
final DaemonProgressIndicator progress = createUpdateProgress();
|
||||
myPassExecutorService.submitPasses(Collections.singletonMap((FileEditor)textEditor, array), progress, Job.DEFAULT_PRIORITY);
|
||||
myPassExecutorService.submitPasses(map, progress);
|
||||
try {
|
||||
while (progress.isRunning()) {
|
||||
try {
|
||||
@@ -323,8 +327,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
|
||||
List<HighlightInfo> highlights = getHighlights(document, null, project);
|
||||
return highlights;
|
||||
return getHighlights(document, null, project);
|
||||
}
|
||||
finally {
|
||||
fileStatusMap.allowDirt(true);
|
||||
@@ -738,7 +741,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM
|
||||
cancelUpdateProgress(true, "Cancel by alarm");
|
||||
myAlarm.cancelAllRequests();
|
||||
DaemonProgressIndicator progress = createUpdateProgress();
|
||||
myPassExecutorService.submitPasses(passes, progress, Job.DEFAULT_PRIORITY);
|
||||
myPassExecutorService.submitPasses(passes, progress);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+202
-175
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.intellij.codeInsight.daemon.impl;
|
||||
|
||||
import com.intellij.codeHighlighting.EditorBoundHighlightingPass;
|
||||
import com.intellij.codeHighlighting.HighlightingPass;
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
|
||||
import com.intellij.concurrency.Job;
|
||||
@@ -38,18 +39,18 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbAwareRunnable;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashMap;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import gnu.trove.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.*;
|
||||
@@ -59,15 +60,16 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
/**
|
||||
* @author cdr
|
||||
*/
|
||||
public abstract class PassExecutorService implements Disposable {
|
||||
public class PassExecutorService implements Disposable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.PassExecutorService");
|
||||
private static final boolean CHECK_CONSISTENCY = ApplicationManager.getApplication().isUnitTestMode();
|
||||
|
||||
private final Map<ScheduledPass, Job<Void>> mySubmittedPasses = new ConcurrentHashMap<ScheduledPass, Job<Void>>();
|
||||
private final Project myProject;
|
||||
private volatile boolean isDisposed;
|
||||
protected volatile boolean isDisposed;
|
||||
private final AtomicInteger nextPassId = new AtomicInteger(100);
|
||||
|
||||
public PassExecutorService(Project project) {
|
||||
public PassExecutorService(@NotNull Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@@ -97,15 +99,13 @@ public abstract class PassExecutorService implements Disposable {
|
||||
mySubmittedPasses.clear();
|
||||
}
|
||||
|
||||
public void submitPasses(@NotNull Map<FileEditor, HighlightingPass[]> passesMap, @NotNull DaemonProgressIndicator updateProgress, final int jobPriority) {
|
||||
public void submitPasses(@NotNull Map<FileEditor, HighlightingPass[]> passesMap, @NotNull DaemonProgressIndicator updateProgress) {
|
||||
if (isDisposed()) return;
|
||||
int id = 1;
|
||||
|
||||
// (doc, passId) -> created pass
|
||||
Map<Pair<Document, Integer>, ScheduledPass> toBeSubmitted = new THashMap<Pair<Document, Integer>, ScheduledPass>(passesMap.size());
|
||||
// null keys are ok
|
||||
Map<Document, List<FileEditor>> documentToEditors = new HashMap<Document, List<FileEditor>>();
|
||||
Map<FileEditor, List<TextEditorHighlightingPass>> textPasses = new HashMap<FileEditor, List<TextEditorHighlightingPass>>(passesMap.size());
|
||||
MultiMap<Document, FileEditor> documentToEditors = MultiMap.createSet();
|
||||
MultiMap<FileEditor, TextEditorHighlightingPass> documentBoundPasses = MultiMap.createSmartList();
|
||||
MultiMap<FileEditor, EditorBoundHighlightingPass> editorBoundPasses = MultiMap.createSmartList();
|
||||
for (Map.Entry<FileEditor, HighlightingPass[]> entry : passesMap.entrySet()) {
|
||||
FileEditor fileEditor = entry.getKey();
|
||||
HighlightingPass[] passes = entry.getValue();
|
||||
@@ -116,83 +116,59 @@ public abstract class PassExecutorService implements Disposable {
|
||||
document = editor.getDocument();
|
||||
}
|
||||
|
||||
for (int i = 0; i < passes.length; i++) {
|
||||
final HighlightingPass pass = passes[i];
|
||||
|
||||
TextEditorHighlightingPass textEditorHighlightingPass;
|
||||
if (pass instanceof TextEditorHighlightingPass) {
|
||||
textEditorHighlightingPass = (TextEditorHighlightingPass)pass;
|
||||
int prevId = 0;
|
||||
for (final HighlightingPass pass : passes) {
|
||||
if (pass instanceof EditorBoundHighlightingPass) {
|
||||
EditorBoundHighlightingPass editorPass = (EditorBoundHighlightingPass)pass;
|
||||
editorPass.setId(nextPassId.incrementAndGet()); // have to make ids unique for this document
|
||||
editorBoundPasses.putValue(fileEditor, editorPass);
|
||||
}
|
||||
else {
|
||||
// run all passes in sequence
|
||||
textEditorHighlightingPass = new TextEditorHighlightingPass(myProject, document, true) {
|
||||
@Override
|
||||
public void doCollectInformation(@NotNull ProgressIndicator progress) {
|
||||
pass.collectInformation(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doApplyInformationToEditor() {
|
||||
pass.applyInformationToEditor();
|
||||
}
|
||||
};
|
||||
textEditorHighlightingPass.setId(id++);
|
||||
if (i > 0) {
|
||||
textEditorHighlightingPass.setCompletionPredecessorIds(new int[]{i - 1});
|
||||
}
|
||||
TextEditorHighlightingPass textEditorHighlightingPass = convertToTextHighlightingPass(pass, document, nextPassId, prevId);
|
||||
document = textEditorHighlightingPass.getDocument();
|
||||
documentBoundPasses.putValue(fileEditor, textEditorHighlightingPass);
|
||||
documentToEditors.putValue(document, fileEditor);
|
||||
prevId = textEditorHighlightingPass.getId();
|
||||
}
|
||||
document = textEditorHighlightingPass.getDocument();
|
||||
|
||||
List<TextEditorHighlightingPass> textPassesForDocument = textPasses.get(fileEditor);
|
||||
if (textPassesForDocument == null) {
|
||||
textPassesForDocument = new SmartList<TextEditorHighlightingPass>();
|
||||
textPasses.put(fileEditor, textPassesForDocument);
|
||||
}
|
||||
textPassesForDocument.add(textEditorHighlightingPass);
|
||||
|
||||
List<FileEditor> editors = documentToEditors.get(document);
|
||||
if (editors == null) {
|
||||
editors = new SmartList<FileEditor>();
|
||||
documentToEditors.put(document, editors);
|
||||
}
|
||||
if (!editors.contains(fileEditor)) editors.add(fileEditor);
|
||||
}
|
||||
}
|
||||
|
||||
List<ScheduledPass> freePasses = new ArrayList<ScheduledPass>(documentToEditors.size()*5);
|
||||
List<ScheduledPass> dependentPasses = new ArrayList<ScheduledPass>(documentToEditors.size()*10);
|
||||
// (fileEditor, passId) -> created pass
|
||||
Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted = new THashMap<Pair<FileEditor, Integer>, ScheduledPass>(passesMap.size());
|
||||
|
||||
final AtomicInteger threadsToStartCountdown = new AtomicInteger(0);
|
||||
for (Map.Entry<Document, List<FileEditor>> entry : documentToEditors.entrySet()) {
|
||||
final List<FileEditor> fileEditors = entry.getValue();
|
||||
List<TextEditorHighlightingPass> passes = textPasses.get(getPreferredFileEditor(entry.getKey(), fileEditors));
|
||||
if (passes == null) {
|
||||
for (Map.Entry<Document, Collection<FileEditor>> entry : documentToEditors.entrySet()) {
|
||||
Collection<FileEditor> fileEditors = entry.getValue();
|
||||
Document document = entry.getKey();
|
||||
FileEditor preferredFileEditor = getPreferredFileEditor(document, fileEditors);
|
||||
List<TextEditorHighlightingPass> passes = (List<TextEditorHighlightingPass>)documentBoundPasses.get(preferredFileEditor);
|
||||
if (passes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
threadsToStartCountdown.addAndGet(passes.size());
|
||||
|
||||
// create one scheduled pass per unique id (possibly for multiple file editors. they all will be applied at the pass finish)
|
||||
ContainerUtil.quickSort(passes, new Comparator<TextEditorHighlightingPass>() {
|
||||
@Override
|
||||
public int compare(final TextEditorHighlightingPass o1, final TextEditorHighlightingPass o2) {
|
||||
return o1.getId() - o2.getId();
|
||||
}
|
||||
});
|
||||
int passId = -1;
|
||||
TextEditorHighlightingPass currentPass = null;
|
||||
for (int i = 0; i <= passes.size(); i++) {
|
||||
int newId = -1;
|
||||
if (i < passes.size()) {
|
||||
currentPass = passes.get(i);
|
||||
newId = currentPass.getId();
|
||||
}
|
||||
if (newId != passId) {
|
||||
createScheduledPass(fileEditors, currentPass, toBeSubmitted, passes, freePasses, dependentPasses, updateProgress, threadsToStartCountdown,
|
||||
jobPriority);
|
||||
passId = newId;
|
||||
}
|
||||
sortById(passes);
|
||||
for (TextEditorHighlightingPass currentPass : passes) {
|
||||
createScheduledPass(preferredFileEditor, currentPass, toBeSubmitted, passes, freePasses, dependentPasses, updateProgress, threadsToStartCountdown);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<FileEditor, Collection<EditorBoundHighlightingPass>> entry : editorBoundPasses.entrySet()) {
|
||||
FileEditor fileEditor = entry.getKey();
|
||||
Collection<EditorBoundHighlightingPass> createdEditorBoundPasses = entry.getValue();
|
||||
List<TextEditorHighlightingPass> createdDocumentBoundPasses = (List<TextEditorHighlightingPass>)documentBoundPasses.get(fileEditor);
|
||||
List<TextEditorHighlightingPass> allCreatedPasses = new ArrayList<TextEditorHighlightingPass>(createdDocumentBoundPasses);
|
||||
allCreatedPasses.addAll(createdEditorBoundPasses);
|
||||
|
||||
for (EditorBoundHighlightingPass pass : createdEditorBoundPasses) {
|
||||
createScheduledPass(fileEditor, pass, toBeSubmitted, allCreatedPasses, freePasses, dependentPasses, updateProgress, threadsToStartCountdown);
|
||||
}
|
||||
}
|
||||
|
||||
if (CHECK_CONSISTENCY) {
|
||||
assertConsistency(freePasses, toBeSubmitted, threadsToStartCountdown);
|
||||
}
|
||||
|
||||
log(updateProgress, null, "---------------------starting------------------------ " + threadsToStartCountdown.get(), freePasses);
|
||||
|
||||
for (ScheduledPass dependentPass : dependentPasses) {
|
||||
@@ -203,54 +179,117 @@ public abstract class PassExecutorService implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FileEditor getPreferredFileEditor(Document document, @NotNull List<FileEditor> fileEditors) {
|
||||
private void assertConsistency(List<ScheduledPass> freePasses,
|
||||
Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted,
|
||||
AtomicInteger threadsToStartCountdown) {
|
||||
assert threadsToStartCountdown.get() == toBeSubmitted.size();
|
||||
TIntObjectHashMap<Pair<ScheduledPass, Integer>> id2Visits = new TIntObjectHashMap<Pair<ScheduledPass, Integer>>();
|
||||
for (ScheduledPass freePass : freePasses) {
|
||||
id2Visits.put(freePass.myPass.getId(), Pair.create(freePass, 0));
|
||||
checkConsistency(freePass, id2Visits);
|
||||
}
|
||||
id2Visits.forEachEntry(new TIntObjectProcedure<Pair<ScheduledPass,Integer>>() {
|
||||
@Override
|
||||
public boolean execute(int id, Pair<ScheduledPass, Integer> pair) {
|
||||
int count = pair.second;
|
||||
assert count == 0 : id;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
assert id2Visits.size() == threadsToStartCountdown.get();
|
||||
}
|
||||
|
||||
private void checkConsistency(ScheduledPass pass, TIntObjectHashMap<Pair<ScheduledPass, Integer>> id2Visits) {
|
||||
for (ScheduledPass succ : ContainerUtil.concat(pass.mySuccessorsOnCompletion, pass.mySuccessorsOnSubmit)) {
|
||||
int succId = succ.myPass.getId();
|
||||
Pair<ScheduledPass, Integer> succPair = id2Visits.get(succId);
|
||||
if (succPair == null) {
|
||||
succPair = Pair.create(succ, succ.myRunningPredecessorsCount.get());
|
||||
id2Visits.put(succId, succPair);
|
||||
}
|
||||
int newPred = succPair.second - 1;
|
||||
id2Visits.put(succId, Pair.create(succ, newPred));
|
||||
assert newPred >= 0;
|
||||
if (newPred == 0) {
|
||||
checkConsistency((succ), id2Visits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TextEditorHighlightingPass convertToTextHighlightingPass(@NotNull final HighlightingPass pass,
|
||||
final Document document,
|
||||
@NotNull AtomicInteger id,
|
||||
int previousPassId) {
|
||||
TextEditorHighlightingPass textEditorHighlightingPass;
|
||||
if (pass instanceof TextEditorHighlightingPass) {
|
||||
textEditorHighlightingPass = (TextEditorHighlightingPass)pass;
|
||||
}
|
||||
else {
|
||||
// run all passes in sequence
|
||||
textEditorHighlightingPass = new TextEditorHighlightingPass(myProject, document, true) {
|
||||
@Override
|
||||
public void doCollectInformation(@NotNull ProgressIndicator progress) {
|
||||
pass.collectInformation(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doApplyInformationToEditor() {
|
||||
pass.applyInformationToEditor();
|
||||
}
|
||||
};
|
||||
textEditorHighlightingPass.setId(id.incrementAndGet());
|
||||
if (previousPassId != 0) {
|
||||
textEditorHighlightingPass.setCompletionPredecessorIds(new int[]{previousPassId});
|
||||
}
|
||||
}
|
||||
return textEditorHighlightingPass;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private FileEditor getPreferredFileEditor(Document document, @NotNull Collection<FileEditor> fileEditors) {
|
||||
assert !fileEditors.isEmpty();
|
||||
if (document != null) {
|
||||
final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
|
||||
if (file != null) {
|
||||
final FileEditor selected = FileEditorManager.getInstance(myProject).getSelectedEditor(file);
|
||||
if (fileEditors.contains(selected)) {
|
||||
if (selected != null && fileEditors.contains(selected)) {
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fileEditors.isEmpty()) {
|
||||
return fileEditors.get(0);
|
||||
}
|
||||
return null;
|
||||
return fileEditors.iterator().next();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ScheduledPass createScheduledPass(@NotNull List<FileEditor> fileEditors,
|
||||
private ScheduledPass createScheduledPass(@NotNull FileEditor fileEditor,
|
||||
@NotNull TextEditorHighlightingPass pass,
|
||||
@NotNull Map<Pair<Document, Integer>, ScheduledPass> toBeSubmitted,
|
||||
@NotNull Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted,
|
||||
@NotNull List<TextEditorHighlightingPass> textEditorHighlightingPasses,
|
||||
@NotNull List<ScheduledPass> freePasses,
|
||||
@NotNull List<ScheduledPass> dependentPasses,
|
||||
@NotNull DaemonProgressIndicator updateProgress,
|
||||
@NotNull AtomicInteger threadsToStartCountdown,
|
||||
int jobPriority) {
|
||||
@NotNull AtomicInteger threadsToStartCountdown) {
|
||||
int passId = pass.getId();
|
||||
Document document = pass.getDocument();
|
||||
Pair<Document, Integer> key = Pair.create(document, passId);
|
||||
Pair<FileEditor, Integer> key = Pair.create(fileEditor, passId);
|
||||
ScheduledPass scheduledPass = toBeSubmitted.get(key);
|
||||
if (scheduledPass != null) return scheduledPass;
|
||||
scheduledPass = new ScheduledPass(fileEditors, pass, updateProgress, threadsToStartCountdown, jobPriority);
|
||||
scheduledPass = new ScheduledPass(fileEditor, pass, updateProgress, threadsToStartCountdown);
|
||||
threadsToStartCountdown.incrementAndGet();
|
||||
toBeSubmitted.put(key, scheduledPass);
|
||||
for (int predecessorId : pass.getCompletionPredecessorIds()) {
|
||||
ScheduledPass predecessor = findOrCreatePredecessorPass(fileEditors, document, toBeSubmitted, textEditorHighlightingPasses, freePasses, dependentPasses,
|
||||
updateProgress, threadsToStartCountdown, jobPriority, predecessorId);
|
||||
ScheduledPass predecessor = findOrCreatePredecessorPass(fileEditor, toBeSubmitted, textEditorHighlightingPasses, freePasses, dependentPasses,
|
||||
updateProgress, threadsToStartCountdown, predecessorId);
|
||||
if (predecessor != null) {
|
||||
predecessor.mySuccessorsOnCompletion.add(scheduledPass);
|
||||
scheduledPass.myRunningPredecessorsCount.incrementAndGet();
|
||||
predecessor.addSuccessorOnCompletion(scheduledPass);
|
||||
}
|
||||
}
|
||||
for (int predecessorId : pass.getStartingPredecessorIds()) {
|
||||
ScheduledPass predecessor = findOrCreatePredecessorPass(fileEditors, document, toBeSubmitted, textEditorHighlightingPasses, freePasses,
|
||||
dependentPasses, updateProgress, threadsToStartCountdown, jobPriority, predecessorId);
|
||||
ScheduledPass predecessor = findOrCreatePredecessorPass(fileEditor, toBeSubmitted, textEditorHighlightingPasses, freePasses,
|
||||
dependentPasses, updateProgress, threadsToStartCountdown,
|
||||
predecessorId);
|
||||
if (predecessor != null) {
|
||||
predecessor.mySuccessorsOnSubmit.add(scheduledPass);
|
||||
scheduledPass.myRunningPredecessorsCount.incrementAndGet();
|
||||
predecessor.addSuccessorOnSubmit(scheduledPass);
|
||||
}
|
||||
}
|
||||
if (scheduledPass.myRunningPredecessorsCount.get() == 0 && !freePasses.contains(scheduledPass)) {
|
||||
@@ -259,43 +298,49 @@ public abstract class PassExecutorService implements Disposable {
|
||||
else if (!dependentPasses.contains(scheduledPass)) {
|
||||
dependentPasses.add(scheduledPass);
|
||||
}
|
||||
|
||||
if (pass.isRunIntentionPassAfter() && fileEditor instanceof TextEditor) {
|
||||
Editor editor = ((TextEditor)fileEditor).getEditor();
|
||||
ShowIntentionsPass ip = new ShowIntentionsPass(myProject, editor, -1);
|
||||
ip.setId(nextPassId.incrementAndGet());
|
||||
ip.setCompletionPredecessorIds(new int[]{scheduledPass.myPass.getId()});
|
||||
|
||||
createScheduledPass(fileEditor, ip, toBeSubmitted, textEditorHighlightingPasses, freePasses, dependentPasses, updateProgress, threadsToStartCountdown);
|
||||
}
|
||||
|
||||
return scheduledPass;
|
||||
}
|
||||
|
||||
private ScheduledPass findOrCreatePredecessorPass(@NotNull List<FileEditor> fileEditors,
|
||||
Document document,
|
||||
@NotNull Map<Pair<Document, Integer>, ScheduledPass> toBeSubmitted,
|
||||
private ScheduledPass findOrCreatePredecessorPass(@NotNull FileEditor fileEditor,
|
||||
@NotNull Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted,
|
||||
@NotNull List<TextEditorHighlightingPass> textEditorHighlightingPasses,
|
||||
@NotNull List<ScheduledPass> freePasses,
|
||||
@NotNull List<ScheduledPass> dependentPasses,
|
||||
@NotNull DaemonProgressIndicator updateProgress,
|
||||
@NotNull AtomicInteger myThreadsToStartCountdown,
|
||||
final int jobPriority,
|
||||
final int predecessorId) {
|
||||
Pair<Document, Integer> predKey = Pair.create(document, predecessorId);
|
||||
Pair<FileEditor, Integer> predKey = Pair.create(fileEditor, predecessorId);
|
||||
ScheduledPass predecessor = toBeSubmitted.get(predKey);
|
||||
if (predecessor == null) {
|
||||
TextEditorHighlightingPass textEditorPass = findPassById(predecessorId, textEditorHighlightingPasses);
|
||||
predecessor = textEditorPass == null ? null : createScheduledPass(fileEditors, textEditorPass, toBeSubmitted, textEditorHighlightingPasses, freePasses,
|
||||
dependentPasses, updateProgress, myThreadsToStartCountdown, jobPriority);
|
||||
predecessor = textEditorPass == null ? null : createScheduledPass(fileEditor, textEditorPass, toBeSubmitted, textEditorHighlightingPasses, freePasses,
|
||||
dependentPasses, updateProgress, myThreadsToStartCountdown);
|
||||
}
|
||||
return predecessor;
|
||||
}
|
||||
|
||||
private static TextEditorHighlightingPass findPassById(final int id, @NotNull List<TextEditorHighlightingPass> textEditorHighlightingPasses) {
|
||||
TextEditorHighlightingPass textEditorPass = null;
|
||||
for (TextEditorHighlightingPass found : textEditorHighlightingPasses) {
|
||||
if (found.getId() == id) {
|
||||
textEditorPass = found;
|
||||
break;
|
||||
return ContainerUtil.find(textEditorHighlightingPasses, new Condition<TextEditorHighlightingPass>() {
|
||||
@Override
|
||||
public boolean value(TextEditorHighlightingPass pass) {
|
||||
return pass.getId() == id;
|
||||
}
|
||||
}
|
||||
return textEditorPass;
|
||||
});
|
||||
}
|
||||
|
||||
private void submit(@NotNull ScheduledPass pass) {
|
||||
if (!pass.myUpdateProgress.isCanceled()) {
|
||||
Job<Void> job = JobLauncher.getInstance().submitToJobThread(pass.myJobPriority, pass, new Consumer<Future>() {
|
||||
Job<Void> job = JobLauncher.getInstance().submitToJobThread(Job.DEFAULT_PRIORITY, pass, new Consumer<Future>() {
|
||||
@Override
|
||||
public void consume(Future future) {
|
||||
try {
|
||||
@@ -317,25 +362,21 @@ public abstract class PassExecutorService implements Disposable {
|
||||
}
|
||||
|
||||
private class ScheduledPass implements Runnable {
|
||||
private final List<FileEditor> myFileEditors;
|
||||
private final FileEditor myFileEditor;
|
||||
private final TextEditorHighlightingPass myPass;
|
||||
private final AtomicInteger myThreadsToStartCountdown;
|
||||
private final int myJobPriority;
|
||||
private final AtomicInteger myRunningPredecessorsCount;
|
||||
private final AtomicInteger myRunningPredecessorsCount = new AtomicInteger(0);
|
||||
private final Collection<ScheduledPass> mySuccessorsOnCompletion = new ArrayList<ScheduledPass>();
|
||||
private final Collection<ScheduledPass> mySuccessorsOnSubmit = new ArrayList<ScheduledPass>();
|
||||
private final DaemonProgressIndicator myUpdateProgress;
|
||||
|
||||
private ScheduledPass(@NotNull List<FileEditor> fileEditors,
|
||||
private ScheduledPass(@NotNull FileEditor fileEditor,
|
||||
@NotNull TextEditorHighlightingPass pass,
|
||||
@NotNull DaemonProgressIndicator progressIndicator,
|
||||
@NotNull AtomicInteger threadsToStartCountdown,
|
||||
int jobPriority) {
|
||||
myFileEditors = fileEditors;
|
||||
@NotNull AtomicInteger threadsToStartCountdown) {
|
||||
myFileEditor = fileEditor;
|
||||
myPass = pass;
|
||||
myThreadsToStartCountdown = threadsToStartCountdown;
|
||||
myJobPriority = jobPriority;
|
||||
myRunningPredecessorsCount = new AtomicInteger(0);
|
||||
myUpdateProgress = progressIndicator;
|
||||
}
|
||||
|
||||
@@ -410,7 +451,7 @@ public abstract class PassExecutorService implements Disposable {
|
||||
log(myUpdateProgress, myPass, "Finished. ");
|
||||
|
||||
if (!myUpdateProgress.isCanceled()) {
|
||||
applyInformationToEditorsLater(myFileEditors, myPass, myUpdateProgress, myThreadsToStartCountdown);
|
||||
applyInformationToEditorsLater(myFileEditor, myPass, myUpdateProgress, myThreadsToStartCountdown);
|
||||
for (ScheduledPass successor : mySuccessorsOnCompletion) {
|
||||
int predecessorsToRun = successor.myRunningPredecessorsCount.decrementAndGet();
|
||||
if (predecessorsToRun == 0) {
|
||||
@@ -425,75 +466,57 @@ public abstract class PassExecutorService implements Disposable {
|
||||
public String toString() {
|
||||
return "SP: " + myPass;
|
||||
}
|
||||
|
||||
private void addSuccessorOnCompletion(@NotNull ScheduledPass successor) {
|
||||
mySuccessorsOnCompletion.add(successor);
|
||||
successor.myRunningPredecessorsCount.incrementAndGet();
|
||||
}
|
||||
|
||||
private void addSuccessorOnSubmit(@NotNull ScheduledPass successor) {
|
||||
mySuccessorsOnSubmit.add(successor);
|
||||
successor.myRunningPredecessorsCount.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyInformationToEditorsLater(@NotNull final List<FileEditor> fileEditors,
|
||||
private void applyInformationToEditorsLater(@NotNull final FileEditor fileEditor,
|
||||
@NotNull final TextEditorHighlightingPass pass,
|
||||
@NotNull final DaemonProgressIndicator updateProgress,
|
||||
@NotNull final AtomicInteger threadsToStartCountdown) {
|
||||
final boolean testMode = ApplicationManager.getApplication().isUnitTestMode();
|
||||
ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doApplyInformationToEditors(updateProgress, pass, fileEditors, threadsToStartCountdown, testMode);
|
||||
}
|
||||
}, ModalityState.stateForComponent(fileEditors.get(0).getComponent()));
|
||||
}
|
||||
|
||||
private void doApplyInformationToEditors(@NotNull DaemonProgressIndicator updateProgress,
|
||||
@NotNull TextEditorHighlightingPass pass,
|
||||
@NotNull List<FileEditor> fileEditors,
|
||||
@NotNull AtomicInteger threadsToStartCountdown,
|
||||
boolean testMode) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
if (isDisposed() || myProject.isDisposed()) {
|
||||
updateProgress.cancel();
|
||||
}
|
||||
if (updateProgress.isCanceled()) {
|
||||
log(updateProgress, pass, " is canceled during apply, sorry");
|
||||
return;
|
||||
}
|
||||
boolean applied = false;
|
||||
for (final FileEditor fileEditor : fileEditors) {
|
||||
LOG.assertTrue(fileEditor != null);
|
||||
try {
|
||||
if (testMode || fileEditor.getComponent().isDisplayable()) {
|
||||
if (!applied) {
|
||||
applied = true;
|
||||
if (isDisposed() || myProject.isDisposed()) {
|
||||
updateProgress.cancel();
|
||||
}
|
||||
if (updateProgress.isCanceled()) {
|
||||
log(updateProgress, pass, " is canceled during apply, sorry");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (fileEditor.getComponent().isDisplayable() || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
log(updateProgress, pass, " Applied");
|
||||
pass.applyInformationToEditor();
|
||||
}
|
||||
afterApplyInformationToEditor(pass, fileEditor, updateProgress);
|
||||
|
||||
if (pass.isRunIntentionPassAfter() && fileEditor instanceof TextEditor && !updateProgress.isCanceled()) {
|
||||
Editor editor = ((TextEditor)fileEditor).getEditor();
|
||||
ShowIntentionsPass ip = new ShowIntentionsPass(myProject, editor, -1);
|
||||
ip.setId(nextPassId.incrementAndGet());
|
||||
threadsToStartCountdown.incrementAndGet();
|
||||
submit(new ScheduledPass(fileEditors, ip, updateProgress, threadsToStartCountdown, Job.DEFAULT_PRIORITY));
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
log(updateProgress, pass, "Error " + e);
|
||||
throw e;
|
||||
}
|
||||
if (threadsToStartCountdown.decrementAndGet() == 0) {
|
||||
log(updateProgress, pass, "Stopping ");
|
||||
updateProgress.stopIfRunning();
|
||||
}
|
||||
else {
|
||||
log(updateProgress, pass, "Finished but there are passes in the queue: " + threadsToStartCountdown.get());
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
log(updateProgress, pass, "Error " + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (threadsToStartCountdown.decrementAndGet() == 0) {
|
||||
log(updateProgress, pass, "Stopping ");
|
||||
updateProgress.stopIfRunning();
|
||||
}
|
||||
else {
|
||||
log(updateProgress, pass, "Finished but there are passes in the queue: "+threadsToStartCountdown.get());
|
||||
}
|
||||
}, ModalityState.stateForComponent(fileEditor.getComponent()));
|
||||
}
|
||||
|
||||
protected boolean isDisposed() {
|
||||
return isDisposed;
|
||||
}
|
||||
|
||||
protected abstract void afterApplyInformationToEditor(TextEditorHighlightingPass pass, FileEditor fileEditor, ProgressIndicator updateProgress);
|
||||
|
||||
@NotNull
|
||||
public List<TextEditorHighlightingPass> getAllSubmittedPasses() {
|
||||
List<TextEditorHighlightingPass> result = new ArrayList<TextEditorHighlightingPass>(mySubmittedPasses.size());
|
||||
@@ -502,13 +525,17 @@ public abstract class PassExecutorService implements Disposable {
|
||||
result.add(scheduledPass.myPass);
|
||||
}
|
||||
}
|
||||
sortById(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void sortById(@NotNull List<TextEditorHighlightingPass> result) {
|
||||
ContainerUtil.quickSort(result, new Comparator<TextEditorHighlightingPass>() {
|
||||
@Override
|
||||
public int compare(TextEditorHighlightingPass o1, TextEditorHighlightingPass o2) {
|
||||
return o1.getId() - o2.getId();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final ConcurrentHashMap<Thread, Integer> threads = new ConcurrentHashMap<Thread, Integer>();
|
||||
@@ -537,11 +564,11 @@ public abstract class PassExecutorService implements Disposable {
|
||||
}
|
||||
|
||||
private static final Key<Throwable> THROWABLE_KEY = Key.create("THROWABLE_KEY");
|
||||
private static void saveException(Throwable e, DaemonProgressIndicator indicator) {
|
||||
private static void saveException(@NotNull Throwable e, @NotNull DaemonProgressIndicator indicator) {
|
||||
indicator.putUserDataIfAbsent(THROWABLE_KEY, e);
|
||||
}
|
||||
@TestOnly
|
||||
public static Throwable getSavedException(DaemonProgressIndicator indicator) {
|
||||
public static Throwable getSavedException(@NotNull DaemonProgressIndicator indicator) {
|
||||
return indicator.getUserData(THROWABLE_KEY);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user