diff --git a/platform/platform-api/src/org/jetbrains/ide/WebServerManager.java b/platform/platform-api/src/org/jetbrains/ide/WebServerManager.java index 3f969052f806..af77316bf416 100644 --- a/platform/platform-api/src/org/jetbrains/ide/WebServerManager.java +++ b/platform/platform-api/src/org/jetbrains/ide/WebServerManager.java @@ -19,4 +19,4 @@ public abstract class WebServerManager implements ApplicationComponent { public abstract int getPort(); public abstract void addClosingListener(ChannelFutureListener listener); -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/execution/AutoExecutor.java b/platform/platform-impl/src/com/intellij/execution/AutoExecutor.java new file mode 100644 index 000000000000..5ce33781662a --- /dev/null +++ b/platform/platform-impl/src/com/intellij/execution/AutoExecutor.java @@ -0,0 +1,142 @@ +package com.intellij.execution; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileAdapter; +import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.newvfs.BulkFileListener; +import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.problems.WolfTheProblemSolver; +import com.intellij.util.Alarm; +import com.intellij.util.Consumer; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class AutoExecutor implements Runnable { + private final Project project; + private final Alarm alarm; + private final Consumer consumer; + private final int delay; + + private final MyDocumentAdapter listener; + private boolean listenerAttached; + + private final Set changedFiles = new THashSet(); + private boolean wasRequested; + + private final Condition documentChangedFilter; + + public AutoExecutor(Project project, Alarm alarm, int delay, Consumer consumer, Condition documentChangedFilter) { + this.project = project; + this.alarm = alarm; + this.delay = delay; + this.consumer = consumer; + this.documentChangedFilter = documentChangedFilter; + + listener = new MyDocumentAdapter(); + + project.getMessageBus().connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { + @Override + public void before(@NotNull List events) { + } + + @Override + public void after(@NotNull List events) { + for (VFileEvent event : events) { + if (event instanceof VFileDeleteEvent) { + synchronized (changedFiles) { + changedFiles.remove(event.getFile()); + } + } + } + } + }); + } + + private void addVfsListener() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + @Override + public void fileDeleted(VirtualFileEvent event) { + synchronized (changedFiles) { + changedFiles.remove(event.getFile()); + } + } + }); + } + + @Override + public void run() { + final VirtualFile[] files; + synchronized (changedFiles) { + wasRequested = false; + files = changedFiles.toArray(new VirtualFile[changedFiles.size()]); + changedFiles.clear(); + } + + final WolfTheProblemSolver problemSolver = WolfTheProblemSolver.getInstance(project); + for (VirtualFile file : files) { + if (problemSolver.hasSyntaxErrors(file)) { + // threat any other file in queue as dependency of this file — don't flush if some of the queued file is invalid + // Vladimir.Krivosheev AutoTestManager behavior behavior is preserved. + // LiveEdit version used another strategy (flush all valid files), but now we use AutoTestManager-inspired strategy + synchronized (changedFiles) { + Collections.addAll(changedFiles, files); + } + + return; + } + } + + consumer.consume(files); + } + + public Project getProject() { + return project; + } + + public void activate() { + if (!listenerAttached) { + listenerAttached = true; + EditorFactory.getInstance().getEventMulticaster().addDocumentListener(listener, project); + } + } + + public void deactivate() { + if (listenerAttached) { + listenerAttached = false; + EditorFactory.getInstance().getEventMulticaster().removeDocumentListener(listener); + } + } + + private class MyDocumentAdapter extends DocumentAdapter { + public void documentChanged(DocumentEvent event) { + final Document document = event.getDocument(); + final VirtualFile file = FileDocumentManager.getInstance().getFile(document); + if (file == null || !documentChangedFilter.value(file)) { + return; + } + + synchronized (changedFiles) { + // changedFiles contains is not enough, because it can contain not-flushed files from prev request (which are not flushed because some is invalid) + if (!changedFiles.add(file) && wasRequested) { + return; + } + } + + alarm.cancelRequest(AutoExecutor.this); + alarm.addRequest(AutoExecutor.this, delay); + } + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/ide/WebServerManagerImpl.java b/platform/platform-impl/src/org/jetbrains/ide/WebServerManagerImpl.java index a95f58cfe031..fb82beb96ec8 100644 --- a/platform/platform-impl/src/org/jetbrains/ide/WebServerManagerImpl.java +++ b/platform/platform-impl/src/org/jetbrains/ide/WebServerManagerImpl.java @@ -72,4 +72,4 @@ class WebServerManagerImpl extends WebServerManager { public String getComponentName() { return getClass().getName(); } -} +} \ No newline at end of file diff --git a/platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestManager.java b/platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestManager.java index 63c44f3d7f81..d053c6305d57 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestManager.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/autotest/AutoTestManager.java @@ -1,21 +1,17 @@ package com.intellij.execution.testframework.autotest; +import com.intellij.execution.AutoExecutor; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentManagerImpl; import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.DocumentAdapter; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.impl.PsiAwareFileEditorManagerImpl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.content.Content; import com.intellij.util.Alarm; +import com.intellij.util.Consumer; import com.intellij.util.containers.WeakList; import java.util.Collection; @@ -24,14 +20,9 @@ import java.util.Collection; * @author yole */ public class AutoTestManager { - private final Project myProject; - private final PsiAwareFileEditorManagerImpl myManager; - private final Alarm myAutoTestAlarm; + private final AutoExecutor myAutoExecutor; private static final int AUTOTEST_DELAY = 10000; - private final Runnable myRunTestsRunnable; - private boolean myListenerAttached; - private final MyDocumentAdapter myListener; public static AutoTestManager getInstance(Project project) { return ServiceManager.getService(project, AutoTestManager.class); @@ -39,18 +30,21 @@ public class AutoTestManager { private final Collection myEnabledDescriptors = new WeakList(); - public AutoTestManager(Project project, FileEditorManager manager) { - myProject = project; - myManager = manager instanceof PsiAwareFileEditorManagerImpl ? - (PsiAwareFileEditorManagerImpl)manager : - null; - myAutoTestAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project); - myRunTestsRunnable = new Runnable() { - public void run() { - runAutoTests(); + public AutoTestManager(Project project) { + myAutoExecutor = new AutoExecutor(project, new Alarm(Alarm.ThreadToUse.SWING_THREAD, project), AUTOTEST_DELAY, new Consumer() { + @Override + public void consume(VirtualFile[] files) { + for (Content content : myEnabledDescriptors) { + runAutoTest(content); + } } - }; - myListener = new MyDocumentAdapter(); + }, new Condition() { + @Override + public boolean value(VirtualFile file) { + // Vladimir.Krivosheev — I don't know, why AutoTestManager checks it, but old behavior is preserved + return FileEditorManager.getInstance(myAutoExecutor.getProject()).isFileOpen(file); + } + }); } public void setAutoTestEnabled(RunContentDescriptor descriptor, boolean enabled) { @@ -59,16 +53,12 @@ public class AutoTestManager { if (!myEnabledDescriptors.contains(content)) { myEnabledDescriptors.add(content); } - if (!myListenerAttached) { - myListenerAttached = true; - EditorFactory.getInstance().getEventMulticaster().addDocumentListener(myListener, myProject); - } + myAutoExecutor.activate(); } else { myEnabledDescriptors.remove(content); - if (myEnabledDescriptors.isEmpty() && myListenerAttached) { - myListenerAttached = false; - EditorFactory.getInstance().getEventMulticaster().removeDocumentListener(myListener); + if (myEnabledDescriptors.isEmpty()) { + myAutoExecutor.deactivate(); } } } @@ -77,20 +67,6 @@ public class AutoTestManager { return myEnabledDescriptors.contains(descriptor.getAttachedContent()); } - public void runAutoTests() { - if (myManager != null) { - for (FileEditor editor : myManager.getAllEditors()) { - final VirtualFile file = myManager.getFile(editor); - if (file != null && myManager.isProblem(file)) { - return; - } - } - } - for (Content content : myEnabledDescriptors) { - runAutoTest(content); - } - } - private static void runAutoTest(Content content) { RunContentDescriptor descriptor = RunContentManagerImpl.getRunContentDescriptorByContent(content); if (descriptor == null) { @@ -106,18 +82,4 @@ public class AutoTestManager { } restarter.run(); } - - private class MyDocumentAdapter extends DocumentAdapter { - public void documentChanged(DocumentEvent event) { - final Document document = event.getDocument(); - final VirtualFile vFile = FileDocumentManager.getInstance().getFile(document); - if (vFile != null) { - final FileEditor[] editors = FileEditorManager.getInstance(myProject).getEditors(vFile); - if (editors.length > 0) { - myAutoTestAlarm.cancelAllRequests(); - myAutoTestAlarm.addRequest(myRunTestsRunnable, AUTOTEST_DELAY); - } - } - } - } } \ No newline at end of file diff --git a/platform/testRunner/src/com/intellij/execution/testframework/autotest/ToggleAutoTestAction.java b/platform/testRunner/src/com/intellij/execution/testframework/autotest/ToggleAutoTestAction.java index 0d40afa098ef..b6f3cadfb0ab 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/autotest/ToggleAutoTestAction.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/autotest/ToggleAutoTestAction.java @@ -21,9 +21,7 @@ public class ToggleAutoTestAction extends ToggleAction { public boolean isSelected(AnActionEvent e) { Project project = e.getData(PlatformDataKeys.PROJECT); RunContentDescriptor descriptor = e.getData(RunContentManager.RUN_CONTENT_DESCRIPTOR); - return project == null || descriptor == null - ? false - : AutoTestManager.getInstance(project).isAutoTestEnabled(descriptor); + return !(project == null || descriptor == null) && AutoTestManager.getInstance(project).isAutoTestEnabled(descriptor); } @Override