blob: 73113a4be3dae15f7d056268ce04be9a4df4d226 [file] [log] [blame]
/*
* 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.codeInsight.daemon.impl;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingLevelManager;
import com.intellij.lang.ExternalLanguageAnnotators;
import com.intellij.lang.Language;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationSession;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author ven
*/
public class ExternalToolPass extends TextEditorHighlightingPass {
private final PsiFile myFile;
private final int myStartOffset;
private final int myEndOffset;
private final AnnotationHolderImpl myAnnotationHolder;
private final Editor myEditor;
private final Map<ExternalAnnotator, MyData> myAnnotator2DataMap = new HashMap<ExternalAnnotator, MyData>();
private final ExternalToolPassFactory myExternalToolPassFactory;
private static class MyData {
private final PsiFile myPsiRoot;
private final Object myCollectedInfo;
private volatile Object myAnnotationResult;
private MyData(@NotNull PsiFile psiRoot, @NotNull Object collectedInfo) {
myPsiRoot = psiRoot;
myCollectedInfo = collectedInfo;
}
}
public ExternalToolPass(@NotNull ExternalToolPassFactory externalToolPassFactory,
@NotNull PsiFile file,
@NotNull Editor editor,
int startOffset,
int endOffset) {
super(file.getProject(), editor.getDocument(), false);
myEditor = editor;
myFile = file;
myStartOffset = startOffset;
myEndOffset = endOffset;
myAnnotationHolder = new AnnotationHolderImpl(new AnnotationSession(file));
myExternalToolPassFactory = externalToolPassFactory;
}
@Override
public void doCollectInformation(@NotNull ProgressIndicator progress) {
final FileViewProvider viewProvider = myFile.getViewProvider();
final Set<Language> relevantLanguages = viewProvider.getLanguages();
for (Language language : relevantLanguages) {
PsiFile psiRoot = viewProvider.getPsi(language);
if (!HighlightingLevelManager.getInstance(myProject).shouldInspect(psiRoot)) continue;
final List<ExternalAnnotator> externalAnnotators = ExternalLanguageAnnotators.allForFile(language, psiRoot);
if (!externalAnnotators.isEmpty()) {
DaemonCodeAnalyzerEx daemonCodeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
boolean errorFound = daemonCodeAnalyzer.getFileStatusMap().wasErrorFound(myDocument);
for(ExternalAnnotator externalAnnotator: externalAnnotators) {
final Object collectedInfo = externalAnnotator.collectInformation(psiRoot, myEditor, errorFound);
if (collectedInfo != null) {
myAnnotator2DataMap.put(externalAnnotator, new MyData(psiRoot, collectedInfo));
}
}
}
}
}
@Override
public void doApplyInformationToEditor() {
DaemonCodeAnalyzerEx daemonCodeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
daemonCodeAnalyzer.getFileStatusMap().markFileUpToDate(myDocument, getId());
final long modificationStampBefore = myDocument.getModificationStamp();
Update update = new Update(myFile) {
@Override
public void setRejected() {
super.setRejected();
doFinish(Collections.<HighlightInfo>emptyList());
}
@Override
public void run() {
if (documentChanged(modificationStampBefore) || myProject.isDisposed()) {
doFinish(Collections.<HighlightInfo>emptyList());
return;
}
doAnnotate();
if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(new Runnable() {
@Override
public void run() {
if (documentChanged(modificationStampBefore) || myProject.isDisposed()) {
doFinish(Collections.<HighlightInfo>emptyList());
return;
}
collectHighlighters();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
List<HighlightInfo> highlights =
documentChanged(modificationStampBefore) || myProject.isDisposed() ? Collections.<HighlightInfo>emptyList() : getHighlights();
doFinish(highlights);
}
}, ModalityState.stateForComponent(myEditor.getComponent()));
}
})) {
doFinish(Collections.<HighlightInfo>emptyList());
}
}
};
myExternalToolPassFactory.scheduleExternalActivity(update);
}
private boolean documentChanged(long modificationStampBefore) {
return myDocument.getModificationStamp() != modificationStampBefore;
}
@NotNull
private List<HighlightInfo> getHighlights() {
List<HighlightInfo> infos = new ArrayList<HighlightInfo>();
for (Annotation annotation : myAnnotationHolder) {
infos.add(HighlightInfo.fromAnnotation(annotation));
}
return infos;
}
private void collectHighlighters() {
for (ExternalAnnotator annotator : myAnnotator2DataMap.keySet()) {
final MyData data = myAnnotator2DataMap.get(annotator);
if (data != null) {
annotator.apply(data.myPsiRoot, data.myAnnotationResult, myAnnotationHolder);
}
}
}
private void doFinish(@NotNull final List<HighlightInfo> highlights) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (!myProject.isDisposed()) {
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myStartOffset, myEndOffset, highlights, getColorsScheme(), getId());
}
}
});
}
private void doAnnotate() {
for (ExternalAnnotator annotator : myAnnotator2DataMap.keySet()) {
final MyData data = myAnnotator2DataMap.get(annotator);
if (data != null) {
data.myAnnotationResult = annotator.doAnnotate(data.myCollectedInfo);
}
}
}
}