Log statistics for usage and interactions of bookmarks and favorites

GitOrigin-RevId: e7ea46fbf93da339464dbdf93e086e7120c5c055
This commit is contained in:
Dmitry Jemerov
2020-08-31 17:39:38 +00:00
committed by intellij-monorepo-bot
parent 07453d0303
commit 0eac6d25ad
8 changed files with 154 additions and 21 deletions
@@ -0,0 +1,32 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide
import com.intellij.pom.Navigatable
import com.intellij.pom.NavigatableWithText
import com.intellij.util.containers.map2Array
class NavigatableInterceptor(val baseNavigatable: Navigatable, val callback: (Navigatable, Boolean) -> Unit) : NavigatableWithText {
override fun navigate(requestFocus: Boolean) {
callback(baseNavigatable, requestFocus)
baseNavigatable.navigate(requestFocus)
}
override fun canNavigate(): Boolean {
return baseNavigatable.canNavigate()
}
override fun canNavigateToSource(): Boolean {
return baseNavigatable.canNavigateToSource()
}
override fun getNavigateActionText(focusEditor: Boolean): String? {
return (baseNavigatable as? NavigatableWithText)?.getNavigateActionText(focusEditor)
}
companion object {
@JvmStatic
fun wrap(navigatables: Array<Navigatable>, callback: (Navigatable, Boolean) -> Unit): Array<NavigatableInterceptor> {
return navigatables.map2Array { NavigatableInterceptor(it, callback) }
}
}
}
@@ -213,6 +213,10 @@ public final class BookmarkManager implements PersistentStateComponent<Element>
return answer;
}
public Collection<Bookmark> getAllBookmarks() {
return myBookmarks.values();
}
@Nullable
public Bookmark findEditorBookmark(@NotNull Document document, int line) {
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
@@ -5,6 +5,7 @@ import com.intellij.ide.bookmarks.Bookmark;
import com.intellij.ide.bookmarks.BookmarkItem;
import com.intellij.ide.bookmarks.BookmarkManager;
import com.intellij.ide.bookmarks.BookmarksListener;
import com.intellij.internal.statistic.BookmarkCounterCollector;
import com.intellij.lang.LangBundle;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.Document;
@@ -161,10 +162,11 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail
@Override
public void handleMnemonic(KeyEvent e, Project project, JBPopup popup) {
char mnemonic = e.getKeyChar();
char mnemonic = Character.toUpperCase(e.getKeyChar());
final Bookmark bookmark = BookmarkManager.getInstance(project).findBookmarkForMnemonic(mnemonic);
if (bookmark != null) {
popup.cancel();
BookmarkCounterCollector.bookmarkNavigate.log(mnemonic >= '0' && mnemonic <= '9', mnemonic >= 'A' && mnemonic <= 'Z', bookmark.getLine() >= 0);
IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(() -> bookmark.navigate(true));
}
}
@@ -205,6 +207,7 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail
if (item instanceof BookmarkItem && withEnterOrDoubleClick) {
Bookmark bookmark = ((BookmarkItem)item).getBookmark();
popup.cancel();
BookmarkCounterCollector.bookmarkNavigate.log(false, false, bookmark.getLine() >= 0);
bookmark.navigate(true);
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 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.
*/
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.favoritesTreeView;
@@ -48,7 +34,7 @@ public class AbstractUrlFavoriteAdapter extends AbstractUrl {
}
@NotNull
FavoriteNodeProvider getNodeProvider() {
public FavoriteNodeProvider getNodeProvider() {
return myNodeProvider;
}
}
@@ -4,6 +4,7 @@ package com.intellij.ide.favoritesTreeView;
import com.intellij.icons.AllIcons;
import com.intellij.ide.CopyPasteDelegator;
import com.intellij.ide.IdeView;
import com.intellij.ide.NavigatableInterceptor;
import com.intellij.ide.bookmarks.Bookmark;
import com.intellij.ide.dnd.aware.DnDAwareTree;
import com.intellij.ide.favoritesTreeView.actions.*;
@@ -17,6 +18,7 @@ import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.ide.util.EditorHelper;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.ide.util.treeView.NodeRenderer;
import com.intellij.internal.statistic.BookmarkCounterCollector;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.Editor;
@@ -43,6 +45,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.tree.TreeUtil;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -282,7 +285,7 @@ public final class FavoritesTreeViewPanel extends JPanel implements DataProvider
}
if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
final FavoriteTreeNodeDescriptor[] selectedNodeDescriptors = FavoritesTreeUtil.getSelectedNodeDescriptors(myTree);
return selectedNodeDescriptors.length == 1 ? selectedNodeDescriptors[0].getElement() : null;
return selectedNodeDescriptors.length == 1 ? new NavigatableInterceptor(selectedNodeDescriptors[0].getElement(), this::logNavigation) : null;
}
FavoritesManager favoriteManager = FavoritesManager.getInstance(myProject);
if (CommonDataKeys.NAVIGATABLE_ARRAY.is(dataId)) {
@@ -292,7 +295,7 @@ public final class FavoritesTreeViewPanel extends JPanel implements DataProvider
selectedElements.addAll(ContainerUtil.map(favoriteManager.getVirtualFiles(listname, false), file -> new OpenFileDescriptor(myProject, file)));
}
selectedElements.addAll(getSelectedElements(Navigatable.class));
return selectedElements.isEmpty() ? null : selectedElements.toArray(new Navigatable[0]);
return selectedElements.isEmpty() ? null : NavigatableInterceptor.wrap(selectedElements.toArray(new Navigatable[0]), this::logNavigation);
}
if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) {
@@ -401,6 +404,11 @@ public final class FavoritesTreeViewPanel extends JPanel implements DataProvider
return null;
}
private Unit logNavigation(Navigatable navigatable, Boolean focusEditor) {
BookmarkCounterCollector.favoritesNavigate.log(myProject, navigatable.getClass());
return Unit.INSTANCE;
}
private <T> List<T> getSelectedElements(Class<T> klass) {
final Object[] elements = getSelectedNodeElements();
ArrayList<T> result = new ArrayList<>();
@@ -0,0 +1,98 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.internal.statistic
import com.intellij.ide.bookmarks.BookmarkManager
import com.intellij.ide.favoritesTreeView.AbstractUrlFavoriteAdapter
import com.intellij.ide.favoritesTreeView.FavoriteNodeProvider
import com.intellij.ide.favoritesTreeView.FavoritesManager
import com.intellij.ide.projectView.impl.DirectoryUrl
import com.intellij.ide.projectView.impl.PsiFileUrl
import com.intellij.internal.statistic.beans.MetricEvent
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
import com.intellij.internal.statistic.service.fus.collectors.ProjectUsagesCollector
import com.intellij.openapi.project.Project
class BookmarkUsagesCollector : ProjectUsagesCollector() {
private val group: EventLogGroup = EventLogGroup("bookmarks", 1)
private val bookmarksTotal = group.registerEvent("bookmarks.total", EventFields.Count)
private val bookmarksWithLine = group.registerEvent("bookmarks.with.line", EventFields.Count)
private val bookmarksWithNumber = group.registerEvent("bookmarks.with.number.mnemonic", EventFields.Count)
private val bookmarksWithLetter = group.registerEvent("bookmarks.with.letter.mnemonic", EventFields.Count)
private val favoritesLists = group.registerEvent("favorites.lists", EventFields.Count)
private val favoritesTotal = group.registerEvent("favorites.total", EventFields.Count)
private val favoriteFiles = group.registerEvent("favorites.files", EventFields.Count)
private val favoriteDirectories = group.registerEvent("favorites.directories", EventFields.Count)
private val favoriteCustom = group.registerEvent("favorites.custom", EventFields.Count,
EventFields.StringValidatedByCustomRule("type", "favorite_type"),
EventFields.PluginInfoFromInstance)
override fun getGroup(): EventLogGroup {
return group
}
override fun getMetrics(project: Project): MutableSet<MetricEvent> {
val result = mutableSetOf<MetricEvent>()
val bookmarkManager = BookmarkManager.getInstance(project)
val bookmarks = bookmarkManager.allBookmarks
result += bookmarksTotal.metric(bookmarks.size)
result += bookmarksWithLine.metric(bookmarks.filter { it.line >= 0 }.size)
result += bookmarksWithNumber.metric(bookmarks.filter { it.mnemonic in '0'..'9' }.size)
result += bookmarksWithLetter.metric(bookmarks.filter { it.mnemonic in 'A'..'Z' }.size)
val favoritesManager = FavoritesManager.getInstance(project)
val listNames = favoritesManager.availableFavoritesListNames
result += favoritesLists.metric(listNames.size)
var favTotal = 0
var favFiles = 0
var favDirectories = 0
val favCustom = mutableMapOf<String, Int>()
val favCustomProviders = mutableMapOf<String, FavoriteNodeProvider>()
for (listName in listNames) {
val urls = favoritesManager.getFavoritesListRootUrls(listName)
favTotal += urls.size
for (url in urls) {
when (val abstractUrl = url.data.first) {
is PsiFileUrl -> favFiles++
is DirectoryUrl -> favDirectories++
is AbstractUrlFavoriteAdapter -> {
val type = abstractUrl.nodeProvider.favoriteTypeId
val count = favCustom.getOrPut(type) { 0 }
favCustom[type] = count + 1
favCustomProviders[type] = abstractUrl.nodeProvider
}
}
}
}
result += favoritesTotal.metric(favTotal)
result += favoriteFiles.metric(favFiles)
result += favoriteDirectories.metric(favDirectories)
for ((type, count) in favCustom) {
result += favoriteCustom.metric(count, type, favCustomProviders[type]!!)
}
return result
}
}
class BookmarkCounterCollector : CounterUsagesCollector() {
companion object {
private val group: EventLogGroup = EventLogGroup("bookmarks.counters", 1)
@JvmField
val favoritesNavigate = group.registerEvent("favorites.navigate", EventFields.Class("navigatable"))
@JvmField
val bookmarkNavigate = group.registerEvent("bookmark.navigate",
EventFields.Boolean("numberMnemonicUsed"),
EventFields.Boolean("letterMnemonicUsed"),
EventFields.Boolean("withLine"));
}
override fun getGroup(): EventLogGroup = Companion.group
}
@@ -1007,6 +1007,8 @@
<statistics.projectUsagesCollector implementation="com.intellij.codeInsight.hints.InlayProviderUsageCollector"/>
<statistics.projectUsagesCollector implementation="com.intellij.codeInsight.daemon.impl.analysis.HighlightingSettingsPerFileCollector"/>
<statistics.projectUsagesCollector implementation="com.intellij.ui.tabs.FileColorsUsagesCollector"/>
<statistics.projectUsagesCollector implementation="com.intellij.internal.statistic.BookmarkUsagesCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.internal.statistic.BookmarkCounterCollector"/>
<statistics.counterUsagesCollector groupId="searchEverywhere" version="4"/>
<statistics.counterUsagesCollector groupId="actions.runAnything" version="1"/>
@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.internal.statistic.collectors.fus
import com.intellij.internal.statistic.eventLog.validator.ValidationResultType
@@ -13,6 +13,6 @@ class PluginInfoValidationRule : CustomValidationRule() {
}
companion object {
private val acceptedRules = hashSetOf("plugin_info", "project_type", "framework", "gutter_icon", "editor_notification_panel_key", "plugin_version")
private val acceptedRules = hashSetOf("plugin_info", "project_type", "framework", "gutter_icon", "editor_notification_panel_key", "plugin_version", "favorite_type")
}
}