From f396310784badcf0f0dee63af026d0c9a18deaa5 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sat, 2 Jul 2016 00:10:37 +0300 Subject: [PATCH] [vcs-log] add users index --- .../vcs/log/data/VcsUserRegistryImpl.java | 9 ++ .../data/index/VcsLogFullDetailsIndex.java | 50 ++++++--- .../log/data/index/VcsLogPersistentIndex.java | 106 ++++++++++++++---- .../vcs/log/data/index/VcsLogUserIndex.java | 76 +++++++++++++ .../log/ui/filter/VcsLogUserFilterImpl.java | 9 ++ 5 files changed, 215 insertions(+), 35 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogUserIndex.java diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java index 9f653779c257..594c2090547f 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java @@ -125,6 +125,15 @@ public class VcsUserRegistryImpl implements Disposable, VcsUserRegistry { } } + public int getUserId(@NotNull VcsUser user) throws IOException { + return myPersistentEnumerator.enumerate(user); + } + + @Nullable + public VcsUser getUserById(Integer userId) throws IOException { + return myPersistentEnumerator.valueOf(userId); + } + private class MyDescriptor implements KeyDescriptor { @Override public void save(@NotNull DataOutput out, VcsUser value) throws IOException { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java index dbd3ab2e3948..b7dff148e110 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java @@ -17,6 +17,8 @@ package com.intellij.vcs.log.data.index; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Ref; +import com.intellij.util.Consumer; import com.intellij.util.PathUtilRt; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; @@ -54,31 +56,47 @@ public class VcsLogFullDetailsIndex implements Disposable { Disposer.register(disposableParent, this); } + @NotNull + public TIntHashSet getCommitsWithAnyKey(@NotNull Set keys) throws StorageException { + TIntHashSet result = new TIntHashSet(); + + for (Integer key : keys) { + iterateCommitIds(key, result::add); + } + + return result; + } + @NotNull public TIntHashSet getCommitsWithAllKeys(@NotNull Set keys) throws StorageException { - TIntHashSet result = null; + Ref result = new Ref<>(null); for (Integer key : keys) { TIntHashSet newResult = new TIntHashSet(); - ValueContainer data = myMapReduceIndex.getData(key); - - ValueContainer.ValueIterator valueIt = data.getValueIterator(); - while (valueIt.hasNext()) { - valueIt.next(); - ValueContainer.IntIterator inputIt = valueIt.getInputIdsIterator(); - while (inputIt.hasNext()) { - int id = inputIt.next(); - if (result == null || result.contains(id)) { - newResult.add(id); - } + iterateCommitIds(key, integer -> { + if (result.get() == null || result.get().contains(integer)) { + newResult.add(integer); } - } + }); - result = newResult; + result.set(newResult); } - if (result == null) return new TIntHashSet(); - return result; + if (result.get() == null) return new TIntHashSet(); + return result.get(); + } + + private void iterateCommitIds(int key, @NotNull Consumer consumer) throws StorageException { + ValueContainer data = myMapReduceIndex.getData(key); + + ValueContainer.ValueIterator valueIt = data.getValueIterator(); + while (valueIt.hasNext()) { + valueIt.next(); + ValueContainer.IntIterator inputIt = valueIt.getInputIdsIterator(); + while (inputIt.hasNext()) { + consumer.consume(inputIt.next()); + } + } } public void update(int commitId, @NotNull VcsFullCommitDetails details) throws IOException { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index e4313234f9ba..eeace0c7dad8 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -17,6 +17,7 @@ package com.intellij.vcs.log.data.index; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.ProgressIndicator; @@ -27,6 +28,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; @@ -34,10 +36,16 @@ import com.intellij.util.Consumer; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.StorageException; -import com.intellij.util.io.*; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.PersistentHashMap; +import com.intellij.util.io.PersistentMap; import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.CommitDetailsGetter; import com.intellij.vcs.log.data.InMemoryMap; +import com.intellij.vcs.log.data.TroveUtil; +import com.intellij.vcs.log.data.VcsUserRegistryImpl; +import com.intellij.vcs.log.ui.filter.VcsLogUserFilterImpl; import com.intellij.vcs.log.util.PersistentUtil; import gnu.trove.TIntHashSet; import gnu.trove.TIntProcedure; @@ -59,9 +67,12 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { @NotNull private final Consumer myFatalErrorsConsumer; @NotNull private final CommitDetailsGetter myDetailsGetter; @NotNull private final VcsLogStorage myHashMap; + @NotNull private final VcsUserRegistryImpl myUserRegistry; + @NotNull private final Set myRoots; @NotNull private final PersistentMap myMessagesIndex; @Nullable private final VcsLogMessagesTrigramIndex myTrigramIndex; + @Nullable private final VcsLogUserIndex myUserIndex; @NotNull private TIntHashSet myCommitsToIndex = new TIntHashSet(); @@ -75,28 +86,34 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { myProject = project; myDetailsGetter = detailsGetter; myFatalErrorsConsumer = fatalErrorsConsumer; + myRoots = providers.keySet(); + + myUserRegistry = (VcsUserRegistryImpl)ServiceManager.getService(myProject, VcsUserRegistry.class); String logId = PersistentUtil.calcLogId(myProject, providers); - myMessagesIndex = createIndex(EnumeratorStringDescriptor.INSTANCE, "messages", logId, 0); - - VcsLogMessagesTrigramIndex trigramIndex; - try { - trigramIndex = new VcsLogMessagesTrigramIndex(logId, disposableParent); - } - catch (IOException e) { - myFatalErrorsConsumer.consume(e); - trigramIndex = null; - } - myTrigramIndex = trigramIndex; + myMessagesIndex = createMap(EnumeratorStringDescriptor.INSTANCE, "messages", logId, 0); + myTrigramIndex = createIndex(() -> new VcsLogMessagesTrigramIndex(logId, this)); + myUserIndex = createIndex(() -> new VcsLogUserIndex(logId, myUserRegistry, this)); Disposer.register(disposableParent, this); } + @Nullable + private I createIndex(@NotNull ThrowableComputable computable) { + try { + return computable.compute(); + } + catch (IOException e) { + myFatalErrorsConsumer.consume(e); + } + return null; + } + @NotNull - private PersistentMap createIndex(@NotNull KeyDescriptor descriptor, - @NotNull String kind, - @NotNull String logId, int version) { + private PersistentMap createMap(@NotNull KeyDescriptor descriptor, + @NotNull String kind, + @NotNull String logId, int version) { try { return PersistentUtil.createPersistentHashMap(descriptor, kind, logId, version); } @@ -141,10 +158,13 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { myMessagesIndex.put(index, detail.getFullMessage()); if (myTrigramIndex != null) myTrigramIndex.update(index, detail); + if (myUserIndex != null) myUserIndex.update(index, detail); } myMessagesIndex.force(); if (myTrigramIndex != null) myTrigramIndex.flush(); - } catch (IOException | StorageException e) { + if (myUserIndex != null) myUserIndex.flush(); + } + catch (IOException | StorageException e) { myFatalErrorsConsumer.consume(e); } } @@ -152,6 +172,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { public boolean isIndexed(int commit) { try { return myMessagesIndex.get(commit) != null && + (myUserIndex == null || myUserIndex.isIndexed(commit)) && (myTrigramIndex == null || myTrigramIndex.isIndexed(commit)); } catch (IOException e) { @@ -198,6 +219,19 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { return result; } + @NotNull + private TIntHashSet filterUsers(@NotNull Set users) { + if (myUserIndex != null) { + try { + return myUserIndex.getCommitsForUsers(users); + } + catch (IOException | StorageException e) { + myFatalErrorsConsumer.consume(e); + } + } + return new TIntHashSet(); + } + @NotNull public TIntHashSet filterMessages(@NotNull String text) { if (myTrigramIndex != null) { @@ -239,7 +273,8 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { public boolean canFilter(@NotNull List filters) { if (filters.isEmpty()) return false; for (VcsLogDetailsFilter filter : filters) { - if (!(filter instanceof VcsLogTextFilter)) { + if (!((filter instanceof VcsLogTextFilter && myTrigramIndex != null) || + (filter instanceof VcsLogUserFilterImpl && myUserIndex != null))) { return false; } } @@ -252,9 +287,42 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { Set result = ContainerUtil.newHashSet(); VcsLogTextFilter textFilter = ContainerUtil.findInstance(detailsFilters, VcsLogTextFilter.class); + VcsLogUserFilterImpl userFilter = ContainerUtil.findInstance(detailsFilters, VcsLogUserFilterImpl.class); + + TIntHashSet filteredByMessage; if (textFilter != null) { - filterMessages(textFilter.getText()).forEach(value -> { - result.add(value); + filteredByMessage = filterMessages(textFilter.getText()); + } + else { + filteredByMessage = null; + } + + TIntHashSet filteredByUser; + if (userFilter != null) { + Set users = ContainerUtil.newHashSet(); + for (VirtualFile root : myRoots) { + users.addAll(userFilter.getUsers(root)); + } + + filteredByUser = filterUsers(users); + } + else { + filteredByUser = null; + } + + if (filteredByMessage == null) { + if (filteredByUser != null) { + filteredByUser.forEach(value -> { + result.add(value); + return true; + }); + } + } + else { + filteredByMessage.forEach(value -> { + if (filteredByUser != null && filteredByUser.contains(value)) { + result.add(value); + } return true; }); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogUserIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogUserIndex.java new file mode 100644 index 000000000000..ad92e286e3fb --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogUserIndex.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-2016 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.vcs.log.data.index; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.StorageException; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsUser; +import com.intellij.vcs.log.data.VcsUserRegistryImpl; +import gnu.trove.THashMap; +import gnu.trove.TIntHashSet; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +public class VcsLogUserIndex extends VcsLogFullDetailsIndex { + private static final Logger LOG = Logger.getInstance(VcsLogUserIndex.class); + private static final int VERSION = 0; + @NotNull private final VcsUserRegistryImpl myUserRegistry; + + public VcsLogUserIndex(@NotNull String logId, + @NotNull VcsUserRegistryImpl userRegistry, + @NotNull Disposable disposableParent) throws IOException { + super(logId, "users", VERSION, new UserIndexer(userRegistry), disposableParent); + myUserRegistry = userRegistry; + } + + public TIntHashSet getCommitsForUsers(@NotNull Set users) throws IOException, StorageException { + Set ids = ContainerUtil.newHashSet(); + for (VcsUser user : users) { + ids.add(myUserRegistry.getUserId(user)); + } + return getCommitsWithAnyKey(ids); + } + + private static class UserIndexer implements DataIndexer { + @NotNull private final VcsUserRegistryImpl myRegistry; + + public UserIndexer(@NotNull VcsUserRegistryImpl registry) { + myRegistry = registry; + } + + @NotNull + @Override + public Map map(@NotNull VcsFullCommitDetails inputData) { + Map result = new THashMap<>(); + + try { + result.put(myRegistry.getUserId(inputData.getAuthor()), null); + } + catch (IOException e) { + LOG.error(e); + } + + return result; + } + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogUserFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogUserFilterImpl.java index 04fdf2409811..d6faefb218b9 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogUserFilterImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogUserFilterImpl.java @@ -59,6 +59,15 @@ public class VcsLogUserFilterImpl implements VcsLogUserFilter { return result; } + @NotNull + public Collection getUsers(@NotNull VirtualFile root) { + Set result = ContainerUtil.newHashSet(); + for (String user : myUsers) { + result.addAll(getUsers(root, user)); + } + return result; + } + @NotNull private Set getUsers(@NotNull VirtualFile root, @NotNull String name) { Set users = ContainerUtil.newHashSet();