From e641f7bffadd7d492e9d6d54e0083134ba55cbfc Mon Sep 17 00:00:00 2001 From: Aleksandra Olemskaia Date: Tue, 30 Jun 2026 18:27:53 +0200 Subject: [PATCH] [settings] IJPL-248896 introduce NoAutomaticReset interface for Configurables A safety escape hatch for plugin developers whose Configurables trigger external actions (e.g. browser OAuth, native file picker) without a modal dialog. Configurables implementing NoAutomaticReset are skipped by SettingsEditor.resetUnmodifiedOnWindowFocus() on Settings focus regain. (cherry picked from commit 79beee8a0e0da064fdf696a1d05850c117f7065a) IJ-CR-217470 GitOrigin-RevId: 83a745f8f651d4513129e74e71c87a0e66739133 --- platform/ide-core/api-dump.txt | 1 + .../intellij/openapi/options/NoAutomaticReset.kt | 16 ++++++++++++++++ .../options/newEditor/SettingsEditor.java | 13 +++++++++---- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 platform/ide-core/src/com/intellij/openapi/options/NoAutomaticReset.kt diff --git a/platform/ide-core/api-dump.txt b/platform/ide-core/api-dump.txt index 52b707118dd6..82ba62836bbc 100644 --- a/platform/ide-core/api-dump.txt +++ b/platform/ide-core/api-dump.txt @@ -844,6 +844,7 @@ c:com.intellij.openapi.options.ConfigurationException - setQuickFix(com.intellij.openapi.options.ConfigurationQuickFix):V - setQuickFix(java.lang.Runnable):V - withHtmlMessage():com.intellij.openapi.options.ConfigurationException +com.intellij.openapi.options.NoAutomaticReset f:com.intellij.openapi.options.OptionsBundle - com.intellij.DynamicBundle com.intellij.openapi.options.SearchableConfigurable diff --git a/platform/ide-core/src/com/intellij/openapi/options/NoAutomaticReset.kt b/platform/ide-core/src/com/intellij/openapi/options/NoAutomaticReset.kt new file mode 100644 index 000000000000..cd085d193910 --- /dev/null +++ b/platform/ide-core/src/com/intellij/openapi/options/NoAutomaticReset.kt @@ -0,0 +1,16 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.openapi.options + +import org.jetbrains.annotations.ApiStatus + +/** + * A marker interface for [Configurable]s that require an explicit user action outside the IDE + * (such as browser-based authorization or a native file picker) before their state can be reset. + * + * When a settings window regains focus, the Settings editor normally resets any configurable that + * was unmodified when focus was lost but now reports `isModified() == true` (e.g. due to a + * background/external change). Implementing this interface opts the configurable out of that + * automatic reset, ensuring that ongoing external interactions are not interrupted. + */ +@ApiStatus.OverrideOnly +interface NoAutomaticReset diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsEditor.java index 55f91ff22b49..67a8b4e9b1e8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsEditor.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.UiDataProvider; import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.BackedByPersistentState; +import com.intellij.openapi.options.NoAutomaticReset; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.ConfigurationException; @@ -72,7 +73,6 @@ import javax.swing.JPanel; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Component; -import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -704,9 +704,14 @@ public final class SettingsEditor extends AbstractEditor implements UiDataProvid if (isModified == null) return; LOG.debug("resetUnmodifiedOnWindowFocus: current=" + current.getDisplayName() + ", leaveState=" + leaveState + ", isModified=" + isModified); if (leaveState == Boolean.FALSE && isModified) { - LOG.warn("resetUnmodifiedOnWindowFocus: resetting " + current.getDisplayName()); - current.reset(); - filter.context.fireReset(current); + if (ConfigurableWrapper.cast(NoAutomaticReset.class, current) != null) { + LOG.debug("resetUnmodifiedOnWindowFocus: skipping reset for " + current.getDisplayName() + " (NoAutomaticReset)"); + } + else { + LOG.warn("resetUnmodifiedOnWindowFocus: resetting " + current.getDisplayName()); + current.reset(); + filter.context.fireReset(current); + } } detectExternalChangesOnFocusGain(); }