From 1bd253cf765354b2fdca38ced68ac05460aca146 Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Fri, 13 Jun 2025 15:07:37 +0300 Subject: [PATCH] [terminal] IJPL-188621 Update font settings on the backend after resetting Extract the existing approach of modifying the settings to the utils. Otherwise, the value on the backend may be out of sync and then replace the updated value. GitOrigin-RevId: 435ec38adc52935ac994b9a645d62e92998b31e2 --- .../terminal/TerminalFontSettingsService.kt | 22 ++++++++------ .../terminal/TerminalOptionsProvider.kt | 20 ++----------- .../terminal/block/ui/TerminalUiUtils.kt | 29 +++++++++++++++++++ 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalFontSettingsService.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalFontSettingsService.kt index db1e23f61ed7..93da468a5535 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalFontSettingsService.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalFontSettingsService.kt @@ -15,7 +15,9 @@ import com.intellij.openapi.editor.colors.impl.AppFontOptions import com.intellij.openapi.editor.colors.impl.FontPreferencesImpl import com.intellij.openapi.editor.impl.FontFamilyService import com.intellij.openapi.util.Disposer +import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.ApiStatus +import org.jetbrains.plugins.terminal.block.ui.updateFrontendSettingsAndSync import java.util.* import java.util.concurrent.CopyOnWriteArrayList import kotlin.math.pow @@ -26,7 +28,7 @@ import kotlin.math.roundToInt storages = [Storage("terminal-font.xml")], ) @ApiStatus.Internal -class TerminalFontSettingsService : AppFontOptions() { +class TerminalFontSettingsService(private val coroutineScope: CoroutineScope) : AppFontOptions() { companion object { @JvmStatic fun getInstance(): TerminalFontSettingsService = service() @@ -121,15 +123,17 @@ class TerminalFontSettingsService : AppFontOptions() */ fun resetNonMonospacedFontsOnce(storedState: TerminalFontSettingsState): Boolean { return RunOnceUtil.runOnceForApp("TerminalFontSettingsService.fixStoredNonMonospacedFonts") { - val adjustedState = if (!FontFamilyService.isMonospaced(storedState.FONT_FAMILY)) { - val newState = TerminalFontSettingsState(getConsoleFontPreferences()) - newState.FONT_SIZE_2D = storedState.FONT_SIZE_2D - newState.LINE_SPACING = storedState.LINE_SPACING - newState - } - else storedState + updateFrontendSettingsAndSync(coroutineScope) { + val adjustedState = if (!FontFamilyService.isMonospaced(storedState.FONT_FAMILY)) { + val newState = TerminalFontSettingsState(getConsoleFontPreferences()) + newState.FONT_SIZE_2D = storedState.FONT_SIZE_2D + newState.LINE_SPACING = storedState.LINE_SPACING + newState + } + else storedState - super.loadState(adjustedState) + super.loadState(adjustedState) + } } } diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalOptionsProvider.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalOptionsProvider.kt index 07b2352eec96..87f4161cb5a6 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalOptionsProvider.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalOptionsProvider.kt @@ -1,22 +1,19 @@ // 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 org.jetbrains.plugins.terminal -import com.intellij.configurationStore.saveSettingsForRemoteDevelopment import com.intellij.ide.util.RunOnceUtil import com.intellij.idea.AppMode import com.intellij.openapi.Disposable import com.intellij.openapi.components.* import com.intellij.openapi.diagnostic.logger -import com.intellij.openapi.progress.withCurrentThreadCoroutineScope import com.intellij.openapi.util.registry.Registry import com.intellij.terminal.TerminalUiSettingsManager import com.intellij.terminal.TerminalUiSettingsManager.CursorShape import com.intellij.util.PlatformUtils -import com.intellij.util.application import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls +import org.jetbrains.plugins.terminal.block.ui.updateFrontendSettingsAndSync import org.jetbrains.plugins.terminal.settings.TerminalLocalOptions import java.util.concurrent.CopyOnWriteArrayList @@ -235,25 +232,12 @@ class TerminalOptionsProvider(private val coroutineScope: CoroutineScope) : Pers private fun performSettingsInitializationOnce() { RunOnceUtil.runOnceForApp("TerminalOptionsProvider.migration.2025.1.1") { - // If the settings update is happened in IDE backend, let's skip it. - // Because we should receive the values from the frontend and use it. - // Otherwise, there can be a race when updating is performed both on backend and frontend simultaneously. - if (AppMode.isRemoteDevHost()) return@runOnceForApp - - try { + updateFrontendSettingsAndSync(coroutineScope) { migrateCursorShape() // Disable the terminal engine migration. // Now it is Reworked by default, not depending on the previously set settings. //initializeTerminalEngine() } - finally { - // Trigger sending the updated values to the backend - coroutineScope.launch { - withCurrentThreadCoroutineScope { - saveSettingsForRemoteDevelopment(application) - } - } - } } } diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/ui/TerminalUiUtils.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/ui/TerminalUiUtils.kt index faf9434ae4f6..296c7466224a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/ui/TerminalUiUtils.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/ui/TerminalUiUtils.kt @@ -1,11 +1,13 @@ // Copyright 2000-2024 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.ui +import com.intellij.configurationStore.saveSettingsForRemoteDevelopment import com.intellij.execution.filters.HyperlinkInfo import com.intellij.execution.filters.HyperlinkWithPopupMenuInfo import com.intellij.execution.impl.EditorHyperlinkSupport import com.intellij.ide.ui.AntialiasingType import com.intellij.ide.ui.UISettings +import com.intellij.idea.AppMode import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.* import com.intellij.openapi.application.ApplicationManager @@ -34,6 +36,7 @@ import com.intellij.openapi.editor.markup.TextAttributes import com.intellij.openapi.fileEditor.impl.zoomIndicator.ZoomIndicatorManager import com.intellij.openapi.ide.CopyPasteManager import com.intellij.openapi.options.advanced.AdvancedSettings +import com.intellij.openapi.progress.withCurrentThreadCoroutineScope import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key @@ -44,6 +47,7 @@ import com.intellij.ui.components.JBLayeredPane import com.intellij.ui.scale.JBUIScale import com.intellij.util.Alarm import com.intellij.util.DocumentUtil +import com.intellij.util.application import com.intellij.util.asSafely import com.intellij.util.concurrency.ThreadingAssertions import com.intellij.util.concurrency.annotations.RequiresBlockingContext @@ -58,6 +62,8 @@ import com.jediterm.terminal.model.TerminalLine import com.jediterm.terminal.model.TerminalTextBuffer import com.jediterm.terminal.ui.AwtTransformers import com.jediterm.terminal.util.CharUtils +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.intellij.lang.annotations.MagicConstant import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.output.TextAttributesProvider @@ -579,4 +585,27 @@ fun sanitizeLineSeparators(text: String): String { } // Now convert this into what the terminal typically expects. return t.replace("\n", "\r") +} + +/** + * Should be used when you need to change the frontend terminal settings that are synced with the backend. + * See [org.jetbrains.plugins.terminal.TerminalRemoteSettingsInfoProvider]. + * It prohibits simultaneous update of the setting on both frontend and backend by executing it only on the frontend. + * And then launches sending the updates to the backend in the provided [coroutineScope]. + */ +internal fun updateFrontendSettingsAndSync(coroutineScope: CoroutineScope, doUpdate: () -> Unit) { + // Update the settings only on the IDE Frontend (or monolith). + if (AppMode.isRemoteDevHost()) return + + try { + doUpdate() + } + finally { + // Trigger sending the updated values to the backend + coroutineScope.launch { + withCurrentThreadCoroutineScope { + saveSettingsForRemoteDevelopment(application) + } + } + } } \ No newline at end of file