diff --git a/annotations/com/intellij/openapi/util/annotations.xml b/annotations/com/intellij/openapi/util/annotations.xml new file mode 100644 index 00000000000..b11815b34e4 --- /dev/null +++ b/annotations/com/intellij/openapi/util/annotations.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/annotations/com/intellij/ui/annotations.xml b/annotations/com/intellij/ui/annotations.xml new file mode 100644 index 00000000000..b911bc06f23 --- /dev/null +++ b/annotations/com/intellij/ui/annotations.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index b0a0704f61c..26460c45679 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -315,6 +315,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/highlighter/ErrorDuringFileAnalyzeNotificationProvider.kt b/idea/src/org/jetbrains/jet/plugin/highlighter/ErrorDuringFileAnalyzeNotificationProvider.kt new file mode 100644 index 00000000000..e30c84f475f --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/highlighter/ErrorDuringFileAnalyzeNotificationProvider.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2013 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 org.jetbrains.jet.plugin.highlighter + +import com.intellij.ui.EditorNotifications +import com.intellij.ui.EditorNotificationPanel +import com.intellij.openapi.util.Key +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.fileEditor.FileEditor +import org.jetbrains.jet.plugin.JetFileType +import com.intellij.psi.PsiManager +import com.intellij.openapi.project.Project +import org.jetbrains.jet.lang.psi.JetFile +import com.intellij.icons.AllIcons +import com.intellij.ide.actions.ActivateToolWindowAction +import com.intellij.notification.EventLog + +private val ERROR_HIGHLIGHT_PANEL_KEY = Key.create("kotlin.error.highlight.panel.key") +private val HAS_ERRORS_IN_HIGHLIHTING_KEY = Key.create("kotlin.has.errors.in.higlighting.key") + +fun updateHighlightingResult(file: JetFile, hasErrors: Boolean) { + if (hasErrors != file.getUserData(HAS_ERRORS_IN_HIGHLIHTING_KEY)) { + file.putUserData(HAS_ERRORS_IN_HIGHLIHTING_KEY, hasErrors) + + val vFile = file.getVirtualFile() + if (vFile != null) { + EditorNotifications.getInstance(file.getProject())?.updateNotifications(vFile) + } + } +} + +public class ErrorDuringFileAnalyzeNotificationProvider(val project: Project) : EditorNotifications.Provider() { + private class HighlightingErrorNotificationPanel : EditorNotificationPanel() { + { + setText("Kotlin internal error occurred in this file. Highlighting may be inadequate.") + myLabel.setIcon(AllIcons.General.Error) + createActionLabel("Open Event Log", ActivateToolWindowAction.getActionIdForToolWindow(EventLog.LOG_TOOL_WINDOW_ID)) + } + } + + override fun getKey() = ERROR_HIGHLIGHT_PANEL_KEY + + override fun createNotificationPanel(file: VirtualFile, fileEditor: FileEditor?): EditorNotificationPanel? { + if (file.getFileType() != JetFileType.INSTANCE) { + return null + } + + val psiFile = PsiManager.getInstance(project).findFile(file) ?: return null + + if (psiFile !is JetFile) { + return null + } + + val hasErrors = psiFile.getUserData(HAS_ERRORS_IN_HIGHLIHTING_KEY) ?: false + + return if (hasErrors) HighlightingErrorNotificationPanel() else null + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/highlighter/JetPsiChecker.java b/idea/src/org/jetbrains/jet/plugin/highlighter/JetPsiChecker.java index dcfc13246cf..027a7d92ba3 100644 --- a/idea/src/org/jetbrains/jet/plugin/highlighter/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/plugin/highlighter/JetPsiChecker.java @@ -16,7 +16,6 @@ package org.jetbrains.jet.plugin.highlighter; -import com.google.common.collect.Sets; import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension; import com.intellij.codeInsight.intention.EmptyIntentionAction; import com.intellij.codeInsight.intention.IntentionAction; @@ -25,11 +24,14 @@ import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.*; -import com.intellij.util.containers.MultiMap; +import com.intellij.psi.MultiRangeReference; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,15 +51,10 @@ import org.jetbrains.jet.plugin.quickfix.QuickFixes; import java.util.Collection; import java.util.List; -import java.util.Set; public class JetPsiChecker implements Annotator, HighlightRangeExtension { - private static final Logger LOG = Logger.getInstance(JetPsiChecker.class); - private static boolean isNamesHighlightingEnabled = true; - private HighlightingPassCache passCache = null; - @TestOnly public static void setNamesHighlightingEnabled(boolean namesHighlightingEnabled) { isNamesHighlightingEnabled = namesHighlightingEnabled; @@ -104,36 +101,41 @@ public class JetPsiChecker implements Annotator, HighlightRangeExtension { JetFile file = (JetFile) element.getContainingFile(); - if (passCache == null || passCache.isOutdated(file)) { - passCache = new HighlightingPassCache(AnalyzerFacadeWithCache.analyzeFileWithCache(file), file); + AnalyzeExhaust analyzeExhaust = AnalyzerFacadeWithCache.analyzeFileWithCache(file); + if (analyzeExhaust.isError()) { + //noinspection StaticMethodReferencedViaSubclass + HighlighterPackage.updateHighlightingResult(file, true); + + // Force stop highlighting annotate cycle + throw new ProcessCanceledException(analyzeExhaust.getError()); } - if (!passCache.analyzeExhaust.isError()) { - BindingContext bindingContext = passCache.analyzeExhaust.getBindingContext(); - for (HighlightingVisitor visitor : getAfterAnalysisVisitor(holder, bindingContext)) { - element.accept(visitor); - } + BindingContext bindingContext = analyzeExhaust.getBindingContext(); + for (HighlightingVisitor visitor : getAfterAnalysisVisitor(holder, bindingContext)) { + element.accept(visitor); + } - if (JetPluginUtil.isInSource(element, /* includeLibrarySources = */ false)) { - for (Diagnostic diagnostic : passCache.elementToDiagnostic.get(element)) { - registerDiagnosticAnnotations(diagnostic, passCache.redeclarations, holder); - } + if (JetPluginUtil.isInSource(element, /* includeLibrarySources = */ false)) { + Ref isMarkedWithRedeclaration = Ref.create(false); + for (Diagnostic diagnostic : bindingContext.getDiagnostics().forElement(element)) { + registerDiagnosticAnnotations(element, diagnostic, holder, isMarkedWithRedeclaration); } } - else if (element instanceof JetFile) { - Throwable error = passCache.analyzeExhaust.getError(); - if (JetPluginUtil.isInSource(element, /* includeLibrarySources = */ false)) { - holder.createErrorAnnotation(file, error.getClass().getCanonicalName() + ": " + error.getMessage()); - } - LOG.error(error); + if (element instanceof JetFile) { + //noinspection StaticMethodReferencedViaSubclass + HighlighterPackage.updateHighlightingResult(file, false); } } - private static void registerDiagnosticAnnotations(@NotNull Diagnostic diagnostic, - @NotNull Set redeclarations, - @NotNull AnnotationHolder holder) { + private static void registerDiagnosticAnnotations( + @NotNull PsiElement element, @NotNull Diagnostic diagnostic, + @NotNull AnnotationHolder holder, Ref isMarkedWithRedeclaration + ) { if (!diagnostic.isValid()) return; + + assert diagnostic.getPsiElement() == element; + List textRanges = diagnostic.getTextRanges(); if (diagnostic.getSeverity() == Severity.ERROR) { if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) { @@ -144,9 +146,7 @@ public class JetPsiChecker implements Annotator, HighlightRangeExtension { for (TextRange range : mrr.getRanges()) { Annotation annotation = holder.createErrorAnnotation(range.shiftRight(referenceExpression.getTextOffset()), getDefaultMessage(diagnostic)); annotation.setTooltip(getMessage(diagnostic)); - registerQuickFix(annotation, diagnostic); - annotation.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); } } @@ -171,8 +171,10 @@ public class JetPsiChecker implements Annotator, HighlightRangeExtension { return; } - if (Errors.REDECLARATION_DIAGNOSTICS.contains(diagnostic.getFactory())) { - registerQuickFix(markRedeclaration(redeclarations, diagnostic, holder), diagnostic); + if (!isMarkedWithRedeclaration.get() && Errors.REDECLARATION_DIAGNOSTICS.contains(diagnostic.getFactory())) { + isMarkedWithRedeclaration.set(true); + Annotation annotation = holder.createErrorAnnotation(diagnostic.getTextRanges().get(0), ""); + annotation.setTooltip(getMessage(diagnostic)); return; } @@ -263,53 +265,8 @@ public class JetPsiChecker implements Annotator, HighlightRangeExtension { return message; } - @Nullable - private static Annotation markRedeclaration(@NotNull Set redeclarations, - @NotNull Diagnostic redeclarationDiagnostic, - @NotNull AnnotationHolder holder) { - if (!redeclarations.add(redeclarationDiagnostic.getPsiElement())) return null; - List textRanges = redeclarationDiagnostic.getTextRanges(); - if (!redeclarationDiagnostic.isValid()) return null; - Annotation annotation = holder.createErrorAnnotation(textRanges.get(0), ""); - annotation.setTooltip(getMessage(redeclarationDiagnostic)); - return annotation; - } - @Override public boolean isForceHighlightParents(@NotNull PsiFile file) { return file instanceof JetFile; } - - private static class HighlightingPassCache { - private final AnalyzeExhaust analyzeExhaust; - private final MultiMap elementToDiagnostic; - - private final Set redeclarations = Sets.newHashSet(); - private final JetFile jetFile; - private final long modificationCount; - - public HighlightingPassCache(AnalyzeExhaust analyzeExhaust, JetFile jetFile) { - this.analyzeExhaust = analyzeExhaust; - this.jetFile = jetFile; - this.elementToDiagnostic = buildElementToDiagnosticCache(analyzeExhaust, jetFile); - this.modificationCount = PsiManager.getInstance(jetFile.getProject()).getModificationTracker().getModificationCount(); - } - - public boolean isOutdated(JetFile jetFile) { - return this.jetFile != jetFile || PsiManager.getInstance(jetFile.getProject()).getModificationTracker().getModificationCount() != modificationCount; - } - - private static MultiMap buildElementToDiagnosticCache(AnalyzeExhaust analyzeExhaust, JetFile jetFile) { - MultiMap elementToDiagnostic = MultiMap.create(); - Collection diagnostics = Sets.newLinkedHashSet(analyzeExhaust.getBindingContext().getDiagnostics()); - - for (Diagnostic diagnostic : diagnostics) { - if (diagnostic.getPsiFile() == jetFile) { - elementToDiagnostic.putValue(diagnostic.getPsiElement(), diagnostic); - } - } - - return elementToDiagnostic; - } - } }