diff --git a/platform/lang-impl/src/com/intellij/ide/impl/ProjectViewSelectInTarget.java b/platform/lang-impl/src/com/intellij/ide/impl/ProjectViewSelectInTarget.java index 3edadf619311..c3011194167b 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/ProjectViewSelectInTarget.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/ProjectViewSelectInTarget.java @@ -9,6 +9,7 @@ import com.intellij.ide.projectView.SelectableTreeStructureProvider; import com.intellij.ide.projectView.TreeStructureProvider; import com.intellij.ide.projectView.impl.AbstractProjectViewPane; import com.intellij.ide.projectView.impl.SelectInProjectViewImpl; +import com.intellij.ide.projectView.impl.SelectInProjectViewImplKt; import com.intellij.notebook.editor.BackedVirtualFile; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.DumbService; @@ -54,8 +55,20 @@ public abstract class ProjectViewSelectInTarget extends SelectInTargetPsiWrapper @Nullable final String subviewId, final VirtualFile virtualFile, final boolean requestFocus) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "ProjectViewSelectInTarget.select: " + + "project=" + project + + ", toSelect=" + toSelect + + ", viewId=" + viewId + + ", subviewId=" + subviewId + + ", virtualFile=" + virtualFile + + ", requestFocus=" + requestFocus + ); + } ProjectView projectView = ProjectView.getInstance(project); if (projectView == null) { + SelectInProjectViewImplKt.getLOG().debug("Not selecting anything because there is no project view"); return ActionCallback.REJECTED; } @@ -63,6 +76,9 @@ public abstract class ProjectViewSelectInTarget extends SelectInTargetPsiWrapper if (ApplicationManager.getApplication().isUnitTestMode()) { AbstractProjectViewPane pane = projectView.getProjectViewPaneById(id); if (pane != null) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Switching to pane " + pane); + } pane.select(toSelect, virtualFile, requestFocus); } return ActionCallback.DONE; @@ -74,20 +90,30 @@ public abstract class ProjectViewSelectInTarget extends SelectInTargetPsiWrapper ToolWindow projectViewToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.PROJECT_VIEW); if (projectViewToolWindow == null) { + SelectInProjectViewImplKt.getLOG().debug("Not selecting anything because there is no project view tool window"); return ActionCallback.REJECTED; } ActionCallback result = new ActionCallback(); Runnable runnable = () -> { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + (requestFocus ? "Activated" : "Shown") + + ". Changing project view to " + id + " / " + subviewId + ", will continue once changed" + ); + } projectView.changeViewCB(id, subviewId).doWhenProcessed(() -> { + SelectInProjectViewImplKt.getLOG().debug("Changed. Delegating to SelectInProjectViewImpl to continue"); project.getService(SelectInProjectViewImpl.class).ensureSelected(id, virtualFile, toSelectSupplier, requestFocus, true, result); }); }; if (requestFocus) { + SelectInProjectViewImplKt.getLOG().debug("Activating the project view tool window, will continue once activated"); projectViewToolWindow.activate(runnable, true); } else { + SelectInProjectViewImplKt.getLOG().debug("Showing the project view tool window, will continue once shown"); projectViewToolWindow.show(runnable); } @@ -114,7 +140,18 @@ public abstract class ProjectViewSelectInTarget extends SelectInTargetPsiWrapper protected boolean canSelect(PsiFileSystemItem file) { VirtualFile vFile = PsiUtilCore.getVirtualFile(file); vFile = vFile == null ? null : BackedVirtualFile.getOriginFileIfBacked(vFile); - if (vFile == null || !vFile.isValid()) return false; + if (vFile == null) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can NOT select " + file + " because its virtual file is null"); + } + return false; + } + else if (!vFile.isValid()) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can NOT select " + file + " because its virtual file " + vFile + " is invalid"); + } + return false; + } return canBeSelectedInProjectView(myProject, vFile); } @@ -126,6 +163,13 @@ public abstract class ProjectViewSelectInTarget extends SelectInTargetPsiWrapper @Override public void select(PsiElement element, final boolean requestFocus) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "ProjectViewSelectInTarget.selectIn: Select in " + this + + ", requestFocus=" + requestFocus + + ", element=" + element + ); + } PsiUtilCore.ensureValid(element); PsiElement toSelect = null; for (TreeStructureProvider provider : getProvidersDumbAware()) { @@ -140,6 +184,9 @@ public abstract class ProjectViewSelectInTarget extends SelectInTargetPsiWrapper } } + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Top level element is " + toSelect); + } toSelect = findElementToSelect(element, toSelect); if (toSelect != null) { diff --git a/platform/lang-impl/src/com/intellij/ide/impl/SelectInTargetPsiWrapper.java b/platform/lang-impl/src/com/intellij/ide/impl/SelectInTargetPsiWrapper.java index d7f6bf040387..1a67b746c201 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/SelectInTargetPsiWrapper.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/SelectInTargetPsiWrapper.java @@ -3,6 +3,7 @@ package com.intellij.ide.impl; import com.intellij.ide.SelectInContext; import com.intellij.ide.SelectInTarget; +import com.intellij.ide.projectView.impl.SelectInProjectViewImplKt; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -32,14 +33,28 @@ public abstract class SelectInTargetPsiWrapper implements SelectInTarget { protected boolean canSelectInner(@NotNull SelectInContext context) { PsiFileSystemItem psiFile = getContextPsiFile(context); - return psiFile != null && canSelect(psiFile); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("PSI file for context " + context + " is " + psiFile); + } + if (psiFile == null) { + return false; + } + boolean canSelect = canSelect(psiFile); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can " + (canSelect ? "select" : "NOT select") + " file " + psiFile + " in " + this); + } + return canSelect; } private boolean isContextValid(SelectInContext context) { if (myProject.isDisposed()) return false; VirtualFile virtualFile = context.getVirtualFile(); - return virtualFile.isValid(); + boolean valid = virtualFile.isValid(); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("File " + virtualFile + " is " + (valid ? "valid" : "NOT valid")); + } + return valid; } @Nullable @@ -61,10 +76,23 @@ public abstract class SelectInTargetPsiWrapper implements SelectInTarget { @Override public final void selectIn(@NotNull SelectInContext context, boolean requestFocus) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "SelectInTargetPsiWrapper.selectIn: Select in " + this + + ", requestFocus=" + requestFocus + + " using context " + context + ); + } VirtualFile file = context.getVirtualFile(); Object selector = context.getSelectorInFile(); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("File is " + file + ", selector is " + selector); + } if (selector == null) { selector = PsiUtilCore.findFileSystemItem(myProject, file); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Falling back to selector " + selector); + } } if (selector instanceof PsiElement) { @@ -74,10 +102,16 @@ public abstract class SelectInTargetPsiWrapper implements SelectInTarget { throw new PsiInvalidElementAccessException(original, "Returned by " + selector + " of " + selector.getClass()); } try (var ignored = SlowOperations.startSection(SlowOperations.ACTION_PERFORM)) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Selecting " + original); + } select(original, requestFocus); } } else { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Selecting non-PSI selector " + selector + " in file " + file); + } select(selector, file, requestFocus); } } @@ -93,12 +127,18 @@ public abstract class SelectInTargetPsiWrapper implements SelectInTarget { if (toSelect == null) { if (element instanceof PsiFile || element instanceof PsiDirectory) { toSelect = element; + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Will select PSI file/dir " + toSelect); + } } else { PsiFile containingFile = element.getContainingFile(); if (containingFile != null) { FileViewProvider viewProvider = containingFile.getViewProvider(); toSelect = viewProvider.getPsi(viewProvider.getBaseLanguage()); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Will select PSI element " + toSelect + " provided by " + viewProvider); + } } } } @@ -107,6 +147,9 @@ public abstract class SelectInTargetPsiWrapper implements SelectInTarget { PsiElement originalElement = null; try { originalElement = toSelect.getOriginalElement(); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Original element to select is " + originalElement); + } } catch (IndexNotReadyException ignored) { } if (originalElement != null) { diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AsyncProjectViewSupport.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AsyncProjectViewSupport.java index 781c619b8913..9cef492bf9aa 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AsyncProjectViewSupport.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AsyncProjectViewSupport.java @@ -144,9 +144,19 @@ public final class AsyncProjectViewSupport { } public ActionCallback select(JTree tree, Object object, VirtualFile file) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "AsyncProjectViewSupport.select: " + + "object=" + object + + ", file=" + file + ); + } if (object instanceof AbstractTreeNode node) { object = node.getValue(); LOG.debug("select AbstractTreeNode"); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Retrieved the value from the node: " + object); + } } PsiElement element = object instanceof PsiElement ? (PsiElement)object : null; LOG.debug("select object: ", object, " in file: ", file); @@ -156,22 +166,47 @@ public final class AsyncProjectViewSupport { ActionCallback callback = new ActionCallback(); //noinspection CodeBlock2Expr + SelectInProjectViewImplKt.getLOG().debug("Updating nodes before selecting"); myNodeUpdater.updateImmediately(() -> expand(tree, promise -> { + SelectInProjectViewImplKt.getLOG().debug("Updated nodes"); promise.onSuccess(o -> callback.setDone()); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Collecting paths to select"); + } acceptOnEDT(visitor, () -> { - if (selectPaths(tree, pathsToSelect, visitor) || + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Collected paths to the element: " + pathsToSelect); + } + boolean selected = selectPaths(tree, pathsToSelect, visitor); + if (selected || element == null || file == null || Registry.is("async.project.view.support.extra.select.disabled")) { + if (selected) { + SelectInProjectViewImplKt.getLOG().debug("Selected successfully. Done"); + } + else { + SelectInProjectViewImplKt.getLOG().debug("Couldn't select, but there's nothing else to do. Done"); + } promise.setResult(null); } else { + SelectInProjectViewImplKt.getLOG().debug("Couldn't select the element, falling back to selecting the file"); // try to search the specified file instead of element, // because Kotlin files cannot represent containing functions pathsToSelect.clear(); TreeVisitor fileVisitor = AbstractProjectViewPane.createVisitor(null, file, pathsToSelect); acceptOnEDT(fileVisitor, () -> { - selectPaths(tree, pathsToSelect, fileVisitor); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Collected paths to the file: " + pathsToSelect); + } + boolean selectedFile = selectPaths(tree, pathsToSelect, fileVisitor); + if (selectedFile) { + SelectInProjectViewImplKt.getLOG().debug("Selected successfully. Done"); + } + else { + SelectInProjectViewImplKt.getLOG().debug("Couldn't select, but there's nothing else to do. Done"); + } promise.setResult(null); }); } @@ -185,7 +220,10 @@ public final class AsyncProjectViewSupport { } private static boolean selectPaths(@NotNull JTree tree, @NotNull List paths, @NotNull TreeVisitor visitor) { - if (paths.isEmpty()) return false; + if (paths.isEmpty()) { + SelectInProjectViewImplKt.getLOG().debug("Nothing to select"); + return false; + } if (paths.size() > 1) { if (visitor instanceof ProjectViewNodeVisitor nodeVisitor) { return selectPaths(tree, new SelectionDescriptor(nodeVisitor.getElement(), nodeVisitor.getFile(), paths)); @@ -197,6 +235,9 @@ public final class AsyncProjectViewSupport { TreePath path = paths.get(0); tree.expandPath(path); // request to expand found path TreeUtil.selectPaths(tree, path); // select and scroll to center + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Selected the only path: " + path); + } return true; } @@ -204,6 +245,9 @@ public final class AsyncProjectViewSupport { List adjustedPaths = ProjectViewPaneSelectionHelper.getAdjustedPaths(selectionDescriptor); adjustedPaths.forEach(it -> tree.expandPath(it)); TreeUtil.selectPaths(tree, adjustedPaths); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Selected paths adjusted according to " + selectionDescriptor + ": " + adjustedPaths); + } return true; } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java index b73666c60b02..36c1f4289e18 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -151,6 +151,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo getDefaultState().setAutoscrollFromSource(selected); getGlobalOptions().setAutoscrollFromSource(selected); if (selected && !myAutoScrollFromSourceHandler.isCurrentProjectViewPaneFocused()) { + SelectInProjectViewImplKt.getLOG().debug("Invoking scroll from source because Always Select Opened File has been turned on"); myAutoScrollFromSourceHandler.scrollFromSource(false); } } @@ -580,6 +581,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo if (ToolWindowId.PROJECT_VIEW.equals(toolWindow.getId())) { AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane(); if (currentProjectViewPane != null && isAutoscrollFromSource(currentProjectViewPane.getId())) { + SelectInProjectViewImplKt.getLOG().debug("Invoking scroll from source because the project view is shown"); myAutoScrollFromSourceHandler.scrollFromSource(false); } } @@ -877,6 +879,9 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo newPane.restoreExpandedPaths(); if (selectedUserObject != null && newSubId != null) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Re-selecting " + selectedUserObject + " after switching to " + myCurrentViewId); + } myProject.getService(SelectInProjectViewImpl.class).ensureSelected( myCurrentViewId, null, @@ -973,6 +978,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo JComponent component = editor.getComponent(); for (FileEditor fileEditor : FileEditorManager.getInstance(myProject).getAllEditors()) { if (SwingUtilities.isDescendingFrom(component, fileEditor.getComponent())) { + SelectInProjectViewImplKt.getLOG().debug("Invoking scroll from source because the editor has gained focus"); myAutoScrollFromSourceHandler.scrollFromSource(false); break; } @@ -1075,6 +1081,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo target.setSubId(subId); } if (isAutoscrollFromSource(id)) { + SelectInProjectViewImplKt.getLOG().debug("Invoking scroll from source because the project view has changed panes"); myAutoScrollFromSourceHandler.scrollFromSource(false); } } @@ -1152,9 +1159,16 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo @Override public void select(final Object element, VirtualFile file, boolean requestFocus) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("select: element=" + element + ", file=" + file + ", requestFocus=" + requestFocus); + } final AbstractProjectViewPane viewPane = getCurrentProjectViewPane(); if (viewPane != null) { myAutoScrollOnFocusEditor.set(!requestFocus); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Delegating to AbstractProjectViewPane, auto scroll enabled=" + + myAutoScrollOnFocusEditor.get()); + } viewPane.select(element, file, requestFocus); } } @@ -1165,11 +1179,24 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo boolean requestFocus, @Nullable ActionCallback result ) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "ProjectViewImpl.select: " + + "elementSupplier=" + elementSupplier + + ", file=" + virtualFile + + ", requestFocus=" + requestFocus + + ", result=" + result + ); + } + SelectInProjectViewImplKt.getLOG().debug("Starting a read action in background to retrieve the element from the supplier"); ReadAction .nonBlocking(elementSupplier::get) .finishOnUiThread( ModalityState.defaultModalityState(), element -> { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Retrieved the element from the supplier: " + element); + } var callback = selectCB(element, virtualFile, requestFocus); if (result != null) { callback.notify(result); @@ -1182,9 +1209,16 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo @NotNull @Override public ActionCallback selectCB(Object element, VirtualFile file, boolean requestFocus) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("ProjectViewImpl.selectCB: element=" + element + ", file=" + file + ", requestFocus=" + requestFocus); + } final AbstractProjectViewPane viewPane = getCurrentProjectViewPane(); if (viewPane instanceof AbstractProjectViewPaneWithAsyncSupport) { myAutoScrollOnFocusEditor.set(!requestFocus); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Delegating to AbstractProjectViewPaneWithAsyncSupport, auto scroll enabled=" + + myAutoScrollOnFocusEditor.get()); + } return ((AbstractProjectViewPaneWithAsyncSupport)viewPane).selectCB(element, file, requestFocus); } select(element, file, requestFocus); @@ -1745,6 +1779,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo protected void selectElementFromEditor(@NotNull FileEditor fileEditor) { if (myProject.isDisposed() || !myViewContentPanel.isShowing()) return; if (isAutoscrollFromSource(getCurrentViewId()) && !isCurrentProjectViewPaneFocused()) { + SelectInProjectViewImplKt.getLOG().debug("Invoking scroll from source because the selected editor tab has been changed"); scrollFromSource(fileEditor, false); } } @@ -1833,6 +1868,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo } void selectOpenedFile() { + SelectInProjectViewImplKt.getLOG().debug("Invoking scroll from source because Select Opened File was performed manually"); myAutoScrollFromSourceHandler.scrollFromSource(true); } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/SelectInProjectViewImpl.kt b/platform/lang-impl/src/com/intellij/ide/projectView/impl/SelectInProjectViewImpl.kt index 0007486cca39..ea689b386fae 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/SelectInProjectViewImpl.kt +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/SelectInProjectViewImpl.kt @@ -48,6 +48,9 @@ internal class SelectInProjectViewImpl( } private fun invokeWithSemaphore(taskName: String, task: suspend () -> Unit, onDone: (() -> Unit)?) { + if (LOG.isDebugEnabled) { + LOG.debug("Attempting to start $taskName") + } coroutineScope.launch( CoroutineName(taskName), start = CoroutineStart.UNDISPATCHED @@ -56,6 +59,9 @@ internal class SelectInProjectViewImpl( tasks.incrementAndGet() semaphore.withPermit { yield() // Ensure the coroutine is redispatched, even if withPermit() didn't suspend, to free the EDT. + if (LOG.isDebugEnabled) { + LOG.debug("Started $taskName") + } task() } } @@ -65,6 +71,9 @@ internal class SelectInProjectViewImpl( } finally { tasks.decrementAndGet() + if (LOG.isDebugEnabled) { + LOG.debug("Finished $taskName") + } } } } @@ -77,6 +86,9 @@ internal class SelectInProjectViewImpl( } private suspend fun doSelectInCurrentTarget(fileEditor: FileEditor?, invokedManually: Boolean) { + if (LOG.isDebugEnabled) { + LOG.debug("doSelectInCurrentTarget: fileEditor=$fileEditor, invokedManually=$invokedManually") + } val editorsToCheck = if (fileEditor == null) allEditors() else listOf(fileEditor) selectInCurrentTarget(invokedManually, editorsToCheck) } @@ -88,12 +100,22 @@ internal class SelectInProjectViewImpl( AdvancedSettings.getBoolean("project.view.do.not.autoscroll.to.libraries") && readAction { fileEditor.file?.let { file -> ProjectFileIndex.getInstance(project).isInLibrary(file) } == true } ) { + if (LOG.isDebugEnabled) { + LOG.debug("Skipping $fileEditor because the file is in a library and autoscroll to libraries is off") + } continue } val psiFilePointer = getPsiFilePointer(fileEditor) if (psiFilePointer != null) { + if (LOG.isDebugEnabled) { + LOG.debug("Trying to select using $fileEditor with psiFilePointer=$psiFilePointer") + } withContext(Dispatchers.EDT) { - createSelectInContext(psiFilePointer, fileEditor).selectInCurrentTarget(requestFocus = invokedManually) + val selectInContext = createSelectInContext(psiFilePointer, fileEditor) + if (LOG.isDebugEnabled) { + LOG.debug("Created select-in context and delegating to it: $selectInContext") + } + selectInContext.selectInCurrentTarget(requestFocus = invokedManually) } break } @@ -169,20 +191,48 @@ internal class SelectInProjectViewImpl( allowSubIdChange: Boolean, result: ActionCallback?, ) { + if (LOG.isDebugEnabled) { + LOG.debug("doEnsureSelected: " + + "paneId=$paneId, " + + "virtualFile=$virtualFile, " + + "elementSupplier=$elementSupplier, " + + "requestFocus=$requestFocus, " + + "allowSubIdChange=$allowSubIdChange, " + + "result=$result" + ) + } val projectView = project.serviceOrNull() as ProjectViewImpl? if (projectView == null) { + LOG.debug("Not selecting anything because there is no project view") result?.setRejected() return } val pane = if (requestFocus) null else projectView.getProjectViewPaneById(paneId) val target = if (pane == null) null else projectView.getProjectViewSelectInTarget(pane) if (!allowSubIdChange) { + if (LOG.isDebugEnabled) { + LOG.debug("SubId change not allowed, checking: " + + "pane=$pane, " + + "target=$target, " + + "virtualFile=$virtualFile, " + + "and isSubIdSelectable" + ) + } val isSelectableInCurrentSubId = - pane != null && - target != null && - virtualFile != null && - readAction { - target.isSubIdSelectable(pane.subId, FileSelectInContext(project, virtualFile, null)) + if (pane == null || target == null || virtualFile == null) { + if (LOG.isDebugEnabled) { + LOG.debug("File $virtualFile is NOT selectable because there's not enough non-null parameters to go on, not selecting anything") + } + false + } + else { + val isSubIdSelectable = readAction { + target.isSubIdSelectable(pane.subId, FileSelectInContext(project, virtualFile, null)) + } + if (LOG.isDebugEnabled && !isSubIdSelectable) { + LOG.debug("File $virtualFile is NOT selectable in $pane with target $target and changing subId is not allowed, not selecting anything") + } + isSubIdSelectable } if (!isSelectableInCurrentSubId) { return @@ -191,6 +241,9 @@ internal class SelectInProjectViewImpl( val visibleAndSelectedUserObject = withContext(Dispatchers.EDT) { pane?.visibleAndSelectedUserObject } + if (LOG.isDebugEnabled && !requestFocus) { // if requestFocus, pane is always null + LOG.debug("Currently visible and selected node is $visibleAndSelectedUserObject") + } data class SelectionContext( val isAlreadyVisibleAndSelected: Boolean, @@ -198,17 +251,30 @@ internal class SelectInProjectViewImpl( ) val context = readAction { - val elementToSelect = elementSupplier.get() ?: virtualFile ?: return@readAction null + val suppliedElement = elementSupplier.get() + val elementToSelect = suppliedElement ?: virtualFile ?: return@readAction null + if (LOG.isDebugEnabled) { + LOG.debug("Element to select is $elementToSelect (from ${if (suppliedElement == null) "virtual file" else "supplier"})") + } + val isAlreadyVisibleAndSelected = visibleAndSelectedUserObject != null && visibleAndSelectedUserObject.canRepresent(elementToSelect) + if (LOG.isDebugEnabled && isAlreadyVisibleAndSelected) { + LOG.debug("This element is already visible and selected") + } SelectionContext( - visibleAndSelectedUserObject != null && visibleAndSelectedUserObject.canRepresent(elementToSelect), + isAlreadyVisibleAndSelected, virtualFile ?: (elementToSelect as? PsiElement)?.virtualFile ) } + if (LOG.isDebugEnabled) { + LOG.debug("The selection context is $context") + } if (context == null || context.isAlreadyVisibleAndSelected || context.virtualFile == null) { + LOG.debug("Nothing to do") result?.setDone() return } withContext(Dispatchers.EDT) { + LOG.debug("Delegating back to the project view") projectView.select(elementSupplier, context.virtualFile, requestFocus, result) } } @@ -220,9 +286,19 @@ internal class SelectInProjectViewImpl( } private suspend fun doSelectInAnyTarget(context: SelectInContext, targets: Collection, requestFocus: Boolean) { + if (LOG.isDebugEnabled) { + LOG.debug("doSelectInAnyTarget: context=$context, targets=$targets, requestFocus=$requestFocus") + } for (target in targets) { - if (readAction { target.canSelect(context) }) { + val canSelect = readAction { target.canSelect(context) } + if (LOG.isDebugEnabled) { + LOG.debug("${if (canSelect) "Can" else "Can NOT"} select $context in $target") + } + if (canSelect) { withContext(Dispatchers.EDT) { + if (LOG.isDebugEnabled) { + LOG.debug("Selecting $context in $target") + } target.selectIn(context, requestFocus) } return @@ -242,12 +318,18 @@ internal class SelectInProjectViewImpl( file: VirtualFile, requestFocus: Boolean, ) { + if (LOG.isDebugEnabled()) { + LOG.debug("doSelectInScopeViewPane: pane=$pane, pointer=$pointer, file=$file, requestFocus=$requestFocus") + } val currentFilter = pane.getCurrentFilter() val allFilters = pane.filters.toMutableList() allFilters.remove(currentFilter) allFilters.add(0, currentFilter) // Start with the current filter and then fall back to others. for (filter in allFilters) { if (readAction { filter.accept(file) }) { + if (LOG.isDebugEnabled()) { + LOG.debug("The file $file has been accepted by the filter $filter, delegating to the pane") + } withContext(Dispatchers.EDT) { pane.select(pointer, file, requestFocus, filter) } @@ -269,10 +351,18 @@ private open class SimpleSelectInContext( } open suspend fun selectInCurrentTarget(requestFocus: Boolean) { - val currentTarget = (project.serviceOrNull() as ProjectViewImpl?)?.currentSelectInTarget ?: return + val currentTarget = (project.serviceOrNull() as ProjectViewImpl?)?.currentSelectInTarget + if (LOG.isDebugEnabled) { + LOG.debug("The current target is $currentTarget") + } + if (currentTarget == null) return currentTarget.selectIn(this, requestFocus) } + override fun toString(): String { + return "SimpleSelectInContext(project=$project) ${super.toString()}" + } + } private class EditorSelectInContext( @@ -287,12 +377,22 @@ private class EditorSelectInContext( withContext(Dispatchers.EDT) { while (true) { if (editor.isDisposed) { + LOG.debug("Not selecting anything because the editor is disposed") break } val offset = editor.caretModel.offset - constrainedReadAction(ReadConstraint.withDocumentsCommitted(project)) { + if (LOG.isDebugEnabled) { + LOG.debug("Looking for the element at offset $offset") + } + val element = constrainedReadAction(ReadConstraint.withDocumentsCommitted(project)) { psiFile?.findElementAt(offset) } + // No, the fact that the element is only used for logging isn't a bug: + // the whole point of the read action above is to ensure the document is committed + // and parsed before selecting the current element, otherwise it may be outdated. + if (LOG.isDebugEnabled) { + LOG.debug("The element is $element") + } if (editor.caretModel.offset == offset && PsiDocumentManager.getInstance(project).isCommitted(editor.document)) { super.selectInCurrentTarget(requestFocus) break @@ -312,6 +412,10 @@ private class EditorSelectInContext( } return file } + + override fun toString(): String { + return "EditorSelectInContext(editor=$editor) ${super.toString()}" + } } private fun T.nullIfInvalid(): T? = if (isValid) this else null @@ -319,4 +423,4 @@ private fun T.nullIfDisposed(): T? = if (isDisposed) null else this private fun T.nullIfInvalid(): T? = if (isValid) this else null private val PsiElement.virtualFile: VirtualFile? get() = PsiUtilCore.getVirtualFile(this) -private val LOG = logger() +internal val LOG = logger() diff --git a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java index ab652561442f..f8d4e305132d 100644 --- a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java +++ b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java @@ -7,6 +7,7 @@ import com.intellij.ide.SelectInContext; import com.intellij.ide.StandardTargetWeights; import com.intellij.ide.impl.ProjectViewSelectInTarget; import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.projectView.impl.SelectInProjectViewImplKt; import com.intellij.notebook.editor.BackedVirtualFile; import com.intellij.openapi.extensions.AreaInstance; import com.intellij.openapi.project.Project; @@ -51,8 +52,12 @@ public final class ScopePaneSelectInTarget extends ProjectViewSelectInTarget { @Override public void select(PsiElement element, boolean requestFocus) { if (getSubId() == null) { + SelectInProjectViewImplKt.getLOG().debug("getSubId() == null, looking for a fallback"); PsiFile file = element.getContainingFile(); NamedScopeFilter filter = getContainingFilter(file == null ? null : file.getVirtualFile()); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("The fallback is " + filter); + } if (filter == null) return; setSubId(filter.toString()); } @@ -71,11 +76,29 @@ public final class ScopePaneSelectInTarget extends ProjectViewSelectInTarget { @Override public boolean isSubIdSelectable(@NotNull String subId, @NotNull SelectInContext context) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("ScopePaneSelectInTarget.isSubIdSelectable: subId=" + subId + ", context is " + context); + } PsiFileSystemItem file = getContextPsiFile(context); - if (!(file instanceof PsiFile)) return false; + if (!(file instanceof PsiFile)) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can NOT select " + file + " because it's not a PsiFile"); + } + return false; + } ScopeViewPane pane = getScopeViewPane(); NamedScopeFilter filter = pane == null ? null : pane.getFilter(subId); - return filter != null && filter.accept(file.getVirtualFile()); + if (filter == null) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can NOT select " + file + " because filter is null"); + } + return false; + } + boolean accept = filter.accept(file.getVirtualFile()); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("The filter " + filter + (accept ? "accepts" : "does NOT accept") + " file " + file); + } + return accept; } private ScopeViewPane getScopeViewPane() { diff --git a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewPane.java b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewPane.java index 3c3b6dda7244..75861e0aaca5 100644 --- a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewPane.java @@ -237,11 +237,17 @@ public final class ScopeViewPane extends AbstractProjectViewPane { @Override public void select(Object object, VirtualFile file, boolean requestFocus) { if (myTreeModel.get() == null) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can NOT select " + object + " / " + file + " in " + this + " because the scope pane isn't initialized yet"); + } // not initialized yet return; } if (file == null) { LOG.warn(new IllegalArgumentException("ScopeViewPane.select: file==null, object=" + object)); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Can NOT select " + object + " / " + file + " in " + this + " because the file is null"); + } return; // Filters don't accept null files anyway, so just do nothing. } @@ -255,6 +261,9 @@ public final class ScopeViewPane extends AbstractProjectViewPane { LOG.warn("ScopeViewPane.select(object=" + object + ",file=" + file + ",requestFocus=" + requestFocus + "): element invalidated"); } } + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Select " + object + " / " + file + " in " + this); + } myProject.getService(SelectInProjectViewImpl.class).selectInScopeViewPane(this, pointer, file, requestFocus); } @@ -267,33 +276,85 @@ public final class ScopeViewPane extends AbstractProjectViewPane { @ApiStatus.Internal public void select(@Nullable SmartPsiElementPointer pointer, VirtualFile file, boolean requestFocus, VirtualFileFilter filter) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "ScopeViewPane.select: " + + "pane=" + this + + ", pointer=" + pointer + + ", file=" + file + + ", requestFocus=" + requestFocus + + ", filter=" + filter + ); + } String subId = filter.toString(); if (!Objects.equals(subId, getSubId())) { - if (!requestFocus) return; - selectScopeView(subId); + if (requestFocus) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "Selected subId=" + getSubId() + + ", requested subId=" + subId + + ", changing the scope" + ); + } + selectScopeView(subId); + } + else { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug( + "Selected subId=" + getSubId() + + ", requested subId=" + subId + + ", changing not allowed because requestFocus=false, aborting" + ); + } + return; + } } if (LOG.isDebugEnabled()) { LOG.debug("select element: ", (pointer == null ? null : pointer.getElement()), " in file: ", file); } TreeVisitor visitor = AbstractProjectViewPane.createVisitorByPointer(pointer, file); - if (visitor == null) return; + if (visitor == null) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Not selecting anything because both the pointer and file are null"); + } + return; + } JTree tree = myTree; - myTreeModel.get().getUpdater().updateImmediately(() -> TreeState.expand(tree, promise -> TreeUtil.visit(tree, visitor, path -> { - if (selectPath(tree, path) || pointer == null || Registry.is("async.project.view.support.extra.select.disabled")) { - promise.setResult(null); - } - else { - // try to search the specified file instead of element, - // because Kotlin files cannot represent containing functions - TreeUtil.visit(tree, AbstractProjectViewPane.createVisitor(file), path2 -> { - selectPath(tree, path2); + SelectInProjectViewImplKt.getLOG().debug("Start updating the tree. Will continue once updated"); + myTreeModel.get().getUpdater().updateImmediately(() -> { + SelectInProjectViewImplKt.getLOG().debug("Updated. Start expanding the tree and looking for the path to select"); + TreeState.expand(tree, promise -> TreeUtil.visit(tree, visitor, path -> { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Expanded. The path to select is " + path); + } + if (selectPath(tree, path) || pointer == null || Registry.is("async.project.view.support.extra.select.disabled")) { promise.setResult(null); - }); - } - }))); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Selected. Done"); + } + } + else { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Not selected. Trying to look for the file without the pointer instead"); + } + // try to search the specified file instead of element, + // because Kotlin files cannot represent containing functions + TreeUtil.visit(tree, AbstractProjectViewPane.createVisitor(file), path2 -> { + selectPath(tree, path2); + promise.setResult(null); + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("Found and selected " + path2); + } + }); + } + })); + }); } - private static boolean selectPath(@NotNull JTree tree, TreePath path) { + private boolean selectPath(@NotNull JTree tree, TreePath path) { + if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) { + SelectInProjectViewImplKt.getLOG().debug("selectPath: " + path + " in " + this); + } if (path == null) { return false; } @@ -444,4 +505,9 @@ public final class ScopeViewPane extends AbstractProjectViewPane { public boolean supportsShowModules() { return PlatformUtils.isIntelliJ(); } + + @Override + public String toString() { + return "ScopeViewPane{id=" + getId() + ",subId=" + getSubId() + "}"; + } } diff --git a/platform/platform-api/src/com/intellij/ide/FileSelectInContext.java b/platform/platform-api/src/com/intellij/ide/FileSelectInContext.java index ad7e3f80aa38..adaaa0ce2af3 100644 --- a/platform/platform-api/src/com/intellij/ide/FileSelectInContext.java +++ b/platform/platform-api/src/com/intellij/ide/FileSelectInContext.java @@ -57,4 +57,13 @@ public class FileSelectInContext implements SelectInContext { FileEditorManager manager = FileEditorManager.getInstance(project); return manager == null ? null : () -> getFirstElement(manager.openFile(file, false)); } + + @Override + public String toString() { + return "FileSelectInContext{" + + "myProject=" + myProject + + ", myFile=" + myFile + + ", myProvider=" + myProvider + + '}'; + } } diff --git a/platform/platform-api/src/com/intellij/ide/SmartSelectInContext.java b/platform/platform-api/src/com/intellij/ide/SmartSelectInContext.java index dc2ab0169ede..c7aef6808ace 100644 --- a/platform/platform-api/src/com/intellij/ide/SmartSelectInContext.java +++ b/platform/platform-api/src/com/intellij/ide/SmartSelectInContext.java @@ -41,4 +41,11 @@ public class SmartSelectInContext extends FileSelectInContext { Object selector = pointer.getElement(); return selector instanceof PsiFile ? (PsiFile)selector : null; } + + @Override + public String toString() { + return "SmartSelectInContext{" + + "pointer=" + pointer + + "} " + super.toString(); + } }