mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
Binary file not shown.
Binary file not shown.
+5
-4
@@ -343,11 +343,12 @@ public class UnusedDeclarationInspection extends FilteringInspectionTool {
|
||||
final RefElementImpl refElement = (RefElementImpl)refEntity;
|
||||
if (!refElement.isSuspicious()) return;
|
||||
|
||||
final PsiElement element = refElement.getElement();
|
||||
if (element == null) return;
|
||||
PsiFile file = refElement.getContainingFile();
|
||||
|
||||
if (file == null) return;
|
||||
final boolean isSuppressed = refElement.isSuppressed(getShortName());
|
||||
if (!getContext().isToCheckMember(element, UnusedDeclarationInspection.this) || isSuppressed) {
|
||||
if (isSuppressed || !scope.contains(element)) {
|
||||
if (!getContext().isToCheckFile(file, UnusedDeclarationInspection.this) || isSuppressed) {
|
||||
if (isSuppressed || !scope.contains(file)) {
|
||||
getEntryPointsManager().addEntryPoint(refElement, false);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -24,6 +24,8 @@
|
||||
// logging
|
||||
void userlog(int priority, const char* format, ...);
|
||||
|
||||
#define CHECK_NULL(p, r) if (p == NULL) { userlog(LOG_ERR, "out of memory"); return r; }
|
||||
|
||||
|
||||
// variable-length array
|
||||
typedef struct __array array;
|
||||
@@ -38,7 +40,7 @@ void array_delete(array* a);
|
||||
void array_delete_vs_data(array* a);
|
||||
|
||||
|
||||
// key/value pairs table
|
||||
// poor man's hash table
|
||||
typedef struct __table table;
|
||||
|
||||
table* table_create(int capacity);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -32,8 +32,6 @@
|
||||
|
||||
#define DEFAULT_SUBDIR_COUNT 5
|
||||
|
||||
#define CHECK_NULL(p) if (p == NULL) { userlog(LOG_ERR, "out of memory"); return ERR_ABORT; }
|
||||
|
||||
typedef struct __watch_node {
|
||||
char* name;
|
||||
int wd;
|
||||
@@ -153,9 +151,9 @@ static int add_watch(const char* path, watch_node* parent) {
|
||||
|
||||
node = malloc(sizeof(watch_node));
|
||||
|
||||
CHECK_NULL(node);
|
||||
CHECK_NULL(node, ERR_ABORT);
|
||||
node->name = strdup(path);
|
||||
CHECK_NULL(node->name);
|
||||
CHECK_NULL(node->name, ERR_ABORT);
|
||||
node->wd = wd;
|
||||
node->parent = parent;
|
||||
node->kids = NULL;
|
||||
@@ -163,9 +161,9 @@ static int add_watch(const char* path, watch_node* parent) {
|
||||
if (parent != NULL) {
|
||||
if (parent->kids == NULL) {
|
||||
parent->kids = array_create(DEFAULT_SUBDIR_COUNT);
|
||||
CHECK_NULL(parent->kids);
|
||||
CHECK_NULL(parent->kids, ERR_ABORT);
|
||||
}
|
||||
CHECK_NULL(array_push(parent->kids, node));
|
||||
CHECK_NULL(array_push(parent->kids, node), ERR_ABORT);
|
||||
}
|
||||
|
||||
if (table_put(watches, wd, node) == NULL) {
|
||||
@@ -238,25 +236,31 @@ static bool is_ignored(const char* path, array* ignores) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int walk_tree(const char* path, watch_node* parent, array* ignores) {
|
||||
static int walk_tree(const char* path, watch_node* parent, array* ignores, bool recursive) {
|
||||
if (is_ignored(path, ignores)) {
|
||||
return ERR_IGNORE;
|
||||
}
|
||||
|
||||
DIR* dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
if (errno == EACCES) {
|
||||
return ERR_IGNORE;
|
||||
DIR* dir;
|
||||
if (recursive) {
|
||||
dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
if (errno == EACCES) {
|
||||
return ERR_IGNORE;
|
||||
}
|
||||
else if (errno == ENOTDIR) { // "future" root
|
||||
return add_watch(path, parent);
|
||||
}
|
||||
userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno));
|
||||
return ERR_CONTINUE;
|
||||
}
|
||||
else if (errno == ENOTDIR) { // flat root
|
||||
return add_watch(path, parent);
|
||||
}
|
||||
userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno));
|
||||
return ERR_CONTINUE;
|
||||
}
|
||||
|
||||
int id = add_watch(path, parent);
|
||||
if (id < 0) {
|
||||
if (!recursive) {
|
||||
return id;
|
||||
}
|
||||
else if (id < 0) {
|
||||
closedir(dir);
|
||||
return id;
|
||||
}
|
||||
@@ -279,7 +283,7 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int subdir_id = walk_tree(subdir, table_get(watches, id), ignores);
|
||||
int subdir_id = walk_tree(subdir, table_get(watches, id), ignores, recursive);
|
||||
if (subdir_id < 0 && subdir_id != ERR_IGNORE) {
|
||||
rm_watch(id, true);
|
||||
id = subdir_id;
|
||||
@@ -293,9 +297,15 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores) {
|
||||
|
||||
|
||||
int watch(const char* root, array* ignores) {
|
||||
bool recursive = true;
|
||||
if (root[0] == '|') {
|
||||
root++;
|
||||
recursive = false;
|
||||
}
|
||||
|
||||
char buf[PATH_MAX];
|
||||
const char* normalized = realpath(root, buf);
|
||||
return walk_tree((normalized != NULL ? normalized : root), NULL, ignores);
|
||||
return walk_tree((normalized != NULL ? normalized : root), NULL, ignores, recursive);
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +333,7 @@ static bool process_inotify_event(struct inotify_event* event) {
|
||||
}
|
||||
|
||||
if ((event->mask & IN_CREATE || event->mask & IN_MOVED_TO) && event->mask & IN_ISDIR) {
|
||||
int result = walk_tree(path, node, NULL);
|
||||
int result = walk_tree(path, node, NULL, true);
|
||||
if (result < 0 && result != ERR_IGNORE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -33,7 +33,7 @@
|
||||
#define LOG_ENV_ERROR "error"
|
||||
#define LOG_ENV_OFF "off"
|
||||
|
||||
#define VERSION "1.0"
|
||||
#define VERSION "1.1"
|
||||
#define VERSION_MSG "fsnotifier " VERSION "\n"
|
||||
|
||||
#define USAGE_MSG \
|
||||
@@ -58,11 +58,8 @@ typedef struct {
|
||||
static array* roots = NULL;
|
||||
|
||||
static bool show_warning = true;
|
||||
|
||||
static bool self_test = false;
|
||||
|
||||
#define CHECK_NULL(p) if (p == NULL) { userlog(LOG_ERR, "out of memory"); return false; }
|
||||
|
||||
static void init_log();
|
||||
static void run_self_test();
|
||||
static void main_loop();
|
||||
@@ -223,7 +220,7 @@ static bool read_input() {
|
||||
|
||||
if (strcmp(line, "ROOTS") == 0) {
|
||||
array* new_roots = array_create(20);
|
||||
CHECK_NULL(new_roots);
|
||||
CHECK_NULL(new_roots, false);
|
||||
|
||||
while (1) {
|
||||
line = read_line(stdin);
|
||||
@@ -235,12 +232,9 @@ static bool read_input() {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (line[0] == '|') line++; // flat roots will be differentiated later
|
||||
|
||||
int l = strlen(line);
|
||||
if (l > 1 && line[l-1] == '/') line[l-1] = '\0';
|
||||
|
||||
CHECK_NULL(array_push(new_roots, strdup(line)));
|
||||
CHECK_NULL(array_push(new_roots, strdup(line)), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +260,7 @@ static bool update_roots(array* new_roots) {
|
||||
}
|
||||
|
||||
array* unwatchable = array_create(20);
|
||||
CHECK_NULL(unwatchable);
|
||||
CHECK_NULL(unwatchable, false);
|
||||
if (!unwatchable_mounts(unwatchable)) {
|
||||
return false;
|
||||
}
|
||||
@@ -312,10 +306,10 @@ static bool register_roots(array* new_roots, array* unwatchable) {
|
||||
}
|
||||
else if (id >= 0) {
|
||||
watch_root* root = malloc(sizeof(watch_root));
|
||||
CHECK_NULL(root);
|
||||
CHECK_NULL(root, false);
|
||||
root->id = id;
|
||||
root->name = new_root;
|
||||
CHECK_NULL(array_push(roots, root));
|
||||
CHECK_NULL(array_push(roots, root), false);
|
||||
}
|
||||
else {
|
||||
if (show_warning && watch_limit_reached()) {
|
||||
@@ -324,7 +318,7 @@ static bool register_roots(array* new_roots, array* unwatchable) {
|
||||
output("MESSAGE\n" INOTIFY_LIMIT_MSG, limit);
|
||||
show_warning = false; // warn only once
|
||||
}
|
||||
CHECK_NULL(array_push(unwatchable, new_root));
|
||||
CHECK_NULL(array_push(unwatchable, new_root), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +356,7 @@ static bool unwatchable_mounts(array* mounts) {
|
||||
}
|
||||
|
||||
if (!is_watchable(dev, point, fs)) {
|
||||
CHECK_NULL(array_push(mounts, strdup(point)));
|
||||
CHECK_NULL(array_push(mounts, strdup(point)), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.intellij.codeInspection.ex;
|
||||
import com.intellij.codeHighlighting.HighlightDisplayLevel;
|
||||
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.codeInspection.LocalDummyInspectionTool;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.psi.search.scope.packageSet.NamedScope;
|
||||
@@ -43,6 +44,7 @@ public class Descriptor {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.Descriptor");
|
||||
private final ScopeToolState myState;
|
||||
private final InspectionProfileImpl myInspectionProfile;
|
||||
private boolean myShouldBeShown = true;
|
||||
|
||||
public Descriptor(ScopeToolState pair, InspectionProfileImpl inspectionProfile) {
|
||||
myState = pair;
|
||||
@@ -56,6 +58,14 @@ public class Descriptor {
|
||||
myEnabled = ((InspectionProfileImpl)inspectionProfile).isToolEnabled(myKey, pair.getScope());
|
||||
myTool = tool;
|
||||
myScope = pair.getScope();
|
||||
|
||||
if (tool instanceof InspectionToolWrapper) {
|
||||
InspectionProfileEntry inspection = ((InspectionToolWrapper)tool).getTool();
|
||||
|
||||
if (inspection instanceof LocalDummyInspectionTool) {
|
||||
myShouldBeShown = ((LocalDummyInspectionTool)inspection).shouldBeShownInInspectionProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
@@ -80,6 +90,10 @@ public class Descriptor {
|
||||
myEnabled = enabled;
|
||||
}
|
||||
|
||||
public boolean shouldBeShown() {
|
||||
return myShouldBeShown;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
+8
-6
@@ -71,7 +71,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class GlobalInspectionContextImpl extends UserDataHolderBase implements GlobalInspectionContext {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl");
|
||||
@@ -346,16 +349,15 @@ public class GlobalInspectionContextImpl extends UserDataHolderBase implements G
|
||||
|
||||
|
||||
public boolean isToCheckMember(@NotNull RefElement owner, InspectionProfileEntry tool) {
|
||||
final PsiElement element = owner.getElement();
|
||||
return isToCheckMember(element, tool) && !((RefElementImpl)owner).isSuppressed(tool.getShortName());
|
||||
return isToCheckFile(((RefElementImpl)owner).getContainingFile(), tool) && !((RefElementImpl)owner).isSuppressed(tool.getShortName());
|
||||
}
|
||||
|
||||
public boolean isToCheckMember(final PsiElement element, final InspectionProfileEntry tool) {
|
||||
public boolean isToCheckFile(PsiFile file, final InspectionProfileEntry tool) {
|
||||
final Tools tools = myTools.get(tool.getShortName());
|
||||
if (tools != null) {
|
||||
for (ScopeToolState state : tools.getTools()) {
|
||||
final NamedScope namedScope = state.getScope(element.getProject());
|
||||
if (namedScope == null || namedScope.getValue().contains(element.getContainingFile(), getCurrentProfile().getProfileManager().getScopesManager())) {
|
||||
final NamedScope namedScope = state.getScope(file.getProject());
|
||||
if (namedScope == null || namedScope.getValue().contains(file, getCurrentProfile().getProfileManager().getScopesManager())) {
|
||||
if (state.isEnabled()) {
|
||||
final InspectionProfileEntry entry = state.getTool();
|
||||
if (entry instanceof InspectionToolWrapper && ((InspectionToolWrapper)entry).getTool() == tool) return true;
|
||||
|
||||
@@ -444,10 +444,6 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel,
|
||||
return false;
|
||||
}
|
||||
for (InspectionToolWrapper tool : tools) {
|
||||
if (tool.getTool() instanceof LocalDummyInspectionTool && !((LocalDummyInspectionTool)tool.getTool()).shouldBeShownInInspectionProfile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String shortName = tool.getShortName();
|
||||
HighlightDisplayKey key = HighlightDisplayKey.find(shortName);
|
||||
if (key == null) {
|
||||
|
||||
+3
-9
@@ -21,14 +21,8 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel;
|
||||
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
|
||||
import com.intellij.codeInspection.InspectionProfile;
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.ModifiableModel;
|
||||
import com.intellij.codeInspection.ex.Descriptor;
|
||||
import com.intellij.codeInspection.ex.InspectionProfileImpl;
|
||||
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
|
||||
import com.intellij.codeInspection.ex.ScopeToolState;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.ex.*;
|
||||
import com.intellij.ide.CommonActionsManager;
|
||||
import com.intellij.ide.DefaultTreeExpander;
|
||||
import com.intellij.ide.IdeTooltipManager;
|
||||
@@ -685,7 +679,7 @@ public class SingleInspectionProfilePanel extends JPanel {
|
||||
keySetList.addAll(SearchUtil.findKeys(filter, quated));
|
||||
}
|
||||
for (Descriptor descriptor : myDescriptors.keySet()) {
|
||||
if (filter != null && filter.length() > 0 && !isDescriptorAccepted(descriptor, filter, forceInclude, keySetList, quated)) {
|
||||
if (!descriptor.shouldBeShown() || filter != null && filter.length() > 0 && !isDescriptorAccepted(descriptor, filter, forceInclude, keySetList, quated)) {
|
||||
continue;
|
||||
}
|
||||
final List<ScopeToolState> nonDefaultTools = mySelectedProfile.getNonDefaultTools(descriptor.getKey().toString());
|
||||
|
||||
@@ -173,6 +173,9 @@ public abstract class BaseRefactoringProcessor {
|
||||
}
|
||||
catch (Throwable e) {
|
||||
anyException.set(Boolean.TRUE);
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,34 +20,32 @@ import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileSystemUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.ManagingFS;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
|
||||
import com.intellij.openapi.vfs.watcher.ChangeKind;
|
||||
import com.intellij.util.PairFunction;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class FileWatcher {
|
||||
@NonNls public static final String PROPERTY_WATCHER_DISABLED = "filewatcher.disabled";
|
||||
@NonNls private static final String PROPERTY_WATCHER_EXECUTABLE_PATH = "idea.filewatcher.executable.path";
|
||||
@NonNls public static final String PROPERTY_WATCHER_DISABLED = "idea.filewatcher.disabled";
|
||||
@NonNls public static final String PROPERTY_WATCHER_EXECUTABLE_PATH = "idea.filewatcher.executable.path";
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.local.FileWatcher");
|
||||
|
||||
@@ -59,15 +57,7 @@ public class FileWatcher {
|
||||
@NonNls private static final String EXIT_COMMAND = "EXIT";
|
||||
@NonNls private static final String MESSAGE_COMMAND = "MESSAGE";
|
||||
|
||||
private static final PairFunction<String,String,Boolean> PATH_COMPARATOR = new PairFunction<String, String, Boolean>() {
|
||||
@Override
|
||||
public Boolean fun(final String s1, final String s2) {
|
||||
return SystemInfo.isFileSystemCaseSensitive ? s1.equals(s2) : s1.equalsIgnoreCase(s2);
|
||||
}
|
||||
};
|
||||
|
||||
private final Object LOCK = new Object();
|
||||
private final Lock SET_ROOTS_LOCK = new ReentrantLock(true);
|
||||
|
||||
private List<String> myDirtyPaths = new ArrayList<String>();
|
||||
private List<String> myDirtyRecursivePaths = new ArrayList<String>();
|
||||
@@ -75,21 +65,23 @@ public class FileWatcher {
|
||||
private List<String> myManualWatchRoots = new ArrayList<String>();
|
||||
|
||||
private final List<Pair<String, String>> myMapping = new ArrayList<Pair<String, String>>();
|
||||
private List<Pair<String, String>> myCanonicalMapping = new ArrayList<Pair<String, String>>();
|
||||
|
||||
private List<String> myRecursiveWatchRoots = new ArrayList<String>();
|
||||
private List<String> myFlatWatchRoots = new ArrayList<String>();
|
||||
|
||||
private final Collection<String> myAllPaths = new ArrayList<String>(2);
|
||||
private final Collection<String> myWatchedPaths = new ArrayList<String>(2);
|
||||
|
||||
private File executable;
|
||||
private volatile Process notifierProcess;
|
||||
private volatile BufferedReader notifierReader;
|
||||
private volatile BufferedWriter notifierWriter;
|
||||
|
||||
private volatile BufferedWriter notifierWriter;
|
||||
private boolean myFailureShownToTheUser = false;
|
||||
private int attemptCount = 0;
|
||||
private static final int MAX_PROCESS_LAUNCH_ATTEMPT_COUNT = 10;
|
||||
private boolean isShuttingDown = false;
|
||||
private final ManagingFS myManagingFS;
|
||||
|
||||
private final ManagingFS myManagingFS;
|
||||
private static final FileWatcher ourInstance = new FileWatcher();
|
||||
|
||||
public static FileWatcher getInstance() {
|
||||
@@ -140,7 +132,6 @@ public class FileWatcher {
|
||||
myDirtyRecursivePaths = new ArrayList<String>();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<String> getDirtyDirs() {
|
||||
@@ -158,26 +149,16 @@ public class FileWatcher {
|
||||
}
|
||||
|
||||
public void setWatchRoots(final List<String> recursive, final List<String> flat) {
|
||||
SET_ROOTS_LOCK.lock();
|
||||
try {
|
||||
synchronized (LOCK) {
|
||||
if (myRecursiveWatchRoots.equals(recursive) && myFlatWatchRoots.equals(flat)) return;
|
||||
}
|
||||
|
||||
final List<Pair<String, String>> mapping = new ArrayList<Pair<String, String>>();
|
||||
long t = System.nanoTime();
|
||||
final List<String> checkedRecursive = checkPaths(recursive, mapping);
|
||||
final List<String> checkedFlat = checkPaths(flat, mapping);
|
||||
t = (System.nanoTime() - t) / 1000;
|
||||
LOG.info((recursive.size() + flat.size()) + " paths checked, " + mapping.size() + " mapped, " + t + " mks");
|
||||
synchronized (LOCK) {
|
||||
if (myRecursiveWatchRoots.equals(recursive) && myFlatWatchRoots.equals(flat)) return;
|
||||
|
||||
if (isAlive()) {
|
||||
try {
|
||||
writeLine(ROOTS_COMMAND);
|
||||
for (String path : checkedRecursive) {
|
||||
for (String path : recursive) {
|
||||
writeLine(path);
|
||||
}
|
||||
for (String path : checkedFlat) {
|
||||
for (String path : flat) {
|
||||
writeLine("|" + path);
|
||||
}
|
||||
writeLine("#");
|
||||
@@ -187,37 +168,10 @@ public class FileWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (LOCK) {
|
||||
myRecursiveWatchRoots = recursive;
|
||||
myFlatWatchRoots = flat;
|
||||
myMapping.clear();
|
||||
myCanonicalMapping = mapping;
|
||||
}
|
||||
myRecursiveWatchRoots = recursive;
|
||||
myFlatWatchRoots = flat;
|
||||
myMapping.clear();
|
||||
}
|
||||
finally {
|
||||
SET_ROOTS_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> checkPaths(final List<String> paths, final List<Pair<String, String>> mapping) {
|
||||
if (!SystemInfo.areSymLinksSupported) return paths;
|
||||
|
||||
final List<String> checkedPaths = new ArrayList<String>(paths.size());
|
||||
for (String path : paths) {
|
||||
String watched = path;
|
||||
final String canonical = getCanonicalPath(path);
|
||||
//noinspection ConstantConditions
|
||||
if (!PATH_COMPARATOR.fun(path, canonical)) {
|
||||
mapping.add(Pair.create((watched = canonical), path));
|
||||
}
|
||||
checkedPaths.add(watched);
|
||||
}
|
||||
return checkedPaths;
|
||||
}
|
||||
|
||||
private static String getCanonicalPath(final String path) {
|
||||
final String realPath = FileSystemUtil.resolveSymLink(path);
|
||||
return realPath != null ? realPath : path;
|
||||
}
|
||||
|
||||
private boolean isAlive() {
|
||||
@@ -247,43 +201,34 @@ public class FileWatcher {
|
||||
|
||||
shutdownProcess();
|
||||
|
||||
String execPath = null;
|
||||
if (executable == null) {
|
||||
executable = getExecutable();
|
||||
|
||||
final String altExecPath = System.getProperty(PROPERTY_WATCHER_EXECUTABLE_PATH);
|
||||
if (altExecPath != null && new File(altExecPath).isFile()) {
|
||||
execPath = FileUtil.toSystemDependentName(altExecPath);
|
||||
}
|
||||
|
||||
if (execPath == null) {
|
||||
final String execName;
|
||||
execName = getExecutableName();
|
||||
if (execName == null) {
|
||||
if (executable == null) {
|
||||
myFailureShownToTheUser = true; // ignore unsupported platforms
|
||||
return;
|
||||
}
|
||||
execPath = PathManager.getBinPath() + File.separatorChar + execName;
|
||||
|
||||
if (!executable.exists()) {
|
||||
notifyOnFailure("File watcher is not found at path: " + executable, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!executable.canExecute()) {
|
||||
final String message = "File watcher is not executable: <a href=\"" + executable + "\">" + executable + "</a>";
|
||||
final File exec = executable;
|
||||
notifyOnFailure(message, new NotificationListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
|
||||
ShowFilePathAction.openFile(exec);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final File exec = new File(execPath);
|
||||
if (!exec.exists()) {
|
||||
notifyOnFailure("File watcher is not found at path: " + execPath, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!exec.canExecute()) {
|
||||
notifyOnFailure("File watcher is not executable: <a href=\"" + execPath + "\">" + execPath +"</a>", new NotificationListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
|
||||
ShowFilePathAction.openFile(exec);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Starting file watcher: " + execPath);
|
||||
|
||||
notifierProcess = Runtime.getRuntime().exec(new String[]{execPath});
|
||||
|
||||
LOG.info("Starting file watcher: " + executable);
|
||||
notifierProcess = Runtime.getRuntime().exec(executable.getAbsolutePath());
|
||||
notifierReader = new BufferedReader(new InputStreamReader(notifierProcess.getInputStream()));
|
||||
notifierWriter = new BufferedWriter(new OutputStreamWriter(notifierProcess.getOutputStream()));
|
||||
|
||||
@@ -299,15 +244,43 @@ public class FileWatcher {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getExecutableName() {
|
||||
private static File getExecutable() {
|
||||
String execPath = null;
|
||||
|
||||
final String altExecPath = System.getProperty(PROPERTY_WATCHER_EXECUTABLE_PATH);
|
||||
if (altExecPath != null && new File(altExecPath).isFile()) {
|
||||
execPath = FileUtil.toSystemDependentName(altExecPath);
|
||||
}
|
||||
|
||||
if (execPath == null) {
|
||||
final String execName = getExecutableName(false);
|
||||
if (execName == null) {
|
||||
return null;
|
||||
}
|
||||
execPath = FileUtil.join(PathManager.getBinPath(), execName);
|
||||
}
|
||||
|
||||
File exec = new File(execPath);
|
||||
if (!exec.exists()) {
|
||||
String homePath = PathManager.getHomePath();
|
||||
if (new File(homePath, "community").exists()) {
|
||||
homePath += File.separator + "community";
|
||||
}
|
||||
exec = new File(FileUtil.join(homePath, "bin", getExecutableName(true)));
|
||||
}
|
||||
return exec;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getExecutableName(final boolean withSubDir) {
|
||||
if (SystemInfo.isWindows) {
|
||||
return "fsnotifier.exe";
|
||||
return (withSubDir ? "win" + File.separator : "") + "fsnotifier.exe";
|
||||
}
|
||||
else if (SystemInfo.isMac) {
|
||||
return "fsnotifier";
|
||||
return (withSubDir ? "mac" + File.separator : "") + "fsnotifier";
|
||||
}
|
||||
else if (SystemInfo.isLinux) {
|
||||
return SystemInfo.isAMD64 ? "fsnotifier64" : "fsnotifier";
|
||||
return (withSubDir ? "linux" + File.separator : "") + (SystemInfo.isAMD64 ? "fsnotifier64" : "fsnotifier");
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -316,7 +289,8 @@ public class FileWatcher {
|
||||
private void notifyOnFailure(String cause, @Nullable NotificationListener listener) {
|
||||
if (!myFailureShownToTheUser) {
|
||||
myFailureShownToTheUser = true;
|
||||
Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "External file sync may be slow", cause, NotificationType.WARNING, listener));
|
||||
Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "External file sync may be slow",
|
||||
cause, NotificationType.WARNING, listener));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,6 +313,32 @@ public class FileWatcher {
|
||||
return notifierProcess != null;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void startup() throws IOException {
|
||||
final Application app = ApplicationManager.getApplication();
|
||||
assert app != null && app.isUnitTestMode() : app;
|
||||
|
||||
myFailureShownToTheUser = true;
|
||||
attemptCount = 0;
|
||||
startupProcess(false);
|
||||
attemptCount = 2 * MAX_PROCESS_LAUNCH_ATTEMPT_COUNT;
|
||||
if (notifierProcess != null) {
|
||||
new WatchForChangesThread().start();
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void shutdown() throws InterruptedException {
|
||||
final Application app = ApplicationManager.getApplication();
|
||||
assert app != null && app.isUnitTestMode() : app;
|
||||
|
||||
final Process process = notifierProcess;
|
||||
if (process != null) {
|
||||
shutdownProcess();
|
||||
process.waitFor();
|
||||
}
|
||||
}
|
||||
|
||||
private class WatchForChangesThread extends Thread {
|
||||
public WatchForChangesThread() {
|
||||
//noinspection HardCodedStringLiteral
|
||||
@@ -396,7 +396,7 @@ public class FileWatcher {
|
||||
final String pathB = readLine();
|
||||
if (pathB == null || "#".equals(pathB)) break;
|
||||
|
||||
pairs.add(new Pair<String, String>(ensureEndsWithSlash(pathA), ensureEndsWithSlash(pathB)));
|
||||
pairs.add(Pair.create(preparePathForMapping(pathA), preparePathForMapping(pathB)));
|
||||
}
|
||||
while (true);
|
||||
|
||||
@@ -414,10 +414,10 @@ public class FileWatcher {
|
||||
}
|
||||
|
||||
synchronized (LOCK) {
|
||||
final String watchedPath = checkWatchable(path);
|
||||
if (watchedPath != null) {
|
||||
final Collection<String> watchedPaths = checkWatchable(path);
|
||||
if (!watchedPaths.isEmpty()) {
|
||||
try {
|
||||
onPathChange(ChangeKind.valueOf(command), watchedPath);
|
||||
onPathChange(ChangeKind.valueOf(command), watchedPaths);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
LOG.error("Illegal watcher command: " + command);
|
||||
@@ -438,9 +438,9 @@ public class FileWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private static String ensureEndsWithSlash(String path) {
|
||||
if (path.endsWith("/") || path.endsWith(File.separator)) return path;
|
||||
return path + '/';
|
||||
private static String preparePathForMapping(final String path) {
|
||||
final String localPath = FileUtil.toSystemDependentName(path);
|
||||
return localPath.endsWith(File.separator) ? localPath : localPath + File.separator;
|
||||
}
|
||||
|
||||
private void writeLine(String line) throws IOException {
|
||||
@@ -486,64 +486,76 @@ public class FileWatcher {
|
||||
return line;
|
||||
}
|
||||
|
||||
public boolean isWatched(VirtualFile file) {
|
||||
return isOperational() && checkWatchable(file.getPresentableUrl()) != null;
|
||||
public boolean isWatched(@NotNull final VirtualFile file) {
|
||||
if (isOperational()) {
|
||||
synchronized (LOCK) {
|
||||
return !checkWatchable(file.getPresentableUrl()).isEmpty();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String checkWatchable(String path) {
|
||||
if (path == null) return null;
|
||||
@NotNull
|
||||
private Collection<String> checkWatchable(final String reportedPath) {
|
||||
if (reportedPath == null) return Collections.emptyList();
|
||||
|
||||
for (Pair<String, String> mapping : myCanonicalMapping) {
|
||||
if (path.startsWith(mapping.first)) {
|
||||
path = mapping.second + path.substring(mapping.first.length());
|
||||
break;
|
||||
myAllPaths.clear();
|
||||
myAllPaths.add(reportedPath);
|
||||
for (Pair<String, String> map : myMapping) {
|
||||
if (FileUtil.startsWith(reportedPath, map.first)) {
|
||||
myAllPaths.add(map.second + reportedPath.substring(map.first.length()));
|
||||
}
|
||||
else if (FileUtil.startsWith(reportedPath, map.second)) {
|
||||
myAllPaths.add(map.first + reportedPath.substring(map.second.length()));
|
||||
}
|
||||
}
|
||||
|
||||
for (String root : myRecursiveWatchRoots) {
|
||||
if (FileUtil.startsWith(path, root)) {
|
||||
return path;
|
||||
myWatchedPaths.clear();
|
||||
ext:
|
||||
for (String path : myAllPaths) {
|
||||
for (String root : myRecursiveWatchRoots) {
|
||||
if (FileUtil.startsWith(path, root)) {
|
||||
myWatchedPaths.add(path);
|
||||
continue ext;
|
||||
}
|
||||
}
|
||||
|
||||
for (String root : myFlatWatchRoots) {
|
||||
if (FileUtil.pathsEqual(path, root)) {
|
||||
myWatchedPaths.add(path);
|
||||
continue ext;
|
||||
}
|
||||
final File parentFile = new File(path).getParentFile();
|
||||
if (parentFile != null && FileUtil.pathsEqual(parentFile.getPath(), root)) {
|
||||
myWatchedPaths.add(path);
|
||||
continue ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String root : myFlatWatchRoots) {
|
||||
if (FileUtil.pathsEqual(path, root)) {
|
||||
return path;
|
||||
}
|
||||
final File parentFile = new File(path).getParentFile();
|
||||
if (parentFile != null && FileUtil.pathsEqual(parentFile.getPath(), root)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return myWatchedPaths;
|
||||
}
|
||||
|
||||
private void onPathChange(final ChangeKind changeKind, final String path) {
|
||||
private void onPathChange(final ChangeKind changeKind, final Collection<String> paths) {
|
||||
switch (changeKind) {
|
||||
case STATS:
|
||||
case CHANGE:
|
||||
addPath(path, myDirtyPaths);
|
||||
myDirtyPaths.addAll(paths);
|
||||
break;
|
||||
|
||||
case CREATE:
|
||||
case DELETE:
|
||||
final File parentFile = new File(path).getParentFile();
|
||||
if (parentFile != null) {
|
||||
addPath(parentFile.getPath(), myDirtyPaths);
|
||||
}
|
||||
else {
|
||||
addPath(path, myDirtyPaths);
|
||||
for (String path : paths) {
|
||||
final File parent = new File(path).getParentFile();
|
||||
myDirtyPaths.add(parent != null ? parent.getPath() : path);
|
||||
}
|
||||
break;
|
||||
|
||||
case DIRTY:
|
||||
addPath(path, myDirtyDirs);
|
||||
myDirtyDirs.addAll(paths);
|
||||
break;
|
||||
|
||||
case RECDIRTY:
|
||||
addPath(path, myDirtyRecursivePaths);
|
||||
myDirtyRecursivePaths.addAll(paths);
|
||||
break;
|
||||
|
||||
case RESET:
|
||||
@@ -552,19 +564,6 @@ public class FileWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private void addPath(String path, List<String> list) {
|
||||
list.add(path);
|
||||
|
||||
for (Pair<String, String> map : myMapping) {
|
||||
if (FileUtil.startsWith(path, map.getFirst())) {
|
||||
list.add(map.getSecond() + path.substring(map.getFirst().length()));
|
||||
}
|
||||
else if (FileUtil.startsWith(path, map.getSecond())) {
|
||||
list.add(map.getFirst() + path.substring(map.getSecond().length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
synchronized (LOCK) {
|
||||
myDirtyPaths.clear();
|
||||
|
||||
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* 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.openapi.vfs.local;
|
||||
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.idea.Bombed;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.openapi.vfs.impl.local.FileWatcher;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import com.intellij.testFramework.PlatformLangTestCase;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class FileWatcherTest extends PlatformLangTestCase {
|
||||
private static final int NATIVE_PROCESS_DELAY = 500; // time to event to be caught by native watcher and passed to watcher thread
|
||||
|
||||
private FileWatcher myWatcher;
|
||||
private LocalFileSystem myFileSystem;
|
||||
private MessageBusConnection myConnection;
|
||||
private final List<VFileEvent> myEvents = new ArrayList<VFileEvent>();
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
myWatcher = FileWatcher.getInstance();
|
||||
assertNotNull(myWatcher);
|
||||
assertFalse(myWatcher.isOperational());
|
||||
myWatcher.startup();
|
||||
assertTrue(myWatcher.isOperational());
|
||||
|
||||
myFileSystem = LocalFileSystem.getInstance();
|
||||
assertNotNull(myFileSystem);
|
||||
|
||||
myConnection = ApplicationManager.getApplication().getMessageBus().connect();
|
||||
myConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
|
||||
@Override
|
||||
public void before(@NotNull List<? extends VFileEvent> events) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
synchronized (myEvents) {
|
||||
myEvents.addAll(events);
|
||||
myEvents.notifyAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
myConnection.disconnect();
|
||||
myWatcher.shutdown();
|
||||
}
|
||||
finally {
|
||||
myFileSystem = null;
|
||||
myWatcher = null;
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testFileRoot() throws Exception {
|
||||
final File file = FileUtil.createTempFile("test.", ".txt");
|
||||
refresh(file);
|
||||
final LocalFileSystem.WatchRequest request = watch(file);
|
||||
try {
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
public void testNonCanonicallyNamedFileRoot() throws Exception {
|
||||
if (SystemInfo.isFileSystemCaseSensitive) {
|
||||
System.out.println("Ignored: case-insensitive FS required");
|
||||
return;
|
||||
}
|
||||
|
||||
final File file = FileUtil.createTempFile("test.", ".txt");
|
||||
refresh(file);
|
||||
|
||||
final String watchRoot = file.getAbsolutePath().toUpperCase(Locale.US);
|
||||
final LocalFileSystem.WatchRequest request = watch(new File(watchRoot));
|
||||
try {
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDirectoryRecursive() throws Exception {
|
||||
final File topDir = FileUtil.createTempDirectory("top.", null);
|
||||
refresh(topDir);
|
||||
|
||||
final LocalFileSystem.WatchRequest request = watch(topDir);
|
||||
try {
|
||||
final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null);
|
||||
assertEvent(VFileCreateEvent.class, subDir.getAbsolutePath());
|
||||
refresh(subDir);
|
||||
|
||||
final File file = FileUtil.createTempFile(subDir, "test.", ".txt", true, false);
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(topDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDirectoryFlat() throws Exception {
|
||||
final File topDir = FileUtil.createTempDirectory("top.", null);
|
||||
final File watchedFile = FileUtil.createTempFile(topDir, "test.", ".txt", true, false);
|
||||
final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null);
|
||||
final File unwatchedFile = FileUtil.createTempFile(subDir, "test.", ".txt", true, false);
|
||||
refresh(topDir);
|
||||
|
||||
final LocalFileSystem.WatchRequest request = watch(topDir, false);
|
||||
try {
|
||||
FileUtil.writeToFile(watchedFile, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, watchedFile.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(unwatchedFile, "new content");
|
||||
assertEvent(VFileEvent.class);
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(topDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDirectoryNonExisting() throws Exception {
|
||||
final File topDir = FileUtil.createTempDirectory("top.", null);
|
||||
final File subDir = new File(topDir, "subDir");
|
||||
final File file = new File(subDir, "file.txt");
|
||||
refresh(topDir);
|
||||
|
||||
final LocalFileSystem.WatchRequest request = watch(subDir);
|
||||
try {
|
||||
assertTrue(subDir.toString(), subDir.mkdir());
|
||||
assertEvent(VFileCreateEvent.class, subDir.getAbsolutePath());
|
||||
refresh(subDir);
|
||||
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(topDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDirectoryOverlapping() throws Exception {
|
||||
final File topDir = FileUtil.createTempDirectory("top.", null);
|
||||
final File file1 = FileUtil.createTempFile(topDir, "file1.", ".txt", true, false);
|
||||
final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null);
|
||||
final File file2 = FileUtil.createTempFile(subDir, "file2.", ".txt", true, false);
|
||||
final File sideDir = FileUtil.createTempDirectory("side.", null);
|
||||
final File file3 = FileUtil.createTempFile(sideDir, "file3.", ".txt", true, false);
|
||||
refresh(topDir);
|
||||
refresh(sideDir);
|
||||
|
||||
final LocalFileSystem.WatchRequest request1 = watch(subDir);
|
||||
final LocalFileSystem.WatchRequest request2 = watch(sideDir);
|
||||
try {
|
||||
FileUtil.writeToFile(file1, "new content");
|
||||
FileUtil.writeToFile(file2, "new content");
|
||||
FileUtil.writeToFile(file3, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, file2.getAbsolutePath(), file3.getAbsolutePath());
|
||||
|
||||
final LocalFileSystem.WatchRequest request3 = watch(topDir);
|
||||
try {
|
||||
FileUtil.writeToFile(file1, "newer content");
|
||||
FileUtil.writeToFile(file2, "newer content");
|
||||
FileUtil.writeToFile(file3, "newer content");
|
||||
assertEvent(VFileContentChangeEvent.class, file1.getAbsolutePath(), file2.getAbsolutePath(), file3.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
unwatch(request3);
|
||||
}
|
||||
|
||||
FileUtil.writeToFile(file1, "newest content");
|
||||
FileUtil.writeToFile(file2, "newest content");
|
||||
FileUtil.writeToFile(file3, "newest content");
|
||||
assertEvent(VFileContentChangeEvent.class, file2.getAbsolutePath(), file3.getAbsolutePath());
|
||||
|
||||
FileUtil.delete(file1);
|
||||
FileUtil.delete(file2);
|
||||
FileUtil.delete(file3);
|
||||
assertEvent(VFileDeleteEvent.class, file1.getAbsolutePath(), file2.getAbsolutePath(), file3.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoots(Arrays.asList(request1, request2));
|
||||
FileUtil.delete(topDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Bombed(user = "roman.shevchenko", year = 2012, month = Calendar.MAY, day = 1)
|
||||
public void testSymlinkAboveWatchRoot() throws Exception {
|
||||
final File topDir = FileUtil.createTempDirectory("top.", null);
|
||||
final File topLink = SymlinkHandlingTest.createTempLink(topDir.getAbsolutePath(), "link");
|
||||
final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null);
|
||||
final File file = FileUtil.createTempFile(subDir, "test.", ".txt", true, false);
|
||||
final File fileLink = new File(new File(topLink, subDir.getName()), file.getName());
|
||||
refresh(topDir);
|
||||
refresh(topLink);
|
||||
|
||||
final LocalFileSystem.WatchRequest request = watch(topLink);
|
||||
try {
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, fileLink.getAbsolutePath());
|
||||
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, fileLink.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, fileLink.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(topLink);
|
||||
FileUtil.delete(topDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Bombed(user = "roman.shevchenko", year = 2012, month = Calendar.MAY, day = 1)
|
||||
public void testSymlinkBelowWatchRoot() throws Exception {
|
||||
final File targetDir = FileUtil.createTempDirectory("top.", null);
|
||||
final File file = FileUtil.createTempFile(targetDir, "test.", ".txt", true, false);
|
||||
final File linkDir = FileUtil.createTempDirectory("link.", null);
|
||||
final File link = new File(linkDir, "link");
|
||||
SymlinkHandlingTest.createTempLink(targetDir.getAbsolutePath(), link.getAbsolutePath());
|
||||
final File fileLink = new File(link, file.getName());
|
||||
refresh(targetDir);
|
||||
refresh(linkDir);
|
||||
|
||||
final LocalFileSystem.WatchRequest request = watch(linkDir);
|
||||
try {
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, fileLink.getAbsolutePath());
|
||||
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, fileLink.getAbsolutePath());
|
||||
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, fileLink.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
FileUtil.delete(linkDir);
|
||||
FileUtil.delete(targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSubst() throws Exception {
|
||||
if (!SystemInfo.isWindows) {
|
||||
System.out.println("Ignored: Windows required");
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<Character> roots = ContainerUtil.map2Set(File.listRoots(), new Function<File, Character>() {
|
||||
@Override
|
||||
public Character fun(File root) {
|
||||
return root.getPath().toLowerCase(Locale.US).charAt(0);
|
||||
}
|
||||
});
|
||||
char subst = 0;
|
||||
for (char c = 'e'; c <= 'z'; c++) {
|
||||
if (!roots.contains(c)) {
|
||||
subst = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertFalse("Occupied: " + roots.toString(), subst == 0);
|
||||
|
||||
final File targetDir = FileUtil.createTempDirectory("top.", null);
|
||||
final File subDir = FileUtil.createTempDirectory(targetDir, "sub.", null);
|
||||
final File file = FileUtil.createTempFile(subDir, "test.", ".txt", true, false);
|
||||
final int rv = new GeneralCommandLine("subst", subst + ":", targetDir.getAbsolutePath()).createProcess().waitFor();
|
||||
assertEquals(0, rv);
|
||||
|
||||
final File substDir = new File((subst + ":\\").toUpperCase(Locale.US), subDir.getName());
|
||||
final File substFile = new File(substDir, file.getName());
|
||||
refresh(targetDir);
|
||||
refresh(substDir);
|
||||
|
||||
final LocalFileSystem.WatchRequest request = watch(substDir);
|
||||
try {
|
||||
FileUtil.writeToFile(file, "new content");
|
||||
assertEvent(VFileContentChangeEvent.class, substFile.getAbsolutePath());
|
||||
|
||||
final LocalFileSystem.WatchRequest request2 = watch(targetDir);
|
||||
try {
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath(), substFile.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
unwatch(request2);
|
||||
}
|
||||
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, substFile.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
new GeneralCommandLine("subst", subst + ":", "/d").createProcess().waitFor();
|
||||
FileUtil.delete(targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<VFileEvent> getEvents() throws InterruptedException {
|
||||
TimeoutUtil.sleep(NATIVE_PROCESS_DELAY);
|
||||
myFileSystem.refresh(false);
|
||||
|
||||
synchronized (myEvents) {
|
||||
final ArrayList<VFileEvent> result = new ArrayList<VFileEvent>(myEvents);
|
||||
myEvents.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private void clearEvents() {
|
||||
myFileSystem.refresh(false);
|
||||
synchronized (myEvents) {
|
||||
myEvents.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private LocalFileSystem.WatchRequest watch(final File watchFile) throws InterruptedException {
|
||||
return watch(watchFile, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private LocalFileSystem.WatchRequest watch(final File watchFile, final boolean recursive) throws InterruptedException {
|
||||
final LocalFileSystem.WatchRequest request = myFileSystem.addRootToWatch(watchFile.getAbsolutePath(), recursive);
|
||||
assertNotNull(request);
|
||||
TimeoutUtil.sleep(NATIVE_PROCESS_DELAY);
|
||||
clearEvents();
|
||||
return request;
|
||||
}
|
||||
|
||||
private void unwatch(final LocalFileSystem.WatchRequest request) throws InterruptedException {
|
||||
myFileSystem.removeWatchedRoot(request);
|
||||
TimeoutUtil.sleep(NATIVE_PROCESS_DELAY);
|
||||
clearEvents();
|
||||
}
|
||||
|
||||
private VirtualFile refresh(final File file) {
|
||||
final VirtualFile vFile = myFileSystem.refreshAndFindFileByIoFile(file);
|
||||
assertNotNull(file.toString(), vFile);
|
||||
VfsUtilCore.visitChildrenRecursively(vFile, new VirtualFileVisitor() {
|
||||
@Override
|
||||
public boolean visitFile(@NotNull VirtualFile file) {
|
||||
file.getChildren();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return vFile;
|
||||
}
|
||||
|
||||
private void assertEvent(final Class<? extends VFileEvent> type, final String... paths) throws InterruptedException {
|
||||
final List<VFileEvent> events = getEvents();
|
||||
assertEquals(events.toString(), paths.length, events.size());
|
||||
|
||||
final Set<String> pathSet = ContainerUtil.map2Set(paths, new Function<String, String>() {
|
||||
@Override
|
||||
public String fun(final String path) {
|
||||
return FileUtil.toSystemIndependentName(path);
|
||||
}
|
||||
});
|
||||
|
||||
for (final VFileEvent event : events) {
|
||||
assertTrue(event.toString(), type.isInstance(event));
|
||||
|
||||
final VirtualFile eventFile = event.getFile();
|
||||
assertNotNull(event.toString(), eventFile);
|
||||
|
||||
assertTrue(eventFile + " not in " + Arrays.toString(paths), pathSet.remove(eventFile.getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -266,7 +266,7 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase {
|
||||
}
|
||||
|
||||
// todo[r.sh] use NIO2 API after migration to JDK 7
|
||||
private static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException {
|
||||
public static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException {
|
||||
final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') ||
|
||||
SystemInfo.isWindows && link.matches("^[c-zC-Z]:[/\\\\].*$");
|
||||
final File linkFile = isAbsolute ? new File(link) : new File(FileUtil.getTempDirectory(), link);
|
||||
|
||||
@@ -52,7 +52,9 @@ public class AndroidCommonUtils {
|
||||
private static final Pattern WARNING_PATTERN = Pattern.compile(".*warning.*");
|
||||
private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*");
|
||||
private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*");
|
||||
|
||||
private static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
|
||||
private static Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class");
|
||||
|
||||
public static final Pattern COMPILER_MESSAGE_PATTERN = Pattern.compile("(.+):(\\d+):.+");
|
||||
|
||||
@@ -204,7 +206,7 @@ public class AndroidCommonUtils {
|
||||
|
||||
private static void addFileToJar(@NotNull File file,
|
||||
@NotNull File rootDirectory,
|
||||
boolean packRClasses,
|
||||
boolean packRAndManifestClasses,
|
||||
@NotNull List<Pair<File, String>> files)
|
||||
throws IOException {
|
||||
|
||||
@@ -213,7 +215,7 @@ public class AndroidCommonUtils {
|
||||
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
addFileToJar(child, rootDirectory, packRClasses, files);
|
||||
addFileToJar(child, rootDirectory, packRAndManifestClasses, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +224,9 @@ public class AndroidCommonUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packRClasses && R_PATTERN.matcher(file.getName()).matches()) {
|
||||
if (!packRAndManifestClasses &&
|
||||
(R_PATTERN.matcher(file.getName()).matches() ||
|
||||
MANIFEST_PATTERN.matcher(file.getName()).matches())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public class GitBranchUiUtil {
|
||||
*/
|
||||
@Nullable
|
||||
public static String getNewBranchNameFromUser(@NotNull Project project, @NotNull Collection<GitRepository> repositories, @NotNull String dialogTitle) {
|
||||
return Messages.showInputDialog(project, "Enter the name of new branch", dialogTitle, Messages.getQuestionIcon(), "",
|
||||
return Messages.showInputDialog(project, "Enter the name of new branch:", dialogTitle, Messages.getQuestionIcon(), "",
|
||||
GitNewBranchNameValidator.newInstance(repositories));
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -95,8 +95,10 @@ public class MavenRepositoriesConfigurable extends BaseConfigurable implements S
|
||||
final String value = (String)myServiceList.getSelectedValue();
|
||||
final String text = Messages.showInputDialog("Artifactory or Nexus Service URL", "Add Service URL", Messages.getQuestionIcon(),
|
||||
value == null ? "http://" : value, new URLInputVaslidator());
|
||||
myModel.add(text);
|
||||
myServiceList.setSelectedValue(text, true);
|
||||
if (StringUtil.isNotEmpty(text)) {
|
||||
myModel.add(text);
|
||||
myServiceList.setSelectedValue(text, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
myEditButton.addActionListener(new ActionListener() {
|
||||
@@ -105,7 +107,7 @@ public class MavenRepositoriesConfigurable extends BaseConfigurable implements S
|
||||
final int index = myServiceList.getSelectedIndex();
|
||||
final String text = Messages.showInputDialog("Artifactory or Nexus Service URL", "Edit Service URL", Messages.getQuestionIcon(),
|
||||
myModel.getElementAt(index), new URLInputVaslidator());
|
||||
if (text != null) {
|
||||
if (StringUtil.isNotEmpty(text)) {
|
||||
myModel.setElementAt(text, index);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ public class DuplicatePropertyInspection extends GlobalSimpleInspectionTool {
|
||||
|
||||
private void checkFile(final PsiFile file, final InspectionManager manager, GlobalInspectionContextImpl context, final RefManager refManager, final ProblemDescriptionsProcessor processor) {
|
||||
if (!(file instanceof PropertiesFile)) return;
|
||||
if (!context.isToCheckMember(file, this)) return;
|
||||
if (!context.isToCheckFile(file, this)) return;
|
||||
final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(file.getProject());
|
||||
final PropertiesFile propertiesFile = (PropertiesFile)file;
|
||||
final List<IProperty> properties = propertiesFile.getProperties();
|
||||
|
||||
@@ -133140,6 +133140,7 @@ unwarrantably
|
||||
unwarranted
|
||||
unwary
|
||||
unwashed
|
||||
unwatch
|
||||
unwatchable
|
||||
unwatched
|
||||
unwavering
|
||||
|
||||
Reference in New Issue
Block a user