From d098eb720d7ea2912b18f09a1740227662222115 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Mon, 13 Oct 2025 23:02:43 +0200 Subject: [PATCH] [terminal] IJPL-206783 detect absolute paths as hyperlinks on mouse hover in the reworked terminal This feature can be disabled by `terminal.generic.hyperlinks` registry key. GitOrigin-RevId: d330c37cb8655eb8bdfc558b887c65ec651f70b1 --- .../terminal/resources/META-INF/terminal.xml | 3 ++- .../hyperlinks/CompositeFilterWrapper.kt | 15 +++++++------ ...Filter.kt => TerminalGenericFileFilter.kt} | 21 ++++++++++++++++++- .../hyperlinks/TerminalFilterScope.kt | 9 ++++++++ 4 files changed, 38 insertions(+), 10 deletions(-) rename plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/{GenericFileFilter.kt => TerminalGenericFileFilter.kt} (90%) create mode 100644 plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/TerminalFilterScope.kt diff --git a/plugins/terminal/resources/META-INF/terminal.xml b/plugins/terminal/resources/META-INF/terminal.xml index 5f83053c1e89..5bb53d43bf33 100644 --- a/plugins/terminal/resources/META-INF/terminal.xml +++ b/plugins/terminal/resources/META-INF/terminal.xml @@ -65,9 +65,10 @@ description="Whether popup-based command completion feature is enabled in the Reworked Terminal"/> - + diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/hyperlinks/CompositeFilterWrapper.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/hyperlinks/CompositeFilterWrapper.kt index a446955d3c61..d48d22b3e298 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/hyperlinks/CompositeFilterWrapper.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/hyperlinks/CompositeFilterWrapper.kt @@ -5,18 +5,17 @@ import com.intellij.execution.filters.CompositeFilter import com.intellij.execution.filters.ConsoleFilterProvider import com.intellij.execution.filters.Filter import com.intellij.execution.impl.ConsoleViewUtil -import com.intellij.openapi.application.* +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.UiWithModelAccess +import com.intellij.openapi.application.asContextElement +import com.intellij.openapi.application.readAction import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.* import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.TestOnly +import org.jetbrains.plugins.terminal.hyperlinks.TerminalFilterScope import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.atomic.AtomicBoolean @@ -59,7 +58,7 @@ class CompositeFilterWrapper(private val project: Project, coroutineScope: Corou private suspend fun computeFilter(): CompositeFilter { val filters = readAction { - ConsoleViewUtil.computeConsoleFilters(project, null, GlobalSearchScope.allScope(project)) + ConsoleViewUtil.computeConsoleFilters(project, null, TerminalFilterScope(project)) } return CompositeFilter(project, customFilters + filters).also { it.setForceUseAllFilters(true) diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/GenericFileFilter.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalGenericFileFilter.kt similarity index 90% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/GenericFileFilter.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalGenericFileFilter.kt index bdf39dbc9046..69cbb3d7ebb7 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/GenericFileFilter.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalGenericFileFilter.kt @@ -1,13 +1,17 @@ package org.jetbrains.plugins.terminal.block.reworked.hyperlinks +import com.intellij.execution.filters.ConsoleFilterProviderEx import com.intellij.execution.filters.Filter import com.intellij.execution.filters.OpenFileHyperlinkInfo import com.intellij.openapi.editor.markup.EffectType import com.intellij.openapi.editor.markup.TextAttributes import com.intellij.openapi.project.Project +import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.psi.search.GlobalSearchScope import com.intellij.ui.JBColor +import org.jetbrains.plugins.terminal.hyperlinks.TerminalFilterScope import java.awt.Font internal enum class ParsingState { @@ -21,7 +25,10 @@ internal enum class ParsingState { * output, sync output, run test output, built-in terminal emulator, etc. Therefore, we manually parse the string instead of using regex * for maximum performance. */ -internal class GenericFileFilter(private val project: Project, private val localFileSystem: LocalFileSystem) : Filter { +internal class TerminalGenericFileFilter( + private val project: Project, + private val localFileSystem: LocalFileSystem +) : Filter { companion object { /** * Max filename considered during parsing. Do not confuse with file path, which may contain several file names separated by '/' or '\'. @@ -226,3 +233,15 @@ private fun String.takeWhileFromIndex(index: Int, predicate: (Char) -> Boolean): private val EMPTY_ATTRS: TextAttributes = TextAttributes(null, null, null, null, Font.PLAIN) private val HOVERED_ATTRS: TextAttributes = TextAttributes(null, null, JBColor.BLACK, EffectType.LINE_UNDERSCORE, Font.PLAIN) + + +internal class TerminalGenericFileFilterProvider : ConsoleFilterProviderEx { + override fun getDefaultFilters(project: Project, scope: GlobalSearchScope): Array { + if (scope is TerminalFilterScope && Registry.`is`("terminal.generic.hyperlinks", false)) { + return arrayOf(TerminalGenericFileFilter(project, LocalFileSystem.getInstance())) + } + return emptyArray() + } + + override fun getDefaultFilters(project: Project): Array = emptyArray() +} diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/TerminalFilterScope.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/TerminalFilterScope.kt new file mode 100644 index 000000000000..998c88a015ed --- /dev/null +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/TerminalFilterScope.kt @@ -0,0 +1,9 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.terminal.hyperlinks + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.DelegatingGlobalSearchScope +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Internal +class TerminalFilterScope(project: Project): DelegatingGlobalSearchScope(allScope(project)) \ No newline at end of file