[terminal] initialize filters lazily; allow adding custom filters

GitOrigin-RevId: 98be53c6a86b78f9254353aace74e3b871439638
This commit is contained in:
Sergey Simonchik
2025-06-16 22:54:32 +02:00
committed by intellij-monorepo-bot
parent 2c5f4e42c2
commit 20e36a1bc2
2 changed files with 47 additions and 33 deletions

View File

@@ -11,54 +11,62 @@ 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 com.intellij.util.asDisposable
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.intellij.util.concurrency.SynchronizedClearableLazy
import kotlinx.coroutines.*
import org.jetbrains.annotations.TestOnly
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean
internal class CompositeFilterWrapper(private val project: Project, private val coroutineScope: CoroutineScope) {
private val filtersUpdatedListeners: MutableList<() -> Unit> = CopyOnWriteArrayList()
private val filtersComputationInProgress: AtomicBoolean = AtomicBoolean(false)
private val customFilters: MutableList<Filter> = CopyOnWriteArrayList()
@Volatile
private var cachedFilter: CompositeFilter? = null
@Volatile
private var filtersComputed: CompletableDeferred<Unit> = CompletableDeferred()
private var areFiltersInUse: Boolean = false
init {
ConsoleFilterProvider.FILTER_PROVIDERS.addChangeListener({
cachedFilter = null
filtersComputed = CompletableDeferred()
scheduleFiltersComputation()
}, coroutineScope.asDisposable())
scheduleFiltersComputation()
private val filterDeferredLazy: SynchronizedClearableLazy<Deferred<CompositeFilter>> = SynchronizedClearableLazy {
startFilterComputation()
}
private fun scheduleFiltersComputation() {
if (filtersComputationInProgress.compareAndSet(false, true)) {
coroutineScope.launch {
val filters = readAction {
ConsoleViewUtil.computeConsoleFilters(project, null, GlobalSearchScope.allScope(project))
}
filtersComputationInProgress.set(false)
cachedFilter = CompositeFilter(project, filters).also {
it.setForceUseAllFilters(true)
}
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
fireFiltersUpdated()
filtersComputed.complete(Unit)
}
}
init {
ConsoleFilterProvider.FILTER_PROVIDERS.addChangeListener(coroutineScope) {
dropFilter()
}
}
fun addFilter(filter: Filter) {
customFilters.add(filter)
dropFilter()
}
private fun dropFilter() {
cachedFilter = null
filterDeferredLazy.drop()
if (areFiltersInUse) {
// If filters have been requested already, there is some text in the editor.
// This text needs to be reprocessed with the updated filters.
// Trigger filter recomputation to fire the `filtersUpdated` event.
filterDeferredLazy.value
}
}
private fun startFilterComputation(): Deferred<CompositeFilter> = coroutineScope.async {
val filters = readAction {
ConsoleViewUtil.computeConsoleFilters(project, null, GlobalSearchScope.allScope(project))
}
val compositeFilter = CompositeFilter(project, customFilters + filters).also {
it.setForceUseAllFilters(true)
}
cachedFilter = compositeFilter
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
fireFiltersUpdated()
}
compositeFilter
}
fun addFiltersUpdatedListener(listener: () -> Unit) {
filtersUpdatedListeners.add(listener)
}
@@ -77,12 +85,13 @@ internal class CompositeFilterWrapper(private val project: Project, private val
cachedFilter?.let {
return it
}
scheduleFiltersComputation()
areFiltersInUse = true
filterDeferredLazy.value
return null
}
@TestOnly
internal suspend fun awaitFiltersComputed() {
filtersComputed.await()
filterDeferredLazy.value.await()
}
}

View File

@@ -1,6 +1,7 @@
// 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.block.reworked.hyperlinks
import com.intellij.execution.filters.Filter
import com.intellij.execution.impl.EditorHyperlinkListener
import com.intellij.execution.impl.EditorHyperlinkSupport
import com.intellij.execution.impl.ExpirableTokenProvider
@@ -79,6 +80,10 @@ class TerminalHyperlinkHighlighter private constructor(
}
}
internal fun addFilter(filter: Filter) {
filterWrapper.addFilter(filter)
}
private fun rehighlightAll() {
tokenProvider.invalidateAll()
hyperlinkSupport.clearHyperlinks(0, document.textLength)