Skip to content
Permalink
Browse files

#415 - obtain covered entries in batch operation

  • Loading branch information...
hsz committed Aug 20, 2017
1 parent 384003b commit 54f0a5e7fea100cf0727272082c22dc8783ce0bc
@@ -38,17 +38,13 @@
import mobi.hsz.idea.gitignore.IgnoreBundle;
import mobi.hsz.idea.gitignore.psi.IgnoreEntry;
import mobi.hsz.idea.gitignore.psi.IgnoreFile;
import mobi.hsz.idea.gitignore.psi.IgnoreVisitor;
import mobi.hsz.idea.gitignore.util.Constants;
import mobi.hsz.idea.gitignore.util.Glob;
import mobi.hsz.idea.gitignore.util.Utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentMap;

/**
@@ -142,54 +138,56 @@ public void cleanup(@NotNull Project project) {
final Set<String> unignored = ContainerUtil.newHashSet();

final ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
final List<Pair<IgnoreEntry, IgnoreEntry>> entries = ContainerUtil.newArrayList();
final List<Pair<IgnoreEntry, IgnoreEntry>> result = ContainerUtil.newArrayList();
final Map<IgnoreEntry, Set<String>> map = ContainerUtil.newHashMap();

file.acceptChildren(new IgnoreVisitor() {
@Override
public void visitEntry(@NotNull IgnoreEntry entry) {
Set<String> matched = getPathsSet(contextDirectory, entry);
Collection<String> intersection;
boolean modified;

if (!entry.isNegated()) {
ignored.addAll(matched);
intersection = Utils.intersection(unignored, matched);
modified = unignored.removeAll(intersection);
} else {
unignored.addAll(matched);
intersection = Utils.intersection(ignored, matched);
modified = ignored.removeAll(intersection);
}
final ArrayList<IgnoreEntry> entries = ContainerUtil.newArrayList(Arrays.asList(
((IgnoreFile) file).findChildrenByClass(IgnoreEntry.class)
));
final Map<IgnoreEntry, Set<String>> matchedMap = getPathsSet(contextDirectory, entries);

for (IgnoreEntry entry : entries) {
Set<String> matched = matchedMap.get(entry);
Collection<String> intersection;
boolean modified;

if (!entry.isNegated()) {
ignored.addAll(matched);
intersection = Utils.intersection(unignored, matched);
modified = unignored.removeAll(intersection);
} else {
unignored.addAll(matched);
intersection = Utils.intersection(ignored, matched);
modified = ignored.removeAll(intersection);
}

if (modified) {
return;
if (modified) {
continue;
}

for (IgnoreEntry recent : map.keySet()) {
Set<String> recentValues = map.get(recent);
if (recentValues.isEmpty() || matched.isEmpty()) {
continue;
}

for (IgnoreEntry recent : map.keySet()) {
Set<String> recentValues = map.get(recent);
if (recentValues.isEmpty() || matched.isEmpty()) {
continue;
if (entry.isNegated() == recent.isNegated()) {
if (recentValues.containsAll(matched)) {
result.add(Pair.create(recent, entry));
} else if (matched.containsAll(recentValues)) {
result.add(Pair.create(entry, recent));
}

if (entry.isNegated() == recent.isNegated()) {
if (recentValues.containsAll(matched)) {
entries.add(Pair.create(recent, entry));
} else if (matched.containsAll(recentValues)) {
entries.add(Pair.create(entry, recent));
}
} else {
if (intersection.containsAll(recentValues)) {
entries.add(Pair.create(entry, recent));
}
} else {
if (intersection.containsAll(recentValues)) {
result.add(Pair.create(entry, recent));
}
}

map.put(entry, matched);
}
});

for (Pair<IgnoreEntry, IgnoreEntry> pair : entries) {
map.put(entry, matched);
}

for (Pair<IgnoreEntry, IgnoreEntry> pair : result) {
problemsHolder.registerProblem(pair.second, message(pair.first, virtualFile, isOnTheFly),
new IgnoreRemoveEntryFix(pair.second));
}
@@ -198,20 +196,35 @@ public void visitEntry(@NotNull IgnoreEntry entry) {
}

/**
* Returns the paths list for the given {@link IgnoreEntry} in {@link VirtualFile} context.
* Returns the paths list for the given {@link IgnoreEntry} array in {@link VirtualFile} context.
* Stores fetched data in {@link #cacheMap} to limit the queries to the files tree.
*
* @param contextDirectory current context
* @param entry to check
* @param entries to check
* @return paths list
*/
@NotNull
private Set<String> getPathsSet(@NotNull VirtualFile contextDirectory, @NotNull IgnoreEntry entry) {
final String key = contextDirectory.getPath() + Constants.DOLLAR + entry.getText();
if (!cacheMap.containsKey(key)) {
cacheMap.put(key, ContainerUtil.newHashSet(Glob.findAsPaths(contextDirectory, entry, true)));
private Map<IgnoreEntry, Set<String>> getPathsSet(@NotNull VirtualFile contextDirectory,
@NotNull ArrayList<IgnoreEntry> entries) {
final Map<IgnoreEntry, Set<String>> result = ContainerUtil.newHashMap();
final ArrayList<IgnoreEntry> notCached = ContainerUtil.newArrayList();

for (IgnoreEntry entry : entries) {
final String key = contextDirectory.getPath() + Constants.DOLLAR + entry.getText();
if (!cacheMap.containsKey(key)) {
notCached.add(entry);
}
result.put(entry, cacheMap.get(key));
}

final Map<IgnoreEntry, Set<String>> found = Glob.findAsPaths(contextDirectory, notCached, true);
for (Map.Entry<IgnoreEntry, Set<String>> item : found.entrySet()) {
final String key = contextDirectory.getPath() + Constants.DOLLAR + item.getKey().getText();
cacheMap.put(key, item.getValue());
result.put(item.getKey(), item.getValue());
}
return cacheMap.get(key);

return result;
}

/**
@@ -68,7 +68,7 @@ public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
if (parent == null || projectDir == null || !Utils.isUnder(parent, projectDir)) {
return null;
}
List<VirtualFile> files = Glob.find(parent, entry);
List<VirtualFile> files = Glob.findOne(parent, entry);
for (VirtualFile file : files) {
if (!file.isDirectory()) {
return null;
@@ -34,9 +34,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.List;
import java.util.WeakHashMap;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -66,8 +65,8 @@ private Glob() {
* @return search result
*/
@NotNull
public static List<VirtualFile> find(@NotNull final VirtualFile root, @NotNull IgnoreEntry entry) {
return find(root, entry, false);
public static List<VirtualFile> findOne(@NotNull final VirtualFile root, @NotNull IgnoreEntry entry) {
return find(root, ContainerUtil.newArrayList(entry), false).get(entry);
}

/**
@@ -79,70 +78,93 @@ private Glob() {
* @return search result
*/
@NotNull
public static List<VirtualFile> find(@NotNull final VirtualFile root, @NotNull IgnoreEntry entry,
final boolean includeNested) {
final Pattern pattern = createPattern(entry);
if (pattern == null) {
return Collections.emptyList();
}

final List<VirtualFile> files = ContainerUtil.newArrayList();
final Matcher matcher = pattern.matcher("");

VirtualFileVisitor<Matcher> visitor = new VirtualFileVisitor<Matcher>(VirtualFileVisitor.NO_FOLLOW_SYMLINKS) {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
boolean matches = false;
String path = Utils.getRelativePath(root, file);

if (path == null || Utils.isVcsDirectory(file)) {
return false;
}

if (getCurrentValue() == null || MatcherUtil.match(getCurrentValue(), path)) {
matches = true;
files.add(file);
}

setValueForChildren(includeNested && matches ? null : getCurrentValue());
return true;
}
};
visitor.setValueForChildren(matcher);
VfsUtil.visitChildrenRecursively(root, visitor);

return files;
public static List<VirtualFile> findOne(@NotNull final VirtualFile root, @NotNull IgnoreEntry entry,
final boolean includeNested) {
return find(root, ContainerUtil.newArrayList(entry), includeNested).get(entry);
}

/**
* Finds for {@link VirtualFile} paths list using glob rule in given root directory.
* Finds for {@link VirtualFile} list using glob rule in given root directory.
*
* @param root root directory
* @param entry ignore entry
* @param root root directory
* @param entries ignore entries
* @param includeNested attach children to the search result
* @return search result
*/
@NotNull
public static List<String> findAsPaths(@NotNull VirtualFile root, @NotNull IgnoreEntry entry) {
return findAsPaths(root, entry, false);
public static Map<IgnoreEntry, List<VirtualFile>> find(@NotNull final VirtualFile root,
@NotNull List<IgnoreEntry> entries,
final boolean includeNested) {
final ConcurrentMap<IgnoreEntry, List<VirtualFile>> result = ContainerUtil.newConcurrentMap();
final HashMap<IgnoreEntry, Matcher> map = ContainerUtil.newHashMap();
for (IgnoreEntry entry : entries) {
result.put(entry, ContainerUtil.<VirtualFile>newArrayList());

final Pattern pattern = createPattern(entry);
if (pattern == null) {
continue;
}
map.put(entry, pattern.matcher(""));
}

VirtualFileVisitor<HashMap<IgnoreEntry, Matcher>> visitor =
new VirtualFileVisitor<HashMap<IgnoreEntry, Matcher>>(VirtualFileVisitor.NO_FOLLOW_SYMLINKS) {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
final HashMap<IgnoreEntry, Matcher> current = ContainerUtil.newHashMap(getCurrentValue());
if (current.isEmpty()) {
return false;
}

final String path = Utils.getRelativePath(root, file);
if (path == null || Utils.isVcsDirectory(file)) {
return false;
}

for (Map.Entry<IgnoreEntry, Matcher> item : current.entrySet()) {
boolean matches = false;
if (item.getValue() == null || MatcherUtil.match(item.getValue(), path)) {
matches = true;
result.get(item.getKey()).add(file);
}
if (includeNested && matches) {
current.put(item.getKey(), null);
}
}

setValueForChildren(current);
return true;
}
};
visitor.setValueForChildren(map);
VfsUtil.visitChildrenRecursively(root, visitor);

return result;
}

/**
* Finds for {@link VirtualFile} paths list using glob rule in given root directory.
*
* @param root root directory
* @param entry ignore entry
* @param entries ignore entry
* @param includeNested attach children to the search result
* @return search result
*/
@NotNull
public static List<String> findAsPaths(@NotNull VirtualFile root, @NotNull IgnoreEntry entry,
boolean includeNested) {
final List<String> list = ContainerUtil.newArrayList();
final List<VirtualFile> files = find(root, entry, includeNested);
for (VirtualFile file : files) {
list.add(Utils.getRelativePath(root, file));
public static Map<IgnoreEntry, Set<String>> findAsPaths(@NotNull VirtualFile root,
@NotNull List<IgnoreEntry> entries, boolean includeNested) {
final Map<IgnoreEntry, Set<String>> result = ContainerUtil.newHashMap();

final Map<IgnoreEntry, List<VirtualFile>> files = find(root, entries, includeNested);
for (Map.Entry<IgnoreEntry, List<VirtualFile>> item : files.entrySet()) {
final Set<String> set = ContainerUtil.newHashSet();
for (VirtualFile file : item.getValue()) {
set.add(Utils.getRelativePath(root, file));
}
result.put(item.getKey(), set);
}
return list;

return result;
}

/**
@@ -3,6 +3,6 @@ subdir1/foo.txt

subdir2/
<warning descr="'subdir2/' is covered by 'subdir2/'">subdir2/</warning>

subdir3/fo*
<warning descr="'subdir3/fo*' is covered by 'subdir3/fo*'">subdir3/fo*</warning>
<warning descr="'subdir3/fo*' is covered by 'subdir3/fo*'">subdir3/fo*</warning>

0 comments on commit 54f0a5e

Please sign in to comment.
You can’t perform that action at this time.