package com.intellij.javascript.karma.server.watch;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.regex.Pattern;
public class KarmaWatchPattern {
private static final Logger LOG = Logger.getInstance(KarmaWatchPattern.class);
private static final Pattern[] BASE_DIR_PATTERNS = new Pattern[] {
// /path/to/{,model/}*.js',
Pattern.compile("/[^/]*\\{.*\\}.*$"),
// /path/to/*.js
Pattern.compile("/[^/]*\\*.*$"),
// /path/to/!(...)
Pattern.compile("/[^/]*!\\(.*$"),
// /path/to/+(...)
Pattern.compile("/[^/]*\\+\\(.*$"),
// /path/to/(...)?
Pattern.compile("/[^/]*\\)\\?.*$"),
};
private final LocalFileSystem myFileSystem;
private final KarmaChangedFilesManager myChangedFileManager;
private final String myVfsPath;
private final String myBasePathDir;
private final boolean myCheckBasePathDir;
private LocalFileSystem.WatchRequest myWatchRequest;
private VirtualFile myRoot;
private String myRootPath;
KarmaWatchPattern(@NotNull LocalFileSystem fileSystem,
@NotNull KarmaChangedFilesManager changedFilesManager,
@NotNull final String pattern) {
LOG.info("Start watching path pattern " + pattern);
myFileSystem = fileSystem;
myChangedFileManager = changedFilesManager;
myVfsPath = pattern.replace(File.separatorChar, KarmaWatchSession.SEPARATOR_CHAR);
String baseDirPath = extractBaseDir(myVfsPath);
if (baseDirPath.isEmpty()) {
baseDirPath = KarmaWatchSession.SEPARATOR;
}
myBasePathDir = baseDirPath;
myCheckBasePathDir = !myVfsPath.equals(myBasePathDir);
update(false);
}
@NotNull
public static String extractBaseDir(@NotNull String filePattern) {
for (Pattern pattern : BASE_DIR_PATTERNS) {
filePattern = pattern.matcher(filePattern).replaceFirst("");
}
return filePattern;
}
public void update(boolean rescan) {
boolean noRootBefore = false;
boolean rootValid = myRoot != null && myRoot.isValid();
if (rootValid) {
String path = myRoot.getPath();
if (!path.equals(myRootPath)) {
rootValid = false;
if (myRootPath != null) {
VfsUtilCore.visitChildrenRecursively(myRoot, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (!file.isDirectory()) {
String subPath = VfsUtilCore.getRelativePath(file, myRoot, KarmaWatchSession.SEPARATOR_CHAR);
if (subPath != null) {
myChangedFileManager.onFileRemoved(KarmaWatchSession.join(myRootPath, subPath));
}
}
return true;
}
});
}
}
}
if (!rootValid) {
noRootBefore = true;
myRoot = null;
myRootPath = null;
stopWatching();
final VirtualFile file = myFileSystem.findFileByPath(myVfsPath);
if (file != null && file.isValid()) {
myRoot = file;
}
else if (myCheckBasePathDir) {
final VirtualFile baseDir = myFileSystem.findFileByPath(myBasePathDir);
if (baseDir != null && baseDir.isValid()) {
myRoot = baseDir;
}
}
if (myRoot != null) {
myRootPath = myRoot.getPath();
}
}
if (myRoot == null) {
LOG.warn("[Karma watch] Can not find vfs root for " + myBasePathDir);
return;
}
if (myWatchRequest == null) {
myWatchRequest = watchRoot(myRoot, rescan && noRootBefore);
LOG.info("Watching " + myRoot.getPath());
}
}
@Nullable
private LocalFileSystem.WatchRequest watchRoot(@NotNull final VirtualFile dir, final boolean reportChildren) {
VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (reportChildren && !file.isDirectory()) {
myChangedFileManager.onFileAdded(file.getPath());
}
file.getChildren();
return true;
}
});
LocalFileSystem.WatchRequest watchRequest = myFileSystem.addRootToWatch(dir.getPath(), true);
if (watchRequest == null) {
LOG.warn("Can not watch valid directory " + dir.getPath());
}
return watchRequest;
}
public void stopWatching() {
LocalFileSystem.WatchRequest watchRequest = myWatchRequest;
if (watchRequest != null) {
myFileSystem.removeWatchedRoot(watchRequest);
}
myWatchRequest = null;
}
@Nullable
public String findWatchedOriginalPath(@NotNull VirtualFile file) {
VirtualFile root = myRoot;
if (root != null && VfsUtilCore.isAncestor(root, file, false)) {
String filePath = file.getPath();
if (SystemInfo.isWindows) {
String originalRoot = myCheckBasePathDir ? myBasePathDir : myVfsPath;
if (StringUtil.startsWithIgnoreCase(filePath, originalRoot)) {
return originalRoot + filePath.substring(originalRoot.length());
}
}
return filePath;
}
return null;
}
}