blob: 7d7cb6aff2df9ca9b61ffc1df6ccfdc921f0ae8f [file] [log] [blame]
/*
* Copyright 2000-2012 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.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.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 com.intellij.util.messages.MessageBusConnection;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class DelayedDocumentWatcher implements Runnable {
private final Project myProject;
private final Alarm myAlarm;
private final Consumer<VirtualFile[]> myConsumer;
private final int myDelay;
private final MyDocumentAdapter myListener;
private final Set<VirtualFile> myChangedFiles = new THashSet<VirtualFile>();
private boolean myWasRequested;
private final Condition<VirtualFile> myDocumentChangedFilter;
private MessageBusConnection myMessageBusConnection;
public DelayedDocumentWatcher(Project project,
Alarm alarm,
int delay,
Consumer<VirtualFile[]> consumer,
Condition<VirtualFile> documentChangedFilter) {
myProject = project;
myAlarm = alarm;
myDelay = delay;
myConsumer = consumer;
myDocumentChangedFilter = documentChangedFilter;
myListener = new MyDocumentAdapter();
}
@Override
public void run() {
final VirtualFile[] files;
synchronized (myChangedFiles) {
myWasRequested = false;
files = myChangedFiles.toArray(new VirtualFile[myChangedFiles.size()]);
myChangedFiles.clear();
}
final WolfTheProblemSolver problemSolver = WolfTheProblemSolver.getInstance(myProject);
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 (myChangedFiles) {
Collections.addAll(myChangedFiles, files);
}
return;
}
}
myConsumer.consume(files);
}
public Project getProject() {
return myProject;
}
public void activate() {
if (myMessageBusConnection != null) {
return;
}
myMessageBusConnection = myProject.getMessageBus().connect(myProject);
myMessageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
for (VFileEvent event : events) {
if (event instanceof VFileDeleteEvent) {
synchronized (myChangedFiles) {
myChangedFiles.remove(event.getFile());
}
}
}
}
});
EditorFactory.getInstance().getEventMulticaster().addDocumentListener(myListener, myProject);
}
public void deactivate() {
if (myMessageBusConnection == null) {
return;
}
try {
EditorFactory.getInstance().getEventMulticaster().removeDocumentListener(myListener);
}
finally {
myMessageBusConnection.disconnect();
myMessageBusConnection = null;
}
}
private class MyDocumentAdapter extends DocumentAdapter {
@Override
public void documentChanged(DocumentEvent event) {
final Document document = event.getDocument();
final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
if (file == null || !myDocumentChangedFilter.value(file)) {
return;
}
synchronized (myChangedFiles) {
// changedFiles contains is not enough, because it can contain not-flushed files from prev request (which are not flushed because some is invalid)
if (!myChangedFiles.add(file) && myWasRequested) {
return;
}
}
myAlarm.cancelRequest(DelayedDocumentWatcher.this);
myAlarm.addRequest(DelayedDocumentWatcher.this, myDelay);
}
}
}