diff --git a/platform/lang-impl/src/com/intellij/ide/NavigatableInterceptor.kt b/platform/lang-impl/src/com/intellij/ide/NavigatableInterceptor.kt new file mode 100644 index 000000000000..c40b5069de5b --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/NavigatableInterceptor.kt @@ -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, callback: (Navigatable, Boolean) -> Unit): Array { + return navigatables.map2Array { NavigatableInterceptor(it, callback) } + } + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java index 12c4cfb43e60..7a8ed18d381f 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java @@ -213,6 +213,10 @@ public final class BookmarkManager implements PersistentStateComponent return answer; } + public Collection getAllBookmarks() { + return myBookmarks.values(); + } + @Nullable public Bookmark findEditorBookmark(@NotNull Document document, int line) { VirtualFile file = FileDocumentManager.getInstance().getFile(document); diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java index 703ccb1b92b7..446fade27e0a 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java @@ -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); } } diff --git a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/AbstractUrlFavoriteAdapter.java b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/AbstractUrlFavoriteAdapter.java index 1c7736ada394..9ce37503c6f4 100644 --- a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/AbstractUrlFavoriteAdapter.java +++ b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/AbstractUrlFavoriteAdapter.java @@ -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; } } diff --git a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java index 7fb59ca3f868..9f1d48a63db7 100644 --- a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java +++ b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java @@ -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 List getSelectedElements(Class klass) { final Object[] elements = getSelectedNodeElements(); ArrayList result = new ArrayList<>(); diff --git a/platform/lang-impl/src/com/intellij/internal/statistic/BookmarkUsagesCollector.kt b/platform/lang-impl/src/com/intellij/internal/statistic/BookmarkUsagesCollector.kt new file mode 100644 index 000000000000..86030241f5d9 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/internal/statistic/BookmarkUsagesCollector.kt @@ -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 { + val result = mutableSetOf() + + 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() + val favCustomProviders = mutableMapOf() + 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 +} \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 580f80551e7e..73fd07f64dfd 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -1007,6 +1007,8 @@ + + diff --git a/platform/statistics/src/com/intellij/internal/statistic/collectors/fus/PluginInfoValidationRule.kt b/platform/statistics/src/com/intellij/internal/statistic/collectors/fus/PluginInfoValidationRule.kt index 2f3b58f3e4dd..3f52667e6bae 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/collectors/fus/PluginInfoValidationRule.kt +++ b/platform/statistics/src/com/intellij/internal/statistic/collectors/fus/PluginInfoValidationRule.kt @@ -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") } }