platform: detect case-only name changes

This commit is contained in:
Roman Shevchenko
2013-09-18 14:38:56 +04:00
parent e19b8201a6
commit cae87182bb
4 changed files with 90 additions and 11 deletions
@@ -30,17 +30,15 @@ import com.intellij.openapi.vfs.newvfs.events.*;
import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile;
import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.OpenTHashSet;
import com.intellij.util.containers.Queue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.*;
import static com.intellij.openapi.diagnostic.LogUtil.debug;
import static com.intellij.util.containers.ContainerUtil.newHashSet;
import static com.intellij.util.containers.ContainerUtil.newTroveSet;
/**
* @author max
@@ -122,10 +120,14 @@ public class RefreshWorker {
if (fullSync) {
String[] currentNames = persistence.list(file);
String[] upToDateNames = VfsUtil.filterNames(fs.list(file));
Set<String> newNames = newHashSet(upToDateNames);
Set<String> newNames = newTroveSet(FileUtil.PATH_HASHING_STRATEGY, upToDateNames);
ContainerUtil.removeAll(newNames, currentNames);
Set<String> deletedNames = newHashSet(currentNames);
Set<String> deletedNames = newTroveSet(FileUtil.PATH_HASHING_STRATEGY, currentNames);
ContainerUtil.removeAll(deletedNames, upToDateNames);
OpenTHashSet<String> actualNames = null;
if (!SystemInfo.isFileSystemCaseSensitive) {
actualNames = new OpenTHashSet<String>(FileUtil.PATH_HASHING_STRATEGY, upToDateNames);
}
debug(LOG, "current=%s +%s -%s", currentNames, newNames, deletedNames);
for (String name : deletedNames) {
@@ -149,6 +151,7 @@ public class RefreshWorker {
FileAttributes childAttributes = fs.getAttributes(child);
if (childAttributes != null) {
checkAndScheduleChildRefresh(file, child, childAttributes);
checkAndScheduleFileNameChange(actualNames, child);
}
else {
LOG.warn("fs=" + fs + " dir=" + file + " name=" + child.getName());
@@ -159,12 +162,18 @@ public class RefreshWorker {
}
else {
Collection<VirtualFile> cachedChildren = file.getCachedChildren();
debug(LOG, "cached=%s", cachedChildren);
OpenTHashSet<String> actualNames = null;
if (!SystemInfo.isFileSystemCaseSensitive) {
actualNames = new OpenTHashSet<String>(FileUtil.PATH_HASHING_STRATEGY, VfsUtil.filterNames(fs.list(file)));
}
debug(LOG, "cached=%s actual=%s", cachedChildren, actualNames);
for (VirtualFile child : cachedChildren) {
checkCancelled();
FileAttributes childAttributes = fs.getAttributes(child);
if (childAttributes != null) {
checkAndScheduleChildRefresh(file, child, childAttributes);
checkAndScheduleFileNameChange(actualNames, child);
}
else {
scheduleDeletion(child);
@@ -223,6 +232,16 @@ public class RefreshWorker {
}
}
private void checkAndScheduleFileNameChange(@Nullable OpenTHashSet<String> actualNames, VirtualFile child) {
if (actualNames != null) {
String currentName = child.getName();
String actualName = actualNames.get(currentName);
if (!currentName.equals(actualName)) {
scheduleAttributeChange(child, VirtualFile.PROP_NAME, currentName, actualName);
}
}
}
private static class RefreshCancelledException extends RuntimeException { }
private void checkCancelled() {
@@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.impl.local.FileWatcher;
import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl;
@@ -651,6 +652,28 @@ public class FileWatcherTest extends PlatformLangTestCase {
}
}
public void testFileCaseChange() throws Exception {
if (SystemInfo.isFileSystemCaseSensitive) {
System.err.println("Ignored: case-insensitive FS required");
return;
}
File topDir = createTestDir("topDir");
File testFile = createTestFile(topDir, "file.txt", "123");
refresh(topDir);
LocalFileSystem.WatchRequest request = watch(topDir);
try {
myAccept = true;
File newFile = new File(testFile.getParent(), StringUtil.capitalize(testFile.getName()));
FileUtil.rename(testFile, newFile);
assertEvent(VFilePropertyChangeEvent.class, newFile.getPath());
}
finally {
unwatch(request);
}
}
@NotNull
private LocalFileSystem.WatchRequest watch(File watchFile) {
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.IoTestUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
@@ -402,4 +403,35 @@ public class LocalFileSystemTest extends PlatformLangTestCase {
assertTrue(topDir.exists());
assertEquals(2, topDir.getChildren().length);
}
public void testFileCaseChange() throws Exception {
if (SystemInfo.isFileSystemCaseSensitive) {
System.err.println("Ignored: case-insensitive FS required");
return;
}
File top = createTempDirectory(false);
File file = IoTestUtil.createTestFile(top, "file.txt", "test");
LocalFileSystem lfs = LocalFileSystem.getInstance();
VirtualFile topDir = lfs.refreshAndFindFileByIoFile(top);
assertNotNull(topDir);
VirtualFile sourceFile = lfs.refreshAndFindFileByIoFile(file);
assertNotNull(sourceFile);
String newName = StringUtil.capitalize(file.getName());
FileUtil.rename(file, new File(top, newName));
topDir.refresh(false, true);
assertFalse(((VirtualDirectoryImpl)topDir).allChildrenLoaded());
assertTrue(sourceFile.isValid());
assertEquals(newName, sourceFile.getName());
topDir.getChildren();
newName = newName.toLowerCase();
FileUtil.rename(file, new File(top, newName));
topDir.refresh(false, true);
assertTrue(((VirtualDirectoryImpl)topDir).allChildrenLoaded());
assertTrue(sourceFile.isValid());
assertEquals(newName, sourceFile.getName());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
/**
@@ -60,13 +61,18 @@ public class OpenTHashSet<T> extends THashSet<T> {
super(ts, strategy);
}
public OpenTHashSet(final TObjectHashingStrategy<T> strategy, final T... ts) {
super(Arrays.asList(ts), strategy);
}
@Override
public int index(final T obj) {
return super.index(obj);
}
public T get(final int index) {
return (T)_set[index];
@SuppressWarnings("unchecked") T t = (T)_set[index];
return t;
}
/**
@@ -77,5 +83,4 @@ public class OpenTHashSet<T> extends THashSet<T> {
final int index = index(obj);
return index < 0 ? null : get(index);
}
}