Files
openide/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java
T
Vladimir Krivosheev 8753c5d96a simplify — introduce forceCloseProject (test only)
Since Kotlin is not used, have to introduce such method to avoid (false, true, false, true, guessWhatIDo)
2017-05-15 16:09:08 +02:00

2716 lines
108 KiB
Java

/*
* Copyright 2000-2017 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.application.options.editor.CodeFoldingConfigurable;
import com.intellij.codeHighlighting.*;
import com.intellij.codeInsight.EditorInfo;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.daemon.*;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
import com.intellij.codeInsight.folding.CodeFoldingManager;
import com.intellij.codeInsight.folding.JavaCodeFoldingSettings;
import com.intellij.codeInsight.hint.EditorHintListener;
import com.intellij.codeInsight.intention.AbstractIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspectionBase;
import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection;
import com.intellij.configurationStore.StorageUtilKt;
import com.intellij.diagnostic.PerformanceWatcher;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.ide.GeneralSettings;
import com.intellij.ide.SaveAndSyncHandlerImpl;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.javaee.ExternalResourceManagerExImpl;
import com.intellij.lang.*;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.actionSystem.TypedAction;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.impl.DocumentMarkupModel;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
import com.intellij.openapi.editor.markup.MarkupModel;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.impl.CoreProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.project.impl.ProjectManagerImpl;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.refactoring.inline.InlineRefactoringActionHandler;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.testFramework.*;
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
import com.intellij.ui.HintListener;
import com.intellij.ui.LightweightHint;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.CheckDtdReferencesInspection;
import gnu.trove.THashSet;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* @author cdr
*/
@SuppressWarnings("StringConcatenationInsideStringBufferAppend")
@SkipSlowTestLocally
public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/typing/";
private DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;
@Override
protected void setUp() throws Exception {
super.setUp();
enableInspectionTool(new UnusedDeclarationInspection());
myDaemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
UndoManager.getInstance(myProject);
myDaemonCodeAnalyzer.setUpdateByTimerEnabled(true);
DaemonProgressIndicator.setDebug(true);
}
@Override
protected void tearDown() throws Exception {
try {
Project project = getProject();
if (project != null) {
doPostponedFormatting(project);
((ProjectManagerImpl)ProjectManagerEx.getInstanceEx()).forceCloseProject(project, false);
}
}
finally {
myDaemonCodeAnalyzer = null;
super.tearDown();
}
}
@Override
protected boolean doTestLineMarkers() {
return true;
}
@Override
protected void setUpProject() throws Exception {
super.setUpProject();
ProjectManagerEx.getInstanceEx().openProject(getProject());
super.runStartupActivities();
UIUtil.dispatchAllInvocationEvents(); // startup activities
}
@Override
protected void runStartupActivities() {
}
private static void typeInAlienEditor(Editor alienEditor, char c) {
TypedAction action = EditorActionManager.getInstance().getTypedAction();
DataContext dataContext = ((EditorEx)alienEditor).getDataContext();
action.actionPerformed(alienEditor, c, dataContext);
}
public void testHighlightersUpdate() throws Exception {
configureByFile(BASE_PATH + "HighlightersUpdate.java");
Document document = getDocument(getFile());
highlightErrors();
List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.ERROR, getProject());
assertEquals(1, errors.size());
TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
assertNull(dirty);
type(' ');
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
assertNotNull(dirty);
}
public void testNoPsiEventsAltogether() throws Exception {
configureByFile(BASE_PATH + "HighlightersUpdate.java");
Document document = getDocument(getFile());
highlightErrors();
type(' ');
backspace();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
assertEquals(getFile().getTextRange(), dirty); // have to rehighlight whole file in case no PSI events have come
}
public void testRenameClass() throws Exception {
configureByFile(BASE_PATH + "AClass.java");
Document document = getDocument(getFile());
Collection<HighlightInfo> infos = highlightErrors();
assertEquals(0, infos.size());
final PsiClass psiClass = ((PsiJavaFile)getFile()).getClasses()[0];
new RenameProcessor(myProject, psiClass, "Class2", false, false).run();
TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
assertEquals(getFile().getTextRange(), dirty);
highlightErrors();
assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
}
public void testTypingSpace() throws Exception {
configureByFile(BASE_PATH + "AClass.java");
Document document = getDocument(getFile());
Collection<HighlightInfo> infos = highlightErrors();
assertEquals(0, infos.size());
type(" ");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
PsiElement elementAtCaret = myFile.findElementAt(myEditor.getCaretModel().getOffset());
assertTrue(elementAtCaret instanceof PsiWhiteSpace);
TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
assertEquals(elementAtCaret.getTextRange(), dirty);
highlightErrors();
assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
}
public void testTypingSpaceInsideError() throws Exception {
configureByFile(BASE_PATH + "Error.java");
Collection<HighlightInfo> infos = highlightErrors();
assertEquals(1, infos.size());
for (int i = 0; i < 100; i++) {
type(" ");
List<HighlightInfo> errors = highlightErrors();
assertEquals(1, errors.size());
}
}
public void testBackSpaceInsideError() throws Exception {
configureByFile(BASE_PATH + "BackError.java");
Collection<HighlightInfo> infos = highlightErrors();
assertEquals(1, infos.size());
backspace();
List<HighlightInfo> errors = highlightErrors();
assertEquals(1, errors.size());
}
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
if (isStressTest() && !getTestName(false).equals("TypingCurliesClearsEndOfFileErrorsInPhp_ItIsPerformanceTestBecauseItRunsTooLong")) {
// all possible inspections
List<InspectionToolWrapper> all = InspectionToolRegistrar.getInstance().createTools();
List<LocalInspectionTool> locals = new ArrayList<>();
all.stream().filter(tool -> tool instanceof LocalInspectionToolWrapper).forEach(tool -> {
LocalInspectionTool e = ((LocalInspectionToolWrapper)tool).getTool();
locals.add(e);
});
return locals.toArray(new LocalInspectionTool[locals.size()]);
}
return new LocalInspectionTool[]{
new FieldCanBeLocalInspection(),
new RequiredAttributesInspectionBase(),
new CheckDtdReferencesInspection(),
new AccessStaticViaInstance(),
};
}
public void testUnusedFieldUpdate() throws Exception {
configureByFile(BASE_PATH + "UnusedField.java");
Document document = getDocument(getFile());
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEquals(1, infos.size());
assertEquals("Private field 'ffff' is never used", infos.get(0).getDescription());
type(" foo(ffff++);");
highlightErrors();
List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.WARNING, getProject());
assertEquals(0, errors.size());
}
public void testUnusedMethodUpdate() throws Exception {
configureByText(JavaFileType.INSTANCE, "class X {\n" +
" static void ffff() {}\n" +
" public static void main(String[] args){\n" +
" for (int i=0; i<1000;i++) {\n" +
" System.out.println(i);\n" +
" <caret>ffff();\n" +
" }\n" +
" }\n}");
enableInspectionTool(new UnusedDeclarationInspection(true));
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
PlatformTestUtil.invokeNamedAction(IdeActions.ACTION_COMMENT_LINE);
infos = doHighlighting(HighlightSeverity.WARNING);
assertEquals(1, infos.size());
assertEquals("Method 'ffff()' is never used", infos.get(0).getDescription());
}
public void testAssignedButUnreadFieldUpdate() throws Exception {
configureByFile(BASE_PATH + "AssignedButUnreadField.java");
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEquals(1, infos.size());
assertEquals("Private field 'text' is assigned but never accessed", infos.get(0).getDescription());
ctrlW();
WriteCommandAction.runWriteCommandAction(getProject(), () -> EditorModificationUtil.deleteSelectedText(getEditor()));
type(" text");
List<HighlightInfo> errors = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(getFile().getText(), errors);
}
public void testDaemonIgnoresNonPhysicalEditor() throws Exception {
configureByFile(BASE_PATH + "AClass.java");
highlightErrors();
EditorFactory editorFactory = EditorFactory.getInstance();
final Document consoleDoc = editorFactory.createDocument("my console blah");
final Editor consoleEditor = editorFactory.createEditor(consoleDoc);
try {
checkDaemonReaction(false, () -> caretRight(consoleEditor));
checkDaemonReaction(true, () -> typeInAlienEditor(consoleEditor, 'x'));
checkDaemonReaction(true, () -> LightPlatformCodeInsightTestCase.backspace(consoleEditor, getProject()));
//real editor
checkDaemonReaction(true, this::caretRight);
}
finally {
editorFactory.releaseEditor(consoleEditor);
}
}
public void testDaemonIgnoresConsoleActivities() throws Exception {
configureByFile(BASE_PATH + "AClass.java");
doHighlighting(HighlightSeverity.WARNING);
final ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(getProject()).getConsole();
consoleView.getComponent(); //create editor
consoleView.print("haha", ConsoleViewContentType.NORMAL_OUTPUT);
UIUtil.dispatchAllInvocationEvents();
try {
checkDaemonReaction(false, () -> {
consoleView.clear();
try {
Thread.sleep(300); // *&^ing alarm
}
catch (InterruptedException e) {
LOG.error(e);
}
UIUtil.dispatchAllInvocationEvents(); //flush
});
checkDaemonReaction(false, () -> {
consoleView.print("sss", ConsoleViewContentType.NORMAL_OUTPUT);
try {
Thread.sleep(300); // *&^ing alarm
}
catch (InterruptedException e) {
LOG.error(e);
}
UIUtil.dispatchAllInvocationEvents(); //flush
});
checkDaemonReaction(false, () -> {
consoleView.setOutputPaused(true);
try {
Thread.sleep(300); // *&^ing alarm
}
catch (InterruptedException e) {
LOG.error(e);
}
UIUtil.dispatchAllInvocationEvents(); //flush
});
}
finally {
Disposer.dispose(consoleView);
}
}
private void checkDaemonReaction(boolean mustCancelItself, @NotNull final Runnable action) {
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
highlightErrors();
myDaemonCodeAnalyzer.waitForTermination();
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
final AtomicBoolean run = new AtomicBoolean();
Disposable disposable = Disposer.newDisposable();
final AtomicReference<RuntimeException> stopDaemonReason = new AtomicReference<>();
StorageUtilKt.setDEBUG_LOG("");
getProject().getMessageBus().connect(disposable).subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC,
new DaemonCodeAnalyzer.DaemonListenerAdapter() {
@Override
public void daemonCancelEventOccurred(@NotNull String reason) {
RuntimeException e = new RuntimeException("Some bastard's restarted daemon: " + reason +
"\nStorage write log: ----------\n" +
StorageUtilKt.getDEBUG_LOG() + "\n--------------");
stopDaemonReason.compareAndSet(null, e);
}
});
try {
while (true) {
try {
myDaemonCodeAnalyzer.runPasses(getFile(), getDocument(getFile()), textEditor, new int[0], true, () -> {
if (!run.getAndSet(true)) {
action.run();
}
});
break;
}
catch (ProcessCanceledException ignored) { }
}
if (mustCancelItself) {
assertNotNull(stopDaemonReason.get());
}
else {
if (stopDaemonReason.get() != null) throw stopDaemonReason.get();
}
}
finally {
StorageUtilKt.setDEBUG_LOG(null);
Disposer.dispose(disposable);
}
}
public void testWholeFileInspection() throws Exception {
configureByFile(BASE_PATH + "FieldCanBeLocal.java");
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEquals(1, infos.size());
assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
ctrlW();
WriteCommandAction.runWriteCommandAction(getProject(), () -> EditorModificationUtil.deleteSelectedText(getEditor()));
type("xxxx");
infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
ctrlW();
WriteCommandAction.runWriteCommandAction(getProject(), () -> EditorModificationUtil.deleteSelectedText(getEditor()));
type("0");
infos = doHighlighting(HighlightSeverity.WARNING);
assertEquals(1, infos.size());
assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
}
private static class MyWholeInspection extends LocalInspectionTool {
private final List<PsiElement> visited = Collections.synchronizedList(new ArrayList<>());
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return "fegna";
}
@Nls
@NotNull
@Override
public String getDisplayName() {
return getGroupDisplayName();
}
@NotNull
@Override
public String getShortName() {
return getGroupDisplayName();
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new PsiElementVisitor() {
@Override
public void visitFile(PsiFile file) {
TimeoutUtil.sleep(1000); // make it run longer that LIP
super.visitFile(file);
}
@Override
public void visitElement(PsiElement element) {
visited.add(element);
super.visitElement(element);
}
};
}
@Override
public boolean runForWholeFile() {
return true;
}
}
public void testWholeFileInspectionRestartedOnAllElements() throws Exception {
MyWholeInspection tool = new MyWholeInspection();
enableInspectionTool(tool);
disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
int visitedCount = new HashSet<>(tool.visited).size();
tool.visited.clear();
type(" "); // white space modification
infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
int countAfter = new HashSet<>(tool.visited).size();
assertTrue("visitedCount = "+visitedCount+"; countAfter="+countAfter, countAfter >= visitedCount);
}
public void testWholeFileInspectionRestartedEvenIfThereWasAModificationInsideCodeBlockInOtherFile() throws Exception {
MyWholeInspection tool = new MyWholeInspection();
enableInspectionTool(tool);
disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
PsiFile file = configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
PsiFile otherFile = createFile(myModule, file.getContainingDirectory().getVirtualFile(), "otherFile.txt", "xxx");
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
int visitedCount = tool.visited.size();
assertTrue(tool.visited.toString(), visitedCount > 0);
tool.visited.clear();
Document otherDocument = PsiDocumentManager.getInstance(getProject()).getDocument(otherFile);
WriteCommandAction.runWriteCommandAction(getProject(), () -> otherDocument.setText("zzz"));
infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
int countAfter = tool.visited.size();
assertTrue(tool.visited.toString(), countAfter > 0);
tool.visited.clear();
//ensure started on another file
configureByExistingFile(otherFile.getVirtualFile());
infos = doHighlighting(HighlightSeverity.WARNING);
assertEmpty(infos);
int countAfter2 = tool.visited.size();
assertTrue(tool.visited.toString(), countAfter2 > 0);
}
public void testOverriddenMethodMarkers() throws Exception {
configureByFile(BASE_PATH + getTestName(false) + ".java");
highlightErrors();
Document document = getEditor().getDocument();
List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
assertEquals(3, markers.size());
type("//xxxx");
highlightErrors();
markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
assertEquals(3, markers.size());
}
public void testOverriddenMethodMarkersDoNotClearedByChangingWhitespaceNearby() throws Exception {
configureByFile(BASE_PATH + "OverriddenMethodMarkers.java");
highlightErrors();
Document document = getEditor().getDocument();
List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
assertEquals(markers.toString(), 3, markers.size());
PsiElement element = ((PsiJavaFile)myFile).getClasses()[0].findMethodsByName("f", false)[0].getReturnTypeElement().getNextSibling();
assertEquals(" ", element.getText());
getEditor().getCaretModel().moveToOffset(element.getTextOffset() + 1);
type(" ");
highlightErrors();
markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
assertEquals(markers.toString(), 3, markers.size());
}
public void testChangeXmlIncludeLeadsToRehighlight() throws Exception {
LanguageFilter[] extensions = ((CompositeLanguage)StdLanguages.XML).getLanguageExtensions();
for (LanguageFilter extension : extensions) {
((CompositeLanguage)StdLanguages.XML).unregisterLanguageExtension(extension);
}
final String location = getTestName(false) + ".xsd";
final String url = "http://myschema/";
ExternalResourceManagerExImpl.registerResourceTemporarily(url, location, getTestRootDisposable());
configureByFiles(null, BASE_PATH + getTestName(false) + ".xml", BASE_PATH + getTestName(false) + ".xsd");
Collection<HighlightInfo> errors = highlightErrors();
assertEquals(0, errors.size());
Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
Editor schemaEditor = null;
for (Editor editor : allEditors) {
Document document = editor.getDocument();
PsiFile file = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
if (file == null) continue;
if (location.equals(file.getName())) {
schemaEditor = editor;
break;
}
}
delete(schemaEditor);
errors = highlightErrors();
assertFalse(errors.isEmpty());
for (LanguageFilter extension : extensions) {
((CompositeLanguage)StdLanguages.XML).registerLanguageExtension(extension);
}
}
public void testRehighlightInnerBlockAfterInline() throws Exception {
configureByFile(BASE_PATH + getTestName(false) + ".java");
Collection<HighlightInfo> errors = highlightErrors();
HighlightInfo error = assertOneElement(errors);
assertEquals("Variable 'e' is already defined in the scope", error.getDescription());
PsiElement element = getFile().findElementAt(getEditor().getCaretModel().getOffset()).getParent();
DataContext dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.getName(), element, ((EditorEx)getEditor()).getDataContext());
new InlineRefactoringActionHandler().invoke(getProject(), getEditor(), getFile(), dataContext);
Collection<HighlightInfo> afterTyping = highlightErrors();
assertEmpty(afterTyping);
}
public void testRangeMarkersDoNotGetAddedOrRemovedWhenUserIsJustTypingInsideHighlightedRegionAndEspeciallyInsideInjectedFragmentsWhichAreColoredGreenAndUsersComplainEndlesslyThatEditorFlickersThere()
throws Throwable {
configureByText(JavaFileType.INSTANCE, "class S { int f() {\n" +
" return <caret>hashCode();\n" +
"}}");
Collection<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals(3, infos.size());
final int[] count = {0};
MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
@Override
public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
count[0]++;
}
@Override
public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
count[0]++;
}
});
type(' ');
highlightErrors();
assertEquals(0, count[0]);
}
public void testLineMarkersReuse() throws Throwable {
configureByFile(BASE_PATH + "LineMarkerChange.java");
List<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
assertSize(5, lineMarkers);
type('X');
final Collection<String> changed = new ArrayList<>();
MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
@Override
public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
}
@Override
public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
}
@Override
public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
}
private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
changed.add(highlighter+": \n"+reason);
}
});
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
List<HighlightInfo> infosAfter = CodeInsightTestFixtureImpl.instantiateAndRun(myFile, myEditor, new int[]{/*Pass.UPDATE_ALL, Pass.LOCAL_INSPECTIONS*/}, false);
assertNotEmpty(filter(infosAfter, HighlightSeverity.ERROR));
assertEmpty(changed);
List<LineMarkerInfo> lineMarkersAfter = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
assertEquals(lineMarkersAfter.size(), lineMarkers.size());
}
public void testLineMarkersDoNotBlinkOnBackSpaceRightBeforeMethodIdentifier() throws Throwable {
configureByText(JavaFileType.INSTANCE, "package x; \n" +
"class <caret>ToRun{\n" +
" public static void main(String[] args) {\n"+
" }\n"+
"}");
List<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
assertSize(2, lineMarkers);
backspace();
final Collection<String> changed = new ArrayList<>();
MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
@Override
public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
}
@Override
public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
}
@Override
public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
}
private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
changed.add(highlighter+": \n"+reason);
}
});
assertEmpty(highlightErrors());
assertSize(2, DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject()));
assertEmpty(changed);
}
public void testTypeParametersMustNotBlinkWhenTypingInsideClass() throws Throwable {
configureByText(JavaFileType.INSTANCE, "package x; \n" +
"abstract class ToRun<TTTTTTTTTTTTTTT> implements Comparable<TTTTTTTTTTTTTTT> {\n" +
" private ToRun<TTTTTTTTTTTTTTT> delegate;\n"+
" <caret>\n"+
" \n"+
"}");
List<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
@Override
public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
if (TextRange.create(highlighter).substring(highlighter.getDocument().getText()).equals("TTTTTTTTTTTTTTT")) {
throw new RuntimeException("Must not remove type parameter highlighter");
}
}
});
assertEmpty(highlightErrors());
type("//xxx");
assertEmpty(highlightErrors());
backspace();
assertEmpty(highlightErrors());
backspace();
assertEmpty(highlightErrors());
backspace();
assertEmpty(highlightErrors());
backspace();
backspace();
assertEmpty(highlightErrors());
}
public void testLocallyUsedFieldHighlighting() {
configureByText(JavaFileType.INSTANCE, "class A {\n" +
" String cons;\n" +
" void foo() {\n" +
" String local = null;\n" +
" <selection>cons</selection>.substring(1);" +
" }\n" +
" public static void main(String[] args) {\n" +
" new A().foo();\n" +
" }" +
"}");
enableInspectionTool(new UnusedDeclarationInspection(true));
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertSize(1, infos);
assertEquals("Variable 'local' is never used", infos.get(0).getDescription());
type("local");
infos = doHighlighting(HighlightSeverity.WARNING);
assertSize(1, infos);
assertEquals("Field 'cons' is never used", infos.get(0).getDescription());
}
public void testOverrideMethodsHighlightingPersistWhenTypeInsideMethodBody() throws Throwable {
configureByText(JavaFileType.INSTANCE, "package x; \n" +
"class ClassA {\n" +
" static <T> void sayHello(Class<? extends T> msg) {}\n" +
"}\n" +
"class ClassB extends ClassA {\n" +
" static <T extends String> void sayHello(Class<? extends T> msg) {<caret>\n" +
" }\n" +
"}\n");
assertSize(1, highlightErrors());
type("//my comment inside method body, so class modifier won't be visited");
assertSize(1, highlightErrors());
}
public void testLineMarkersClearWhenTypingAtTheEndOfPsiComment() throws Throwable {
configureByText(JavaFileType.INSTANCE, "class S {\n//ddd<caret>\n}");
StringBuffer log = new StringBuffer();
final LineMarkerProvider provider = new LineMarkerProvider() {
@Nullable
@Override
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
String msg = "provider.getLineMarkerInfo(" + element + ") called\n";
LineMarkerInfo<PsiComment> info = null;
if (element instanceof PsiComment) {
info = new LineMarkerInfo<>((PsiComment)element, element.getTextRange(), null, Pass.LINE_MARKERS, null, null, GutterIconRenderer.Alignment.LEFT);
msg += " provider info: "+info + "\n";
}
log.append(msg);
return info;
}
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
}
};
LineMarkerProviders.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, provider);
Disposer.register(getTestRootDisposable(), () -> LineMarkerProviders.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, provider));
myDaemonCodeAnalyzer.restart();
try {
TextRange range = FileStatusMap.getDirtyTextRange(getEditor(), Pass.UPDATE_ALL);
log.append("FileStatusMap.getDirtyTextRange: " + range+"\n");
List<PsiElement> elements = CollectHighlightsUtil.getElementsInRange(getFile(), range.getStartOffset(), range.getEndOffset());
log.append("CollectHighlightsUtil.getElementsInRange" + range + ": " + elements.size() +" elements : "+ elements+"\n");
List<HighlightInfo> infos = doHighlighting();
log.append(" File text: '" + getFile().getText() + "'\n");
log.append("infos: " + infos + "\n");
assertEmpty(filter(infos,HighlightSeverity.ERROR));
List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
assertOneElement(lineMarkers);
type(' ');
infos = doHighlighting();
log.append("File text: '" + getFile().getText() + "'\n");
log.append("infos: " + infos + "\n");
assertEmpty(filter(infos,HighlightSeverity.ERROR));
lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
assertOneElement(lineMarkers);
backspace();
infos = doHighlighting();
log.append("File text: '" + getFile().getText() + "'\n");
log.append("infos: " + infos + "\n");
assertEmpty(filter(infos,HighlightSeverity.ERROR));
lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
assertOneElement(lineMarkers);
}
catch (AssertionError e) {
System.err.println("Log:\n"+log+"\n---");
throw e;
}
}
public void testWhenTypingOverWrongReferenceItsColorChangesToBlackAndOnlyAfterHighlightingFinishedItReturnsToRed() throws Throwable {
configureByText(StdFileTypes.JAVA, "class S { int f() {\n" +
" return asfsdfsdfsd<caret>;\n" +
"}}");
Collection<HighlightInfo> errors = highlightErrors();
assertOneElement(errors);
assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
Document document = getDocument(getFile());
type("xxx");
List<HighlightInfo> infos = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightInfoType.SYMBOL_TYPE_SEVERITY, getProject());
for (HighlightInfo info : infos) {
assertNotSame(HighlightInfoType.WRONG_REF, info.type);
}
errors = highlightErrors();
assertOneElement(errors);
assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
}
public void testQuickFixRemainsAvailableAfterAnotherFixHasBeenAppliedInTheSameCodeBlockBefore() throws Exception {
configureByFile(BASE_PATH + "QuickFixes.java");
DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
boolean old = settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST;
settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = true;
try {
Collection<HighlightInfo> errors = highlightErrors();
assertEquals(3, errors.size());
new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
List<IntentionAction> fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
IntentionAction fix = assertContainsOneOf(fixes, "Delete catch for 'java.io.IOException'");
final IntentionAction finalFix = fix;
WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix.invoke(getProject(), getEditor(), getFile()));
errors = highlightErrors();
assertEquals(2, errors.size());
new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
fix = assertContainsOneOf(fixes, "Delete catch for 'java.io.IOException'");
final IntentionAction finalFix1 = fix;
WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix1.invoke(getProject(), getEditor(), getFile()));
errors = highlightErrors();
assertOneElement(errors);
new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
fix = assertContainsOneOf(fixes, "Delete catch for 'java.io.IOException'");
final IntentionAction finalFix2 = fix;
WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix2.invoke(getProject(), getEditor(), getFile()));
errors = highlightErrors();
assertEmpty(errors);
}
finally {
settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = old;
}
}
private static IntentionAction assertContainsOneOf(@NotNull Collection<IntentionAction> collection, @NotNull String text) {
IntentionAction result = null;
for (IntentionAction action : collection) {
if (text.equals(action.getText())) {
if (result != null) {
fail("multiple " + " objects present in collection " + collection);
}
else {
result = action;
}
}
}
assertNotNull(" object not found in collection " + collection, result);
return result;
}
public void testRangeHighlightersDoNotGetStuckForever() throws Throwable {
configureByText(StdFileTypes.JAVA, "class S { void ffffff() {fff<caret>fff();}}");
List<HighlightInfo> infos = highlightErrors();
assertEmpty(infos);
MarkupModel markup = DocumentMarkupModel.forDocument(myEditor.getDocument(), getProject(), true);
final TextRange[] highlightersBefore = getHighlightersTextRange(markup);
type("%%%%");
highlightErrors();
backspace();
backspace();
backspace();
backspace();
infos = highlightErrors();
assertEmpty(infos);
final TextRange[] highlightersAfter = getHighlightersTextRange(markup);
assertEquals(highlightersBefore.length, highlightersAfter.length);
for (int i = 0; i < highlightersBefore.length; i++) {
TextRange before = highlightersBefore[i];
TextRange after = highlightersAfter[i];
assertEquals(before.getStartOffset(), after.getStartOffset());
assertEquals(before.getEndOffset(), after.getEndOffset());
}
}
@NotNull
private static TextRange[] getHighlightersTextRange(@NotNull MarkupModel markup) {
final RangeHighlighter[] highlighters = markup.getAllHighlighters();
final TextRange[] result = new TextRange[highlighters.length];
for (int i = 0; i < highlighters.length; i++) {
result[i] = ProperTextRange.create(highlighters[i]);
}
return orderByHashCode(result); // markup.getAllHighlighters returns unordered array
}
@NotNull
private static <T extends Segment> T[] orderByHashCode(@NotNull T[] highlighters) {
Arrays.sort(highlighters, (o1, o2) -> o2.hashCode() - o1.hashCode());
return highlighters;
}
public void testFileStatusMapDirtyCachingWorks() throws Throwable {
myDaemonCodeAnalyzer.setUpdateByTimerEnabled(false); // to prevent auto-start highlighting
UIUtil.dispatchAllInvocationEvents();
configureByText(StdFileTypes.JAVA, "class <caret>S { int ffffff = 0;}");
UIUtil.dispatchAllInvocationEvents();
final int[] creation = {0};
class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
private Fac(Project project) {
super(project);
}
@Override
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
if (textRange == null) return null;
return new MyPass(myProject);
}
class MyPass extends TextEditorHighlightingPass {
private MyPass(final Project project) {
super(project, getEditor().getDocument(), false);
creation[0]++;
}
@Override
public void doCollectInformation(@NotNull ProgressIndicator progress) {
}
@Override
public void doApplyInformationToEditor() {
}
}
}
TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
highlightErrors();
assertEquals(1, creation[0]);
//cached
highlightErrors();
assertEquals(1, creation[0]);
highlightErrors();
assertEquals(1, creation[0]);
type(' ');
highlightErrors();
assertEquals(2, creation[0]);
highlightErrors();
assertEquals(2, creation[0]);
highlightErrors();
assertEquals(2, creation[0]);
}
public void testDefensivelyDirtyFlagDoesNotClearPrematurely() throws Throwable {
class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
private Fac(Project project) {
super(project);
}
@Override
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
return null;
}
}
TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
configureByText(StdFileTypes.JAVA, "@Deprecated<caret> class S { } ");
List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals(2, infos.size());
assertEquals("@Deprecated", infos.get(0).getText());
assertEquals("S", infos.get(1).getText());
backspace();
type('d');
List<HighlightInfo> after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals("@Deprecated", after.get(0).getText());
assertEquals("S", after.get(1).getText());
backspace();
type('d');
getEditor().getCaretModel().moveToOffset(getEditor().getDocument().getTextLength());
type(" ");
after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals(2, after.size());
assertEquals("@Deprecated", after.get(0).getText());
assertEquals("S", after.get(1).getText());
}
public void testModificationInsideCodeblockDoesnotAffectErrorMarkersOutside() throws Exception {
configureByFile(BASE_PATH + "ErrorMark.java");
List<HighlightInfo> errs = highlightErrors();
assertEquals(1, errs.size());
assertEquals("'}' expected", errs.get(0).getDescription());
type("//comment");
errs = highlightErrors();
assertEquals(1, errs.size());
assertEquals("'}' expected", errs.get(0).getDescription());
}
public void testErrorMarkerAtTheEndOfTheFile() throws Exception {
CommandProcessor.getInstance().executeCommand(getProject(), () -> {
try {
configureByFile(BASE_PATH + "ErrorMarkAtEnd.java");
}
catch (Exception e) {
LOG.error(e);
}
}, "cc", this);
List<HighlightInfo> errs = highlightErrors();
assertEmpty(errs);
CommandProcessor.getInstance().executeCommand(getProject(), () -> {
Document document = getEditor().getDocument();
int offset = getEditor().getCaretModel().getOffset();
while (offset < document.getTextLength()) {
int i = StringUtil.indexOf(document.getText(), '}', offset, document.getTextLength());
if (i == -1) break;
getEditor().getCaretModel().moveToOffset(i);
delete(getEditor());
}
}, "my", this);
errs = highlightErrors();
assertEquals(2, errs.size());
assertEquals("'}' expected", errs.get(0).getDescription());
undo();
errs = highlightErrors();
assertEmpty(errs);
}
// disabled for now
public void _testSOEInEndlessAppendChainPerformance() throws Throwable {
StringBuilder text = new StringBuilder("class S { String ffffff = new StringBuilder()\n");
for (int i=0; i<2000; i++) {
text.append(".append(").append(i).append(")\n");
}
text.append(".toString();<caret>}");
configureByText(StdFileTypes.JAVA, text.toString());
PlatformTestUtil.startPerformanceTest("too many tree visitors", 30000, () -> {
List<HighlightInfo> infos = highlightErrors();
assertEmpty(infos);
type("kjhgas");
List<HighlightInfo> errors = highlightErrors();
assertFalse(errors.isEmpty());
backspace();
backspace();
backspace();
backspace();
backspace();
backspace();
infos = highlightErrors();
assertEmpty(infos);
}).useLegacyScaling().assertTiming();
}
public void testBulbAppearsAfterType() throws Throwable {
String text = "class S { ArrayList<caret>XXX x;}";
configureByText(StdFileTypes.JAVA, text);
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
final Set<LightweightHint> shown = ContainerUtil.newIdentityTroveSet();
getProject().getMessageBus().connect().subscribe(EditorHintListener.TOPIC, (project, hint, flags) -> {
shown.add(hint);
hint.addHintListener(event -> shown.remove(hint));
});
DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
highlightErrors();
IntentionHintComponent hintComponent = codeAnalyzer.getLastIntentionHint();
assertNotNull(hintComponent);
assertFalse(hintComponent.isDisposed());
assertNotNull(hintComponent.getComponentHint());
assertTrue(shown.contains(hintComponent.getComponentHint()));
type("x");
highlightErrors();
hintComponent = codeAnalyzer.getLastIntentionHint();
assertNotNull(hintComponent);
assertFalse(hintComponent.isDisposed());
assertNotNull(hintComponent.getComponentHint());
assertTrue(shown.contains(hintComponent.getComponentHint()));
}
@Override
protected void configureByExistingFile(@NotNull VirtualFile virtualFile) {
super.configureByExistingFile(virtualFile);
EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
}
@Override
protected VirtualFile configureByFiles(@Nullable File rawProjectRoot, @NotNull VirtualFile... vFiles) throws IOException {
VirtualFile file = super.configureByFiles(rawProjectRoot, vFiles);
EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
return file;
}
public void testDaemonIgnoresFrameDeactivation() throws Throwable {
// return default value to avoid unnecessary save
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
String text = "class S { ArrayList<caret>XXX x;}";
configureByText(StdFileTypes.JAVA, text);
highlightErrors();
GeneralSettings settings = GeneralSettings.getInstance();
ApplicationEx application = ApplicationManagerEx.getApplicationEx();
boolean frameSave = settings.isSaveOnFrameDeactivation();
boolean appSave = application.isDoNotSave();
settings.setSaveOnFrameDeactivation(true);
application.doNotSave(false);
try {
SaveAndSyncHandlerImpl.doSaveDocumentsAndProjectsAndApp();
checkDaemonReaction(false, SaveAndSyncHandlerImpl::doSaveDocumentsAndProjectsAndApp);
}
finally {
application.doNotSave(appSave);
settings.setSaveOnFrameDeactivation(frameSave);
}
}
public void testApplyLocalQuickFix() throws Throwable {
configureByText(StdFileTypes.JAVA, "class X { static int sss; public int f() { return this.<caret>sss; }}");
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
List<HighlightInfo> warns = doHighlighting(HighlightSeverity.WARNING);
assertOneElement(warns);
List<HighlightInfo.IntentionActionDescriptor> actions = ShowIntentionsPass.getAvailableFixes(getEditor(), getFile(), -1);
final HighlightInfo.IntentionActionDescriptor descriptor = assertOneElement(actions);
CodeInsightTestFixtureImpl.invokeIntention(descriptor.getAction(), getFile(), getEditor(), "");
highlightErrors();
assertEmpty(ShowIntentionsPass.getAvailableFixes(getEditor(), getFile(), -1));
}
public void testApplyErrorInTheMiddle() throws Throwable {
String text = "class <caret>X { ";
for (int i = 0; i < 100; i++) {
text += "\n {\n" +
"// String x = \"<zzzzzzzzzz/>\";\n" +
" }";
}
text += "\n}";
configureByText(StdFileTypes.JAVA, text);
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
List<HighlightInfo> warns = highlightErrors();
assertEmpty(warns);
type("//");
List<HighlightInfo> errors = highlightErrors();
assertEquals(2, errors.size());
backspace();
backspace();
errors = highlightErrors();
assertEmpty(errors);
}
public void testErrorInTheEndOutsideVisibleArea() throws Throwable {
String text = "<xml> \n" + StringUtil.repeatSymbol('\n', 1000) + "</xml>\nxxxxx<caret>";
configureByText(StdFileTypes.XML, text);
ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 1000), myEditor);
assertTrue(visibleRange.getStartOffset() > 0);
List<HighlightInfo> warns = highlightErrors();
HighlightInfo info = assertOneElement(warns);
assertEquals("Top level element is not completed", info.getDescription());
type("xxx");
List<HighlightInfo> errors = highlightErrors();
info = assertOneElement(errors);
assertEquals("Top level element is not completed", info.getDescription());
}
public static ProperTextRange makeEditorWindowVisible(Point viewPosition, Editor editor) {
((EditorImpl)editor).getScrollPane().getViewport().setSize(1000, 1000);
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
((EditorImpl)editor).getScrollPane().getViewport().setViewPosition(viewPosition);
((EditorImpl)editor).getScrollPane().getViewport().setExtentSize(new Dimension(100, ((EditorImpl)editor).getPreferredHeight() -
viewPosition.y));
return VisibleHighlightingPassFactory.calculateVisibleRange(editor);
}
public void testEnterInCodeBlock() throws Throwable {
String text = "class LQF {\n" +
" int wwwwwwwwwwww;\n" +
" public void main() {<caret>\n" +
" wwwwwwwwwwww = 1;\n" +
" }\n" +
"}";
configureByText(StdFileTypes.JAVA, text);
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals(4, infos.size());
type('\n');
infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals(4, infos.size());
deleteLine();
infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
assertEquals(4, infos.size());
}
public void testTypingNearEmptyErrorElement() throws Throwable {
String text = "class LQF {\n" +
" public void main() {\n" +
" int wwwwwwwwwwww = 1<caret>\n" +
" }\n" +
"}";
configureByText(StdFileTypes.JAVA, text);
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
List<HighlightInfo> infos = highlightErrors();
assertEquals(1, infos.size());
type(';');
infos = highlightErrors();
assertEmpty(infos);
}
public void testLIPGetAllParentsAfterCodeBlockModification() throws Throwable {
@Language("JAVA")
String text = "class LQF {\n" +
" int f;\n" +
" public void me() {\n" +
" //<caret>\n" +
" }\n" +
"}";
configureByText(StdFileTypes.JAVA, text);
final List<PsiElement> visitedElements = Collections.synchronizedList(new ArrayList<>());
class MyVisitor extends PsiElementVisitor {
@Override
public void visitElement(PsiElement element) {
visitedElements.add(element);
}
}
final LocalInspectionTool tool = new LocalInspectionTool() {
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return "fegna";
}
@Nls
@NotNull
@Override
public String getDisplayName() {
return getGroupDisplayName();
}
@NotNull
@Override
public String getShortName() {
return getGroupDisplayName();
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new MyVisitor();
}
};
disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
enableInspectionTool(tool);
List<HighlightInfo> infos = highlightErrors();
assertEmpty(infos);
List<PsiElement> allPsi = CollectHighlightsUtil.getElementsInRange(myFile, 0, myFile.getTextLength());
assertEquals(new HashSet<>(allPsi), new HashSet<>(visitedElements));
// inside code block modification
visitedElements.clear();
backspace();
backspace();
infos = highlightErrors();
assertEmpty(infos);
PsiMethod method = ((PsiJavaFile)myFile).getClasses()[0].getMethods()[0];
List<PsiElement> methodAndParents =
CollectHighlightsUtil.getElementsInRange(myFile, method.getTextRange().getStartOffset(), method.getTextRange().getEndOffset(), true);
assertEquals(new HashSet<>(methodAndParents), new HashSet<>(visitedElements));
}
public void testCancelsItSelfOnTypingInAlienProject() throws Throwable {
String body = StringUtil.repeat("\"String field = null;\"\n", 1000);
configureByText(StdFileTypes.JAVA, "class X{ void f() {" + body + "<caret>\n} }");
File temp = createTempDirectory();
final Project alienProject = createProject(temp + "/alien.ipr", DebugUtil.currentStackTrace());
boolean succ2 = ProjectManagerEx.getInstanceEx().openProject(alienProject);
assertTrue(succ2);
DaemonProgressIndicator.setDebug(true);
try {
Module alienModule = doCreateRealModuleIn("x", alienProject, getModuleType());
final VirtualFile alienRoot = PsiTestUtil.createTestProjectStructure(alienProject, alienModule, myFilesToDelete);
OpenFileDescriptor alienDescriptor = new WriteAction<OpenFileDescriptor>() {
@Override
protected void run(@NotNull Result<OpenFileDescriptor> result) throws Throwable {
VirtualFile alienFile = alienRoot.createChildData(this, "X.java");
setFileText(alienFile, "class Alien { }");
OpenFileDescriptor alienDescriptor = new OpenFileDescriptor(alienProject, alienFile);
result.setResult(alienDescriptor);
}
}.execute().throwException().getResultObject();
FileEditorManager fe = FileEditorManager.getInstance(alienProject);
final Editor alienEditor = fe.openTextEditor(alienDescriptor, false);
((EditorImpl)alienEditor).setCaretActive();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
PsiDocumentManager.getInstance(alienProject).commitAllDocuments();
// start daemon in main project. should check for its cancel when typing in alien
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
final boolean[] checked = {false};
di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, () -> {
if (checked[0]) return;
checked[0] = true;
typeInAlienEditor(alienEditor, 'x');
});
}
catch (ProcessCanceledException ignored) {
//DaemonProgressIndicator.setDebug(true);
//System.out.println("indicator = " + indicator[0]);
return;
}
finally {
ProjectManagerEx.getInstanceEx().closeAndDispose(alienProject);
}
fail("must throw PCE");
}
public void testPasteInAnonymousCodeBlock() throws Throwable {
configureByText(StdFileTypes.JAVA, "class X{ void f() {" +
" int x=0;\n" +
" Runnable r = new Runnable() { public void run() {\n" +
" <caret>\n" +
" }};\n" +
" <selection>int y = x;</selection>\n " +
"\n} }");
assertEmpty(highlightErrors());
PlatformTestUtil.invokeNamedAction(IdeActions.ACTION_EDITOR_COPY);
assertEquals("int y = x;", getEditor().getSelectionModel().getSelectedText());
getEditor().getSelectionModel().removeSelection();
PlatformTestUtil.invokeNamedAction(IdeActions.ACTION_EDITOR_PASTE);
List<HighlightInfo> errors = highlightErrors();
assertEquals(1, errors.size());
}
public void testReactivityPerformance() throws Throwable {
@NonNls String filePath = "/psi/resolve/Thinlet.java";
configureByFile(filePath);
type(' ');
CompletionContributor.forLanguage(getFile().getLanguage());
highlightErrors();
final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, false));
System.out.println("N = " + N);
final long[] interruptTimes = new long[N];
for (int i = 0; i < N; i++) {
codeAnalyzer.restart();
final int finalI = i;
final long start = System.currentTimeMillis();
final AtomicLong typingStart = new AtomicLong();
final AtomicReference<RuntimeException> exception = new AtomicReference<>();
Thread watcher = new Thread("reactivity watcher") {
@Override
public void run() {
while (true) {
final long start1 = typingStart.get();
if (start1 == -1) break;
if (start1 == 0) {
try {
Thread.sleep(5);
}
catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
continue;
}
long elapsed = System.currentTimeMillis() - start1;
if (elapsed > 500) {
// too long, see WTF
String message = "Too long interrupt: " + elapsed +
"; Progress: " + codeAnalyzer.getUpdateProgress() +
"\n----------------------------";
dumpThreadsToConsole();
exception.set(new RuntimeException(message));
throw exception.get();
}
}
}
};
try {
PsiFile file = getFile();
Editor editor = getEditor();
Project project = file.getProject();
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
watcher.start();
Runnable interrupt = () -> {
long now = System.currentTimeMillis();
if (now - start < 100) {
// wait to engage all highlighting threads
return;
}
typingStart.set(System.currentTimeMillis());
type(' ');
long end = System.currentTimeMillis();
long interruptTime = end - now;
interruptTimes[finalI] = interruptTime;
assertTrue(codeAnalyzer.getUpdateProgress().isCanceled());
System.out.println(interruptTime);
throw new ProcessCanceledException();
};
long hiStart = System.currentTimeMillis();
codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
long hiEnd = System.currentTimeMillis();
DaemonProgressIndicator progress = codeAnalyzer.getUpdateProgress();
String message = "Should have been interrupted: " + progress + "; Elapsed: " + (hiEnd - hiStart) + "ms";
dumpThreadsToConsole();
throw new RuntimeException(message);
}
catch (ProcessCanceledException ignored) {
}
finally {
typingStart.set(-1); // cancel watcher
watcher.join();
if (exception.get() != null) {
throw exception.get();
}
}
}
long ave = ArrayUtil.averageAmongMedians(interruptTimes, 3);
System.out.println("Average among the N/3 median times: " + ave + "ms");
assertTrue(ave < 300);
}
private static void dumpThreadsToConsole() {
System.err.println("----all threads---");
for (Thread thread : Thread.getAllStackTraces().keySet()) {
boolean canceled = CoreProgressManager.isCanceledThread(thread);
if (canceled) {
System.err.println("Thread " + thread + " indicator is canceled");
}
}
PerformanceWatcher.dumpThreadsToConsole("");
System.err.println("----///////---");
}
public void testTypingLatencyPerformance() throws Throwable {
@NonNls String filePath = "/psi/resolve/ThinletBig.java";
configureByFile(filePath);
type(' ');
CompletionContributor.forLanguage(getFile().getLanguage());
long s = System.currentTimeMillis();
highlightErrors();
long e = System.currentTimeMillis();
//System.out.println("Hi elapsed: "+(e-s));
//List<String> dumps = new ArrayList<>();
final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, false));
System.out.println("N = " + N);
final long[] interruptTimes = new long[N];
for (int i = 0; i < N; i++) {
codeAnalyzer.restart();
final int finalI = i;
final long start = System.currentTimeMillis();
Runnable interrupt = () -> {
long now = System.currentTimeMillis();
if (now - start < 100) {
// wait to engage all highlighting threads
return;
}
// uncomment to debug what's causing pauses
/*
AtomicBoolean finished = new AtomicBoolean();
AppExecutorUtil.getAppScheduledExecutorService().schedule(() -> {
if (!finished.get()) {
dumps.add(ThreadDumper.dumpThreadsToString());
}
}, 10, TimeUnit.MILLISECONDS);
*/
type(' ');
long end = System.currentTimeMillis();
//finished.set(true);
long interruptTime = end - now;
interruptTimes[finalI] = interruptTime;
assertTrue(codeAnalyzer.getUpdateProgress().isCanceled());
throw new ProcessCanceledException();
};
try {
PsiFile file = getFile();
Editor editor = getEditor();
Project project = file.getProject();
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
throw new RuntimeException("should have been interrupted");
}
catch (ProcessCanceledException ignored) {
}
backspace();
//highlightErrors();
}
System.out.println("Interrupt times: " + Arrays.toString(interruptTimes));
/*
for (String dump : dumps) {
System.out.println("\n\n-----------------------------\n\n" + dump);
}
*/
long mean = ArrayUtil.averageAmongMedians(interruptTimes, 3);
long avg = Arrays.stream(interruptTimes).sum() / interruptTimes.length;
long max = Arrays.stream(interruptTimes).max().getAsLong();
long min = Arrays.stream(interruptTimes).min().getAsLong();
System.out.println("Average among the N/3 median times: " + mean + "ms; max: "+max+"; min:"+min+"; avg: "+avg);
assertTrue(String.valueOf(mean), mean < 10);
}
@NotNull
private static Collection<LocalInspectionTool> getInspectionsFor(@NotNull com.intellij.lang.Language language) {
List<InspectionToolWrapper> allInspections = InspectionToolRegistrar.getInstance().createTools();
Collection<LocalInspectionTool> filtered = allInspections.stream().
map(wrapper -> wrapper instanceof LocalInspectionToolWrapper ? (LocalInspectionToolWrapper)wrapper :
//wrapper instanceof GlobalInspectionToolWrapper
//? ((GlobalInspectionToolWrapper)wrapper).getSharedLocalInspectionToolWrapper() :
null)
.filter(wrapper -> wrapper != null && wrapper.isApplicable(language))
.map(InspectionToolWrapper::getTool)
.collect(Collectors.toSet());
System.out.println("All inspections: " + allInspections.size() + "; filtered for " + language+": "+filtered.size());
return filtered;
}
/*
private void inspect(LocalInspectionToolWrapper wrapper) {
PsiFile file = getFile();
TextRange range = file.getTextRange();
List<Divider.DividedElements> allDivided = new ArrayList<>();
Divider.divideInsideAndOutsideAllRoots(file, range, range, Conditions.alwaysTrue(), new CommonProcessors.CollectProcessor<>(allDivided));
List<PsiElement> elements = ContainerUtil.concat(
(List<List<PsiElement>>)ContainerUtil.map(allDivided, d -> ContainerUtil.concat(d.inside, d.outside, d.parents)));
PsiErrorElement errorElement = ContainerUtil.findInstance(elements, PsiErrorElement.class);
assertNull(file.getText(), errorElement);
Set<String> elementDialectIds = InspectionEngine.calcElementDialectIds(elements);
//List<HighlightInfo> errors = DaemonAnalyzerTestCase.filter(doHighlighting(), HighlightSeverity.ERROR);// warmup
//assertEmpty(errors);
timeInspection(wrapper, elements, elementDialectIds); // warmup
}
private int timeInspection(@NotNull LocalInspectionToolWrapper wrapper, @NotNull List<PsiElement> elements, @NotNull Set<String> elementDialectIds) {
long start = System.currentTimeMillis();
InspectionEngine.inspectElements(Collections.singletonList(wrapper), getFile(), InspectionManager.getInstance(getProject()), true, true, new EmptyProgressIndicator(), elements,
elementDialectIds);
return (int)(System.currentTimeMillis() - start);
}
public void testTypingLatencyForAllInspectionsPerformance() throws Throwable {
@NonNls String filePath = "/psi/resolve/ThinletBig.java";
//Collection<LocalInspectionTool> tools = getInspectionsFor(JavaLanguage.INSTANCE);
//enableInspectionTools(tools.toArray(new InspectionProfileEntry[tools.size()]));
configureByFile(filePath);
//type(' ');
//CompletionContributor.forLanguage(getFile().getLanguage());
//long s = System.currentTimeMillis();
//highlightErrors();
//long e = System.currentTimeMillis();
//System.out.println("Hi elapsed: "+(e-s));
final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, false));
System.out.println("N = " + N);
final long[] interruptTimes = new long[N];
LossyEncodingInspection encodingInspection = new LossyEncodingInspection();
LocalInspectionToolWrapper wrapper = new LocalInspectionToolWrapper(encodingInspection);
codeAnalyzer.setUpdateByTimerEnabled(false);
((PsiDocumentManagerBase)PsiDocumentManager.getInstance(getProject())).disableBackgroundCommit(getTestRootDisposable());
// position caret away from line start to disable smart backspace heuristics which does commit document
int offset = getEditor().getDocument().getText().indexOf("{ ")+1;
getEditor().getCaretModel().moveToOffset(offset);
for (int i = 0; i < N; i++) {
type(' ');
System.out.println("i = " + i);
//codeAnalyzer.restart();
//final int finalI = i;
//final long start = System.currentTimeMillis();
try {
//PsiFile file = getFile();
//Editor editor = getEditor();
//Project project = file.getProject();
//CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
//TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
//PsiDocumentManager.getInstance(myProject).commitAllDocuments();
inspect(wrapper);
}
catch (ProcessCanceledException ignored) {
}
backspace();
}
//long mean = ArrayUtil.averageAmongMedians(interruptTimes, 3);
//long avg = Arrays.stream(interruptTimes).sum() / interruptTimes.length;
//long max = Arrays.stream(interruptTimes).max().getAsLong();
//long min = Arrays.stream(interruptTimes).min().getAsLong();
//System.out.println("Average among the N/3 median times: " + mean + "ms; max: "+max+"; min:"+min+"; avg: "+avg);
//assertTrue(mean < 10);
}
public void testResolveAllAfterTypingLatencyPerformance() throws Throwable {
@NonNls String filePath = "/psi/resolve/ThinletBig.java";
//Collection<LocalInspectionTool> tools = getInspectionsFor(JavaLanguage.INSTANCE);
//enableInspectionTools(tools.toArray(new InspectionProfileEntry[tools.size()]));
configureByFile(filePath);
//type(' ');
//CompletionContributor.forLanguage(getFile().getLanguage());
//long s = System.currentTimeMillis();
//highlightErrors();
//long e = System.currentTimeMillis();
//System.out.println("Hi elapsed: "+(e-s));
final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, false));
System.out.println("N = " + N);
final long[] interruptTimes = new long[N];
LossyEncodingInspection encodingInspection = new LossyEncodingInspection();
LocalInspectionToolWrapper wrapper = new LocalInspectionToolWrapper(encodingInspection);
codeAnalyzer.setUpdateByTimerEnabled(false);
((PsiDocumentManagerBase)PsiDocumentManager.getInstance(getProject())).disableBackgroundCommit(getTestRootDisposable());
// position caret away from line start to disable smart backspace heuristics which does commit document
int offset = getEditor().getDocument().getText().indexOf("{ ")+1;
getEditor().getCaretModel().moveToOffset(offset);
for (int i = 0; i < N; i++) {
type(' ');
System.out.println("i = " + i);
//codeAnalyzer.restart();
//final int finalI = i;
//final long start = System.currentTimeMillis();
try {
//PsiFile file = getFile();
//Editor editor = getEditor();
//Project project = file.getProject();
//CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
//TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
//PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
//getFile().accept(new PsiRecursiveElementWalkingVisitor() {
// @Override
// public void visitElement(PsiElement element) {
// for (PsiReference reference : element.getReferences()) {
// reference.resolve();
// }
// super.visitElement(element);
// }
//});
}
catch (ProcessCanceledException ignored) {
}
backspace();
}
//long mean = ArrayUtil.averageAmongMedians(interruptTimes, 3);
//long avg = Arrays.stream(interruptTimes).sum() / interruptTimes.length;
//long max = Arrays.stream(interruptTimes).max().getAsLong();
//long min = Arrays.stream(interruptTimes).min().getAsLong();
//System.out.println("Average among the N/3 median times: " + mean + "ms; max: "+max+"; min:"+min+"; avg: "+avg);
//assertTrue(mean < 10);
}
*/
private static void startCPUProfiling() {
try {
Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
Method method = ReflectionUtil.getDeclaredMethod(aClass, "startCPUProfiling");
method.invoke(null);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void stopCPUProfiling() {
try {
Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
Method method = ReflectionUtil.getDeclaredMethod(aClass, "stopCPUProfiling");
method.invoke(null);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String captureCPUSnapshot() {
try {
Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
Method method = ReflectionUtil.getDeclaredMethod(aClass, "captureCPUSnapshot");
return (String)method.invoke(null);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void testPostHighlightingPassRunsOnEveryPsiModification() throws Exception {
PsiFile x = createFile("X.java", "public class X { public static void ffffffffffffff(){} }");
PsiFile use = createFile("Use.java", "public class Use { { <caret>X.ffffffffffffff(); } }");
configureByExistingFile(use.getVirtualFile());
InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getCurrentProfile();
HighlightDisplayKey myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME);
if (myDeadCodeKey == null) {
myDeadCodeKey = HighlightDisplayKey.register(UnusedDeclarationInspectionBase.SHORT_NAME, UnusedDeclarationInspectionBase.DISPLAY_NAME);
}
UnusedDeclarationInspectionBase myDeadCodeInspection = new UnusedDeclarationInspectionBase(true);
enableInspectionTool(myDeadCodeInspection);
assert profile.isToolEnabled(myDeadCodeKey, myFile);
Editor xEditor = createEditor(x.getVirtualFile());
List<HighlightInfo> xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false),
HighlightSeverity.WARNING);
HighlightInfo info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
assertNull(xInfos.toString(), info);
Editor useEditor = myEditor;
List<HighlightInfo> useInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(use, useEditor, new int[0], false), HighlightSeverity.ERROR);
assertEmpty(useInfos);
type('/');
type('/');
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false), HighlightSeverity.WARNING);
info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
assertNotNull(xInfos.toString(), info);
}
public void testErrorDisappearsRightAfterTypingInsideVisibleAreaWhileDaemonContinuesToChugAlong() throws Throwable {
String text = "class X{\nint xxx;\n{\nint i = <selection>null</selection><caret>;\n" + StringUtil.repeat("{ this.hashCode(); }\n\n\n", 10000) + "}}";
configureByText(StdFileTypes.JAVA, text);
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(100, 100);
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
((EditorImpl)myEditor).getScrollPane().getViewport().setViewPosition(new Point(0, 0));
((EditorImpl)myEditor).getScrollPane().getViewport().setExtentSize(new Dimension(100, 100000));
ProperTextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(getEditor());
assertTrue(visibleRange.getLength() > 0);
final Document document = myEditor.getDocument();
assertTrue(visibleRange.getLength() < document.getTextLength());
List<HighlightInfo> err1 = highlightErrors();
HighlightInfo info = assertOneElement(err1);
final String errorDescription = "Incompatible types. Found: 'null', required: 'int'";
assertEquals(errorDescription, info.getDescription());
MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, myProject, false);
final boolean[] errorRemoved = {false};
model.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
@Override
public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
Object tt = highlighter.getErrorStripeTooltip();
if (!(tt instanceof HighlightInfo)) return;
String description = ((HighlightInfo)tt).getDescription();
if (errorDescription.equals(description)) {
errorRemoved[0] = true;
List<TextEditorHighlightingPass> passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(document);
GeneralHighlightingPass ghp = null;
for (TextEditorHighlightingPass pass : passes) {
if (pass instanceof GeneralHighlightingPass && pass.getId() == Pass.UPDATE_ALL) {
assert ghp == null : ghp;
ghp = (GeneralHighlightingPass)pass;
}
}
assertNotNull(ghp);
boolean finished = ghp.isFinished();
assertFalse(finished);
}
}
});
type("1");
List<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
assertTrue(errorRemoved[0]);
}
public void testDaemonWorksForDefaultProjectSinceItIsNeededInSettingsDialogForSomeReason() {
assertNotNull(DaemonCodeAnalyzer.getInstance(ProjectManager.getInstance().getDefaultProject()));
}
public void testChangeEventsAreNotAlwaysGeneric() throws Exception {
String body = "class X {\n" +
"<caret> @org.PPP\n" +
" void dtoArrayDouble() {\n" +
" }\n" +
"}";
configureByText(JavaFileType.INSTANCE, body);
makeEditorWindowVisible(new Point(), myEditor);
List<HighlightInfo> errors = highlightErrors();
assertFalse(errors.isEmpty());
type("//");
errors = highlightErrors();
assertEmpty(errors);
backspace();
backspace();
errors = highlightErrors();
assertFalse(errors.isEmpty());
}
public void testInterruptOnTyping() throws Throwable {
@NonNls String filePath = "/psi/resolve/Thinlet.java";
configureByFile(filePath);
highlightErrors();
final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
codeAnalyzer.restart();
try {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiFile file = getFile();
Editor editor = getEditor();
Project project = file.getProject();
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, () -> type(' '));
}
catch (ProcessCanceledException ignored) {
return;
}
fail("PCE must have been thrown");
}
public void testAllPassesFinishAfterInterruptOnTyping_Performance() throws Throwable {
@NonNls String filePath = "/psi/resolve/Thinlet.java";
configureByFile(filePath);
highlightErrors();
final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
type(' ');
for (int i=0; i<100; i++) {
backspace();
codeAnalyzer.restart();
try {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiFile file = getFile();
Editor editor = getEditor();
Project project = file.getProject();
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, () -> type(' '));
}
catch (ProcessCanceledException ignored) {
codeAnalyzer.waitForTermination();
continue;
}
fail("PCE must have been thrown");
}
}
public void testCodeFoldingInSplittedWindowAppliesToAllEditors() throws Exception {
final Set<Editor> applied = new THashSet<>();
final Set<Editor> collected = new THashSet<>();
registerFakePass(applied, collected);
configureByText(PlainTextFileType.INSTANCE, "");
Editor editor1 = getEditor();
final Editor editor2 = EditorFactory.getInstance().createEditor(editor1.getDocument(),getProject());
Disposer.register(getProject(), () -> EditorFactory.getInstance().releaseEditor(editor2));
TextEditor textEditor1 = new PsiAwareTextEditorProvider().getTextEditor(editor1);
TextEditor textEditor2 = new PsiAwareTextEditorProvider().getTextEditor(editor2);
List<HighlightInfo> errors = myDaemonCodeAnalyzer.runPasses(myFile, editor1.getDocument(), Arrays.asList(textEditor1,textEditor2), new int[0], false, null);
assertEmpty(errors);
assertEquals(collected, ContainerUtil.newHashSet(editor1, editor2));
assertEquals(applied, ContainerUtil.newHashSet(editor1, editor2));
}
private void registerFakePass(@NotNull final Set<Editor> applied, @NotNull final Set<Editor> collected) {
class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
private Fac(Project project) {
super(project);
}
@Override
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
return new EditorBoundHighlightingPass(editor, file, false) {
@Override
public void doCollectInformation(@NotNull ProgressIndicator progress) {
collected.add(editor);
}
@Override
public void doApplyInformationToEditor() {
applied.add(editor);
}
};
}
}
TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
}
private volatile boolean runHeavyProcessing;
public void testDaemonDisablesItselfDuringHeavyProcessing() throws Exception {
executeWithoutReparseDelay(() -> {
runHeavyProcessing = false;
try {
final Set<Editor> applied = Collections.synchronizedSet(new THashSet<>());
final Set<Editor> collected = Collections.synchronizedSet(new THashSet<>());
registerFakePass(applied, collected);
configureByText(PlainTextFileType.INSTANCE, "");
Editor editor = getEditor();
EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
editorTracker.setActiveEditors(Collections.singletonList(editor));
while (HeavyProcessLatch.INSTANCE.isRunning()) {
UIUtil.dispatchAllInvocationEvents();
}
type("xxx"); // restart daemon
assertTrue(editorTracker.getActiveEditors().contains(editor));
assertSame(editor, FileEditorManager.getInstance(myProject).getSelectedTextEditor());
// wait for first pass to complete
long start = System.currentTimeMillis();
while (myDaemonCodeAnalyzer.isRunning() || !applied.contains(editor)) {
UIUtil.dispatchAllInvocationEvents();
if (System.currentTimeMillis() - start > 10000) {
fail("Too long waiting for daemon");
}
}
runHeavyProcessing = true;
ApplicationManager.getApplication().executeOnPooledThread(() -> {
AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("my own heavy op");
try {
while (runHeavyProcessing) {
}
}
finally {
token.finish();
}
});
while (!HeavyProcessLatch.INSTANCE.isRunning()) {
UIUtil.dispatchAllInvocationEvents();
}
applied.clear();
collected.clear();
type("xxx"); // try to restart daemon
start = System.currentTimeMillis();
while (System.currentTimeMillis() < start + 5000) {
assertEmpty(applied); // it should not restart
assertEmpty(collected);
UIUtil.dispatchAllInvocationEvents();
}
}
finally {
runHeavyProcessing = false;
}
});
}
public void testModificationInsideCodeBlockDoesNotRehighlightWholeFile() throws Exception {
configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
List<HighlightInfo> errors = highlightErrors();
assertEquals(1, errors.size());
assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
errors.get(0).getHighlighter().dispose();
errors = highlightErrors();
assertEmpty(errors);
type("23");
errors = highlightErrors();
assertEmpty(errors);
myEditor.getCaretModel().moveToOffset(0);
type("/* */");
errors = highlightErrors();
assertEquals(1, errors.size());
assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
}
public void _testCaretMovementDoesNotRestartHighlighting() throws Exception {
configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
final DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
final AtomicReference<ProgressIndicator> indicator = new AtomicReference<>();
final List<HighlightInfo> errors = filter(
di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, () -> {
if (indicator.get() == null) {
indicator.set(di.getUpdateProgress());
}
assertSame(indicator.get(), di.getUpdateProgress());
caretRight();
if (getEditor().getCaretModel().getOffset() == getEditor().getDocument().getTextLength()-1) {
getEditor().getCaretModel().moveToOffset(0);
}
}), HighlightSeverity.ERROR);
assertEquals(1, errors.size());
assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
}
public void testHighlightingDoesWaitForEmbarrassinglySlowExternalAnnotatorsToFinish() throws Exception {
configureByText(JavaFileType.INSTANCE, "class X { int f() { int gg<caret> = 11; return 0;} }");
final AtomicBoolean run = new AtomicBoolean();
final int SLEEP = 20000;
ExternalAnnotator<Integer, Integer> annotator = new ExternalAnnotator<Integer, Integer>() {
@Nullable
@Override
public Integer collectInformation(@NotNull PsiFile file) {
return 0;
}
@Nullable
@Override
public Integer doAnnotate(final Integer collectedInfo) {
TimeoutUtil.sleep(SLEEP);
return 0;
}
@Override
public void apply(@NotNull final PsiFile file, final Integer annotationResult, @NotNull final AnnotationHolder holder) {
run.set(true);
}
};
ExternalLanguageAnnotators.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, annotator);
try {
long start = System.currentTimeMillis();
List<HighlightInfo> errors = filter(CodeInsightTestFixtureImpl.instantiateAndRun(getFile(), getEditor(), new int[0], false),
HighlightSeverity.ERROR);
long elapsed = System.currentTimeMillis() - start;
assertEquals(0, errors.size());
if (!run.get()) {
fail(ThreadDumper.dumpThreadsToString());
}
assertTrue("Elapsed: "+elapsed, elapsed >= SLEEP);
}
finally {
ExternalLanguageAnnotators.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, annotator);
}
}
public void testModificationInExcludedFileDoesNotCauseRehighlight() throws Exception {
final PsiFile excluded = configureByText(JavaFileType.INSTANCE, "class EEE { void f(){} }");
PsiTestUtil.addExcludedRoot(myModule, excluded.getVirtualFile().getParent());
configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
List<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
assertNull(scope);
WriteCommandAction.runWriteCommandAction(getProject(), () -> ((PsiJavaFile)excluded).getClasses()[0].getMethods()[0].delete());
UIUtil.dispatchAllInvocationEvents();
scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
assertNull(scope);
}
public void testModificationInWorkspaceXmlDoesNotCauseRehighlight() throws Exception {
configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
ApplicationEx application = ApplicationManagerEx.getApplicationEx();
boolean appSave = application.isDoNotSave();
application.doNotSave(false);
try {
application.saveAll();
final PsiFile excluded = PsiManager.getInstance(getProject()).findFile(getProject().getWorkspaceFile());
List<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
assertNull(scope);
WriteCommandAction.runWriteCommandAction(getProject(), () -> {
Document document = PsiDocumentManager.getInstance(getProject()).getDocument(excluded);
document.insertString(0, "<!-- dsfsd -->");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
});
UIUtil.dispatchAllInvocationEvents();
scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
assertNull(scope);
}
finally {
application.doNotSave(appSave);
}
}
public void testLightBulbDoesNotUpdateIntentionsInEDT() throws Exception {
final IntentionAction longLongUpdate = new AbstractIntentionAction() {
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
}
@Nls
@NotNull
@Override
public String getText() {
return "LongAction";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (ApplicationManager.getApplication().isDispatchThread()) {
throw new RuntimeException("Must not update actions in EDT");
}
return true;
}
};
IntentionManager.getInstance().addAction(longLongUpdate);
Disposer.register(getTestRootDisposable(), () -> IntentionManager.getInstance().unregisterIntention(longLongUpdate));
configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
makeEditorWindowVisible(new Point(0, 0), myEditor);
doHighlighting();
myDaemonCodeAnalyzer.restart();
executeWithoutReparseDelay(() -> {
for (int i = 0; i < 1000; i++) {
caretRight();
UIUtil.dispatchAllInvocationEvents();
caretLeft();
DaemonProgressIndicator updateProgress = myDaemonCodeAnalyzer.getUpdateProgress();
while (myDaemonCodeAnalyzer.getUpdateProgress() == updateProgress) { // wait until daemon started
UIUtil.dispatchAllInvocationEvents();
}
long start = System.currentTimeMillis();
while (myDaemonCodeAnalyzer.isRunning() && System.currentTimeMillis() < start + 500) {
UIUtil.dispatchAllInvocationEvents(); // wait for a bit more until ShowIntentionsPass.doApplyInformationToEditor() called
}
}
});
}
private static void executeWithoutReparseDelay(@NotNull Runnable task) {
DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
int oldDelay = settings.AUTOREPARSE_DELAY;
settings.AUTOREPARSE_DELAY = 0;
try {
task.run();
}
finally {
settings.AUTOREPARSE_DELAY = oldDelay;
}
}
public void testLightBulbIsHiddenWhenFixRangeIsCollapsed() {
configureByText(StdFileTypes.JAVA, "class S { void foo() { boolean var; if (<selection>va<caret>r</selection>) {}} }");
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
final Set<LightweightHint> visibleHints = ContainerUtil.newIdentityTroveSet();
getProject().getMessageBus().connect(getTestRootDisposable()).subscribe(EditorHintListener.TOPIC, new EditorHintListener() {
@Override
public void hintShown(final Project project, final LightweightHint hint, final int flags) {
visibleHints.add(hint);
hint.addHintListener(new HintListener() {
@Override
public void hintHidden(EventObject event) {
visibleHints.remove(hint);
hint.removeHintListener(this);
}
});
}
});
highlightErrors();
IntentionHintComponent lastHintBeforeDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
assertNotNull(lastHintBeforeDeletion);
delete(myEditor);
highlightErrors();
IntentionHintComponent lastHintAfterDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
assertSame(lastHintBeforeDeletion, lastHintAfterDeletion);
assertEmpty(visibleHints);
}
public void testCodeFoldingPassRestartsOnRegionUnfolding() throws Exception {
executeWithoutReparseDelay(() -> {
configureByText(StdFileTypes.JAVA, "class Foo {\n" +
" void m() {\n" +
"\n" +
" }\n" +
"}");
CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myEditor);
waitForDaemon();
EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_COLLAPSE_ALL_REGIONS);
waitForDaemon();
checkFoldingState("[FoldRegion +(25:33), placeholder='{}']");
new WriteCommandAction<Void>(myProject) {
@Override
protected void run(@NotNull Result<Void> result) throws Throwable {
myEditor.getDocument().insertString(0, "/*");
}
}.execute();
waitForDaemon();
checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{}']");
EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_EXPAND_ALL_REGIONS);
waitForDaemon();
checkFoldingState("[FoldRegion -(0:37), placeholder='/.../']");
});
}
public void testChangingSettingsHasImmediateEffectOnOpenedEditor() throws Exception {
executeWithoutReparseDelay(() -> {
configureByText(StdFileTypes.JAVA, "class C { \n" +
" void m() {\n" +
" } \n" +
"}");
CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myEditor);
waitForDaemon();
checkFoldingState("[FoldRegion -(22:27), placeholder='{}']");
JavaCodeFoldingSettings settings = JavaCodeFoldingSettings.getInstance();
boolean savedValue = settings.isCollapseMethods();
try {
settings.setCollapseMethods(true);
CodeFoldingConfigurable.applyCodeFoldingSettingsChanges();
waitForDaemon();
checkFoldingState("[FoldRegion +(22:27), placeholder='{}']");
}
finally {
settings.setCollapseMethods(savedValue);
}
});
}
private void checkFoldingState(String expected) {
assertEquals(expected, Arrays.toString(myEditor.getFoldingModel().getAllFoldRegions()));
}
private void waitForDaemon() {
long deadline = System.currentTimeMillis() + 60_000;
while (!daemonIsWorkingOrPending()) {
if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to start");
UIUtil.dispatchInvocationEvent();
}
while (daemonIsWorkingOrPending()) {
if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to finish");
UIUtil.dispatchInvocationEvent();
}
}
private boolean daemonIsWorkingOrPending() {
return PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument()) || myDaemonCodeAnalyzer.isRunningOrPending();
}
public void testRehighlightInDebuggerExpressionFragment() throws Exception {
PsiExpressionCodeFragment fragment = JavaCodeFragmentFactory.getInstance(getProject()).createExpressionCodeFragment("+ <caret>\"a\"", null,
PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())), true);
myFile = fragment;
Document document = PsiDocumentManager.getInstance(getProject()).getDocument(fragment);
myEditor = EditorFactory.getInstance().createEditor(document, getProject(), StdFileTypes.JAVA, false);
ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 0), myEditor);
assertEquals(document.getTextLength(), visibleRange.getLength());
try {
final EditorInfo editorInfo = new EditorInfo(document.getText());
final String newFileText = editorInfo.getNewFileText();
ApplicationManager.getApplication().runWriteAction(() -> {
if (!document.getText().equals(newFileText)) {
document.setText(newFileText);
}
editorInfo.applyToEditor(myEditor);
});
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
List<HighlightInfo> errors = highlightErrors();
HighlightInfo error = assertOneElement(errors);
assertEquals("Operator '+' cannot be applied to 'java.lang.String'", error.getDescription());
type(" ");
Collection<HighlightInfo> afterTyping = highlightErrors();
HighlightInfo after = assertOneElement(afterTyping);
assertEquals("Operator '+' cannot be applied to 'java.lang.String'", after.getDescription());
}
finally {
EditorFactory.getInstance().releaseEditor(myEditor);
}
}
public void testFileReload() throws Exception {
VirtualFile file = createFile("a.java", "").getVirtualFile();
Document document = getDocument(file);
assertNotNull(document);
FileStatusMap fileStatusMap = myDaemonCodeAnalyzer.getFileStatusMap();
WriteCommandAction.runWriteCommandAction(getProject(), () -> {
PlatformTestUtil.tryGcSoftlyReachableObjects();
assertNull(PsiDocumentManager.getInstance(getProject()).getCachedPsiFile(document));
document.insertString(0, "class X { void foo() {}}");
assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
FileContentUtilCore.reparseFiles(file);
assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
findClass("X").getMethods()[0].delete();
assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
});
}
public void testAddRemoveHighlighterRaceInIncorrectAnnotatorsWhichUseFileRecursiveVisit() throws Exception {
Annotator annotator = new MyIncorrectlyRecursiveAnnotator();
com.intellij.lang.Language java = StdFileTypes.JAVA.getLanguage();
LanguageAnnotators.INSTANCE.addExplicitExtension(java, annotator);
try {
List<Annotator> list = LanguageAnnotators.INSTANCE.allForLanguage(java);
assertTrue(list.toString(), list.contains(annotator));
configureByText(StdFileTypes.JAVA, "class X {\n" +
" int foo(Object param) {\n" +
" if (param == this) return 1;\n" +
" return 0;\n" +
" }\n" +
"}\n");
((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
assertEquals(getFile().getTextRange(), VisibleHighlightingPassFactory.calculateVisibleRange(getEditor()));
assertEquals("XXX", assertOneElement(doHighlighting(HighlightSeverity.WARNING)).getDescription());
for (int i=0; i<100; i++) {
//System.out.println("i = " + i);
DaemonCodeAnalyzer.getInstance(getProject()).restart();
List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
assertEquals("XXX", assertOneElement(infos).getDescription());
}
}
finally {
LanguageAnnotators.INSTANCE.removeExplicitExtension(java, annotator);
}
List<Annotator> list = LanguageAnnotators.INSTANCE.allForLanguage(java);
assertFalse(list.toString(), list.contains(annotator));
}
public static class MyIncorrectlyRecursiveAnnotator implements Annotator {
Random random = new Random();
@Override
public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) {
if (psiElement instanceof PsiFile) {
psiElement.accept(new JavaRecursiveElementWalkingVisitor(){
@Override
public void visitKeyword(PsiKeyword keyword) {
if (Objects.equals(keyword.getText(), "this")) {
holder.createAnnotation(HighlightSeverity.WARNING, keyword.getTextRange(), "XXX");
TimeoutUtil.sleep(random.nextInt(100));
}
}
});
}
}
}
public void testDumbQuickFixIsNoLongerVisibleAfterApplied() {
MyInspection tool = new MyInspection();
enableInspectionTool(tool);
disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
@Language("JAVA")
String text = "class X { void f() { if (this == null) {} else return; } }";
configureByText(JavaFileType.INSTANCE, text);
WriteCommandAction.runWriteCommandAction(getProject(), () -> myEditor.getDocument().setText(text));
getEditor().getCaretModel().moveToOffset(getFile().getText().indexOf("if (") + 1);
assertEmpty(doHighlighting(HighlightSeverity.ERROR));
List<IntentionAction> fixes = findStupidFixes();
IntentionAction fix = assertOneElement(fixes);
fix.invoke(getProject(), getEditor(), getFile());
fixes = findStupidFixes();
assertEmpty(fixes);
assertEmpty(doHighlighting(HighlightSeverity.ERROR));
fixes = findStupidFixes();
assertEmpty(fixes);
}
private List<IntentionAction> findStupidFixes() {
return CodeInsightTestFixtureImpl.getAvailableIntentions(getEditor(), getFile())
.stream().filter(f->f.getFamilyName().equals(new MyInspection.StupidQuickFixWhichDoesntCheckItsOwnApplicability().getFamilyName()))
.collect(Collectors.toList());
}
private static class MyInspection extends LocalInspectionTool {
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return "danuna";
}
@Nls
@NotNull
@Override
public String getDisplayName() {
return getGroupDisplayName();
}
@NotNull
@Override
public String getShortName() {
return getGroupDisplayName();
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitIfStatement(PsiIfStatement statement) {
if (statement.getElseBranch() != null) {
PsiKeyword keyw = (PsiKeyword)statement.getChildren()[0];
holder.registerProblem(keyw, "dododo", new StupidQuickFixWhichDoesntCheckItsOwnApplicability());
}
}
};
}
private static class StupidQuickFixWhichDoesntCheckItsOwnApplicability implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return "danu";
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
WriteCommandAction.runWriteCommandAction(project, () -> ((PsiIfStatement)descriptor.getPsiElement().getParent()).getElseBranch().delete());
}
}
}
}