(
ApplicationBundle.message("tab.editor.settings.appearance"),
"reference.settingsdialog.IDE.editor.appearance",
"editor.preferences.appearance"
diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurableEP.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurableEP.java
index d21b4db77425..0a9a5c9f17ec 100644
--- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurableEP.java
+++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurableEP.java
@@ -3,6 +3,7 @@ package com.intellij.application.options.editor;
import com.intellij.openapi.options.ConfigurableEP;
import com.intellij.openapi.options.UnnamedConfigurable;
+import org.jetbrains.annotations.ApiStatus;
/**
* To provide additional options in Editor | Appearance section register implementation of
@@ -14,5 +15,6 @@ import com.intellij.openapi.options.UnnamedConfigurable;
*
* A new instance of the specified class will be created each time then the Settings dialog is opened
*/
-final class EditorAppearanceConfigurableEP extends ConfigurableEP {
+@ApiStatus.Internal
+public final class EditorAppearanceConfigurableEP extends ConfigurableEP {
}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZeroWidthCodeVisionEntry.kt b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZeroWidthCodeVisionEntry.kt
new file mode 100644
index 000000000000..4b02c0eb7c39
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZeroWidthCodeVisionEntry.kt
@@ -0,0 +1,21 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.codeInsight.codeVision.ui.model
+
+import com.intellij.codeInsight.codeVision.CodeVisionEntry
+import com.intellij.lang.LangBundle
+import org.jetbrains.annotations.ApiStatus
+
+@ApiStatus.Internal
+class ZeroWidthCodeVisionEntry(
+ providerId: String
+) : CodeVisionEntry(
+ providerId,
+ icon = null,
+ longPresentation = LangBundle.message("show.a.code.vision.inlay.inlay.when.no.results.are.available"),
+ tooltip = "",
+ extraActions = emptyList()
+) {
+ init {
+ showInMorePopup = false
+ }
+}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZombieCodeVisionEntry.kt b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZombieCodeVisionEntry.kt
index 5f1889b2b847..594d97ab26b1 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZombieCodeVisionEntry.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZombieCodeVisionEntry.kt
@@ -3,6 +3,7 @@ package com.intellij.codeInsight.codeVision.ui.model
import com.intellij.codeInsight.codeVision.CodeVisionEntry
import com.intellij.openapi.util.NlsContexts
+import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import javax.swing.Icon
@@ -13,6 +14,7 @@ class ZombieCodeVisionEntry(
icon: Icon?,
val count: Int?,
val text: String = longPresentation,
+ val shouldBeDelimited: Boolean = true
) : CodeVisionEntry(providerId, icon, longPresentation, tooltip, emptyList()) {
override fun toString() = "ZombieCodeVisionEntry('$text')"
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/CodeVisionListPainter.kt b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/CodeVisionListPainter.kt
index df138acc4710..3303ee5bd050 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/CodeVisionListPainter.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/CodeVisionListPainter.kt
@@ -86,7 +86,7 @@ open class CodeVisionListPainter(
painter.paint(editor, textAttributes, g, it, Point(x, y), state, it == hoveredEntry, hoveredEntry)
x += size.width
- if (painter.shouldBeDelimited() && (index < value.visibleLens.size - 1 || hovered)) {
+ if (painter.shouldBeDelimited(it) && (index < value.visibleLens.size - 1 || hovered)) {
delimiterPainter.paint(editor, textAttributes, g, Point(x, y), state, false)
x += delimiterWidth
}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/ICodeVisionEntryBasePainter.kt b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/ICodeVisionEntryBasePainter.kt
index c807156c4d8f..4953d516a1f4 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/ICodeVisionEntryBasePainter.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/painters/ICodeVisionEntryBasePainter.kt
@@ -26,5 +26,5 @@ interface ICodeVisionEntryBasePainter : ICodeVisionPainter {
): Dimension
@ApiStatus.Internal
- fun shouldBeDelimited(): Boolean = true
+ fun shouldBeDelimited(entry: T): Boolean = true
}
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/providers/CodeVisionPainterProviders.kt b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/providers/CodeVisionPainterProviders.kt
index 7789785a58c6..09dd299d048a 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/providers/CodeVisionPainterProviders.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/renderers/providers/CodeVisionPainterProviders.kt
@@ -4,15 +4,22 @@ package com.intellij.codeInsight.codeVision.ui.renderers.providers
import com.intellij.codeInsight.codeVision.CodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.model.AdditionalCodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.model.CounterCodeVisionEntry
+import com.intellij.codeInsight.codeVision.ui.model.RangeCodeVisionModel
import com.intellij.codeInsight.codeVision.ui.model.RichTextCodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.model.TextCodeVisionEntry
+import com.intellij.codeInsight.codeVision.ui.model.ZeroWidthCodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.renderers.painters.CodeVisionRichTextPainter
import com.intellij.codeInsight.codeVision.ui.renderers.painters.CodeVisionVisionTextPainter
import com.intellij.codeInsight.codeVision.ui.renderers.painters.DefaultCodeVisionPainter
import com.intellij.codeInsight.codeVision.ui.renderers.painters.ICodeVisionEntryBasePainter
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.util.ClassExtension
import com.intellij.openapi.util.registry.Registry
+import java.awt.Dimension
+import java.awt.Graphics
+import java.awt.Point
private val INSTANCE = CodeVisionPainterProviders()
@@ -50,7 +57,35 @@ private class ZombieCodeVisionEntryPainter : DefaultCodeVisionPainter {
+
+ override fun paint(
+ editor: Editor,
+ textAttributes: TextAttributes,
+ g: Graphics,
+ value: ZeroWidthCodeVisionEntry,
+ point: Point,
+ state: RangeCodeVisionModel.InlayState,
+ hovered: Boolean,
+ hoveredEntry: CodeVisionEntry?
+ ) {
+ /* no-op */
+ }
+
+ override fun size(editor: Editor, state: RangeCodeVisionModel.InlayState, value: ZeroWidthCodeVisionEntry): Dimension {
+ return Dimension(0, 0)
+ }
+
+ override fun shouldBeDelimited(entry: ZeroWidthCodeVisionEntry): Boolean {
+ return false
+ }
+}
internal fun CodeVisionEntry.painter(): ICodeVisionEntryBasePainter {
return INSTANCE.getPainter(this@painter)
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.kt b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.kt
index 0be7b85c4051..a9317132af4d 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.kt
@@ -53,6 +53,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import java.awt.Container
+import java.util.Collections
import java.util.concurrent.CancellationException
open class TrafficLightRenderer private constructor(
@@ -652,9 +653,12 @@ open class TrafficLightRenderer private constructor(
): TrafficLightRendererInfo {
val viewProvider = psiFile.getViewProvider()
val languages = viewProvider.getLanguages()
- val settingMap = HashMap(languages.size)
+ val settingMap = LinkedHashMap(languages.size)
val settings = HighlightingSettingsPerFile.getInstance(project)
- for (psiRoot in viewProvider.getAllFiles()) {
+ val roots = viewProvider.getAllFiles().toMutableList()
+ // leave the first item (the base language) in place, show all others in a stable order
+ roots.subList(1, roots.size).sortBy { file -> file.language.displayName }
+ for (psiRoot in roots) {
val setting = settings.getHighlightingSettingForRoot(psiRoot)
settingMap[psiRoot.getLanguage()] = setting
}
@@ -662,7 +666,7 @@ open class TrafficLightRenderer private constructor(
val virtualFile = psiFile.getVirtualFile()!!
val inLib = fileIndex.isInLibrary(virtualFile) && !fileIndex.isInContent(virtualFile)
val shouldHighlight = ProblemHighlightFilter.shouldHighlightFile(psiFile)
- return TrafficLightRendererInfo(java.util.Map.copyOf(settingMap), inLib, shouldHighlight)
+ return TrafficLightRendererInfo(Collections.unmodifiableMap(settingMap), inLib, shouldHighlight)
}
private fun toPercent(progress: Double, finished: Boolean): Int {
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/grave/CodeVisionZombie.kt b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/grave/CodeVisionZombie.kt
index dc6267b33717..cf4daa1f8a56 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/grave/CodeVisionZombie.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/grave/CodeVisionZombie.kt
@@ -4,6 +4,7 @@ package com.intellij.codeInsight.daemon.impl.grave
import com.intellij.codeInsight.codeVision.CodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.model.CounterCodeVisionEntry
import com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry
+import com.intellij.codeInsight.codeVision.ui.renderers.providers.painter
import com.intellij.codeInsight.daemon.impl.readGutterIcon
import com.intellij.codeInsight.daemon.impl.writeGutterIcon
import com.intellij.openapi.editor.impl.zombie.LimbedNecromancy
@@ -35,6 +36,7 @@ internal object CodeVisionNecromancy : LimbedNecromancy): CodeVisionZombie {
@@ -74,6 +77,7 @@ internal data class CodeVisionLimb(
val tooltip: String,
val icon: Icon?,
val count: Int?,
+ val shouldBeDelimited: Boolean
) {
constructor(pair: Pair) : this(
pair.first.startOffset,
@@ -83,9 +87,10 @@ internal data class CodeVisionLimb(
pair.second.tooltip,
pair.second.icon,
(pair.second as? CounterCodeVisionEntry)?.count,
+ pair.second.painter().shouldBeDelimited(pair.second)
)
fun asEntry(): ZombieCodeVisionEntry {
- return ZombieCodeVisionEntry(providerId, longPresentation, tooltip, icon, count)
+ return ZombieCodeVisionEntry(providerId, longPresentation, tooltip, icon, count, shouldBeDelimited = shouldBeDelimited)
}
}
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java b/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java
index ffb080dd1eb5..bffdd6799e80 100644
--- a/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java
+++ b/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java
@@ -1,4 +1,4 @@
-// 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.
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.filters;
import com.intellij.execution.impl.ConsoleViewImpl;
@@ -45,7 +45,7 @@ public class TextConsoleBuilderImpl extends TextConsoleBuilder {
protected @NotNull ConsoleView createConsole() {
var result = new ConsoleViewImpl(myProject, myScope, myViewer, myUsePredefinedMessageFilter);
- result.setUseOwnModalityForUpdates$intellij_platform_lang_impl(useOwnModalityStateForUpdates);
+ result.setUseOwnModalityForUpdates(useOwnModalityStateForUpdates);
return result;
}
diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.kt b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.kt
index d7f22cddbe77..6810ece05c8a 100644
--- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.kt
+++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.kt
@@ -85,6 +85,7 @@ import com.intellij.util.containers.ContainerUtil
import com.intellij.util.text.CharArrayUtil
import com.intellij.util.ui.EDT
import com.intellij.util.ui.UIUtil
+import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.VisibleForTesting
@@ -576,7 +577,8 @@ open class ConsoleViewImpl protected constructor(
}
}
- internal var useOwnModalityForUpdates: Boolean = false
+ @ApiStatus.Internal
+ var useOwnModalityForUpdates: Boolean = false
protected open val stateForUpdate: ModalityState?
get() = if (useOwnModalityForUpdates) ModalityState.stateForComponent(this) else null
diff --git a/platform/lang-impl/src/com/intellij/ui/StringComboboxEditor.java b/platform/lang-impl/src/com/intellij/ui/StringComboboxEditor.java
index 0309131bb444..b33f15baf7f5 100644
--- a/platform/lang-impl/src/com/intellij/ui/StringComboboxEditor.java
+++ b/platform/lang-impl/src/com/intellij/ui/StringComboboxEditor.java
@@ -49,7 +49,7 @@ public class StringComboboxEditor extends EditorComboBoxEditor {
}
@Override
- protected void onEditorCreate(final EditorEx editor) {
+ protected void onEditorCreate(final @NotNull EditorEx editor) {
Disposer.register(((EditorImpl)editor).getDisposable(), new Disposable() {
@Override
public void dispose() {
diff --git a/platform/platform-impl/src/com/intellij/accessibility/AccessibilityToolsStateCollector.kt b/platform/platform-impl/src/com/intellij/accessibility/AccessibilityToolsStateCollector.kt
new file mode 100644
index 000000000000..b42e40af367f
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/accessibility/AccessibilityToolsStateCollector.kt
@@ -0,0 +1,120 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.accessibility
+
+import com.intellij.execution.process.OSProcessUtil
+import com.intellij.internal.statistic.beans.MetricEvent
+import com.intellij.internal.statistic.eventLog.EventLogGroup
+import com.intellij.internal.statistic.eventLog.events.EventFields
+import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
+import com.intellij.openapi.util.SystemInfoRt
+import com.intellij.ui.mac.foundation.Foundation
+import com.intellij.ui.mac.foundation.Foundation.NSAutoreleasePool
+import com.intellij.ui.mac.foundation.ID
+import java.awt.Toolkit
+
+internal class AccessibilityToolsStateCollector : ApplicationUsagesCollector() {
+ private enum class ScreenReader {
+ NVDA, JAWS, VoiceOver, Orca
+ }
+
+ private enum class ScreenMagnifier {
+ WindowsMagnifier, MacOSZoom
+ }
+
+ private enum class VoiceControl {
+ WindowsVoiceAccess, MacOSVoiceControl
+ }
+
+ private val GROUP = EventLogGroup("accessibility.tools.state", 1, "FUS",
+ "Statistics about the usage of third-party accessibility tools and settings")
+
+ private val SCREEN_READER = GROUP.registerEvent(
+ "screen.reader",
+ EventFields.Enum("name", "Name of the screen reader tool"),
+ "Screen reader is running in the system")
+ private val SCREEN_MAGNIFIER = GROUP.registerEvent(
+ "screen.magnifier",
+ EventFields.Enum("name", "Name of the screen magnifier tool"),
+ "Screen magnifier is running in the system")
+ private val VOICE_CONTROL = GROUP.registerEvent(
+ "voice.control",
+ EventFields.Enum("name", "Name of the voice control tool"),
+ "Voice control is running in the system")
+ private val OS_HIGH_CONTRAST = GROUP.registerEvent(
+ "os.high.contrast",
+ EventFields.Enabled,
+ "High Contrast mode is enabled in the OS settings")
+
+ override fun getGroup(): EventLogGroup = GROUP
+
+ override fun getMetrics(): Set {
+ val set = mutableSetOf()
+
+ when {
+ SystemInfoRt.isWindows -> {
+ val processList = OSProcessUtil.getProcessList()
+ for (process in processList) {
+ when (process.executableName.lowercase()) {
+ "nvda.exe" -> {
+ set.add(SCREEN_READER.metric(ScreenReader.NVDA))
+ }
+ "jfw.exe" -> {
+ set.add(SCREEN_READER.metric(ScreenReader.JAWS))
+ }
+ "magnify.exe" -> {
+ set.add(SCREEN_MAGNIFIER.metric(ScreenMagnifier.WindowsMagnifier))
+ }
+ "voiceaccess.exe" -> {
+ set.add(VOICE_CONTROL.metric(VoiceControl.WindowsVoiceAccess))
+ }
+ }
+ }
+
+ if (Toolkit.getDefaultToolkit().getDesktopProperty("win.highContrast.on") == true) {
+ set.add(OS_HIGH_CONTRAST.metric(true))
+ }
+ }
+ SystemInfoRt.isMac -> {
+ val pool = NSAutoreleasePool()
+ var universalAccess: ID? = null
+ var accessibility: ID? = null
+ try {
+ universalAccess = Foundation.invoke(
+ Foundation.invoke("NSUserDefaults", "alloc"),
+ "initWithSuiteName:",
+ Foundation.nsString("com.apple.universalaccess")
+ )
+ if (Foundation.invoke(universalAccess, "boolForKey:", Foundation.nsString("voiceOverOnOffKey")).booleanValue()) {
+ set.add(SCREEN_READER.metric(ScreenReader.VoiceOver))
+ }
+ if (Foundation.invoke(universalAccess, "boolForKey:", Foundation.nsString("closeViewZoomedIn")).booleanValue()) {
+ set.add(SCREEN_MAGNIFIER.metric(ScreenMagnifier.MacOSZoom))
+ }
+ if (Foundation.invoke(universalAccess, "boolForKey:", Foundation.nsString("increaseContrast")).booleanValue()) {
+ set.add(OS_HIGH_CONTRAST.metric(true))
+ }
+
+ accessibility = Foundation.invoke(
+ Foundation.invoke("NSUserDefaults", "alloc"),
+ "initWithSuiteName:",
+ Foundation.nsString("com.apple.Accessibility")
+ )
+ if (Foundation.invoke(accessibility, "boolForKey:", Foundation.nsString("CommandAndControlEnabled")).booleanValue()) {
+ set.add(VOICE_CONTROL.metric(VoiceControl.MacOSVoiceControl))
+ }
+ }
+ finally {
+ if (universalAccess != null) {
+ Foundation.cfRelease(universalAccess)
+ }
+ if (accessibility != null) {
+ Foundation.cfRelease(accessibility)
+ }
+ pool.drain()
+ }
+ }
+ }
+
+ return set
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java
index 9d524a82c3b6..945ceeb1d4e0 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java
@@ -259,6 +259,12 @@ public final class PluginManagerConfigurable
}
}
+ public void scheduleApply() {
+ if (myPanel != null) {
+ myPanel.scheduleApply();
+ }
+ }
+
@Override
public void reset() {
if (myPanel != null) {
diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurablePanel.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurablePanel.java
index 89572a21d2cc..db268e82c384 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurablePanel.java
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurablePanel.java
@@ -47,6 +47,8 @@ import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.wm.impl.welcomeScreen.PluginsTabFactory;
+import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenEventCollector;
import com.intellij.ui.GotItTooltip;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBScrollPane;
@@ -1979,6 +1981,19 @@ public final class PluginManagerConfigurablePanel implements Disposable {
return myPluginModelFacade.getModel().isModified();
}
+ public void scheduleApply() {
+ ApplicationManager.getApplication().invokeLater(() -> {
+ try {
+ apply();
+ WelcomeScreenEventCollector.logPluginsModified();
+ InstalledPluginsState.getInstance().runShutdownCallback();
+ }
+ catch (ConfigurationException exception) {
+ Logger.getInstance(PluginsTabFactory.class).error(exception);
+ }
+ }, ModalityState.nonModal());
+ }
+
public void apply() throws ConfigurationException {
if (myPluginsAutoUpdateEnabled != null) {
UpdateOptions state = UpdateSettings.getInstance().getState();
diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/MarketplaceRequests.kt b/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/MarketplaceRequests.kt
index 73dc79bdc6aa..4de0b5d14cb0 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/MarketplaceRequests.kt
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/MarketplaceRequests.kt
@@ -745,14 +745,23 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
fun getCompatibleUpdateByModule(module: String): PluginId? {
try {
- val data = objectMapper.writeValueAsString(CompatibleUpdateForModuleRequest(module))
+ val url = URI(MarketplaceUrls.getSearchPluginsUpdatesUrl())
- @Suppress("DEPRECATION")
- return HttpRequests.post(MarketplaceUrls.getSearchCompatibleUpdatesUrl(), HttpRequests.JSON_CONTENT_TYPE)
+ val query = buildString {
+ append("build=${ApplicationInfoImpl.orFromPluginCompatibleBuild(null)}")
+ append("&os=${buildEncodedOsParameter()}")
+ append("&arch=${buildEncodedArchParameter()}")
+ append("&module=${URLEncoder.encode(module, StandardCharsets.UTF_8)}")
+ }
+
+ val urlString = url.withQuery(query).toString()
+
+ return HttpRequests.request(urlString)
+ .accept(HttpRequests.JSON_CONTENT_TYPE)
+ .setHeadersViaTuner()
.productNameAsUserAgent()
.throwStatusCodeException(false)
.connect {
- it.write(data)
objectMapper.readValue(it.inputStream, object : TypeReference>() {})
}.firstOrNull()
?.pluginId
@@ -966,23 +975,6 @@ private data class CompatibleUpdateRequest(
)
}
-private data class CompatibleUpdateForModuleRequest(
- val module: String,
- val build: String,
- val os: String = OS.CURRENT.name,
- val arch: String = CpuArch.CURRENT.name,
-) {
-
- @JvmOverloads
- constructor(
- module: String,
- buildNumber: BuildNumber? = null,
- ) : this(
- module,
- ApplicationInfoImpl.orFromPluginCompatibleBuild(buildNumber),
- )
-}
-
private fun Logger.infoOrDebug(
message: String,
throwable: Throwable,
diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/utils/MarketplaceUrls.kt b/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/utils/MarketplaceUrls.kt
index b571b21fb17e..98c1e0925b68 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/utils/MarketplaceUrls.kt
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/marketplace/utils/MarketplaceUrls.kt
@@ -56,9 +56,6 @@ object MarketplaceUrls {
"${getPluginManagerUrl()}/api/search/aggregation/$field"
).addParameters(mapOf("build" to IDE_BUILD_FOR_REQUEST))
- @Deprecated("Use getSearchPluginsUpdatesUrl() instead")
- fun getSearchCompatibleUpdatesUrl(): String = Urls.newFromEncoded("${getPluginManagerUrl()}/api/search/compatibleUpdates").toExternalForm()
-
fun getSearchPluginsUpdatesUrl(): String = Urls.newFromEncoded("${getPluginManagerUrl()}/api/search/updates/compatible").toExternalForm()
fun getSearchNearestUpdate(): String = Urls.newFromEncoded("${getPluginManagerUrl()}/api/search/updates/nearest").toExternalForm()
diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/ThemeListProviderImpl.kt b/platform/platform-impl/src/com/intellij/ide/ui/laf/ThemeListProviderImpl.kt
index 5d76bad5ac3f..37f6d08ba3c7 100644
--- a/platform/platform-impl/src/com/intellij/ide/ui/laf/ThemeListProviderImpl.kt
+++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/ThemeListProviderImpl.kt
@@ -5,7 +5,6 @@ import com.intellij.ide.IdeBundle
import com.intellij.ide.ui.TargetUIType
import com.intellij.ide.ui.ThemeListProvider
import com.intellij.openapi.editor.colors.Groups
-import com.intellij.openapi.util.registry.Registry
import com.intellij.ui.ExperimentalUI
private class ThemeListProviderImpl : ThemeListProvider {
@@ -41,13 +40,7 @@ private class ThemeListProviderImpl : ThemeListProvider {
else customThemes.add(info)
}
- if (Registry.`is`("llm.riderNext.enabled", false)) {
- uiThemeProviderListManager.getThemeListForTargetUI(TargetUIType.NEXT).forEach { info ->
- if (!info.isThemeFromPlugin) nextUiThemes.add(info)
- else customThemes.add(info)
- }
- }
- else if (ExperimentalUI.isNewUI()) {
+ if (ExperimentalUI.isNewUI()) {
uiThemeProviderListManager.getThemeListForTargetUI(TargetUIType.ISLANDS).forEach { info ->
if (!info.isThemeFromPlugin) islandsUiThemes.add(info)
else customThemes.add(info)
diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.kt b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.kt
index 715986d965f2..34033b1b40df 100644
--- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.kt
@@ -21,7 +21,6 @@ import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.IconLoader.getDarkIcon
import com.intellij.openapi.util.IconLoader.getDisabledIcon
import com.intellij.openapi.util.NlsContexts
-import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.wm.IdeFocusManager
@@ -42,6 +41,8 @@ import com.intellij.util.SingleAlarm
import com.intellij.util.concurrency.EdtScheduler
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.ui.JBUI
+import com.intellij.util.ui.StartupUiUtil
+import com.intellij.util.ui.moveToFitChildPopupX
import kotlinx.coroutines.Job
import java.awt.*
import java.awt.event.AWTEventListener
@@ -367,6 +368,26 @@ class ActionMenu constructor(
}
}
+ override fun getPopupMenuOrigin(): Point {
+ val result = super.getPopupMenuOrigin()
+ if (!StartupUiUtil.isWaylandToolkit() || parent !is JPopupMenu) return result
+ correctPopupMenuPositionForWayland(result)
+ return result
+ }
+
+ private fun correctPopupMenuPositionForWayland(result: Point) {
+ val popupMenu = popupMenu
+ var popupMenuSize = popupMenu.size
+ if (popupMenuSize.width <= 0) { // not shown yet
+ popupMenuSize = popupMenu.preferredSize
+ }
+
+ val popupMenuBounds = Rectangle(result, popupMenuSize)
+ moveToFitChildPopupX(popupMenuBounds, this)
+ result.x = popupMenuBounds.x
+ result.y = popupMenuBounds.y
+ }
+
fun clearItems() {
if (MacMenuSettings.isSystemMenu && isMainMenuPlace) {
for (menuComponent in getMenuComponents()) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsFeedback.kt b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsFeedback.kt
index 7a333de2d6f7..08ae96bd65df 100644
--- a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsFeedback.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsFeedback.kt
@@ -15,7 +15,6 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
-import com.intellij.openapi.util.registry.Registry
import com.intellij.ui.ExperimentalUI
import java.lang.ref.WeakReference
@@ -39,8 +38,7 @@ internal class IslandsFeedback : ProjectActivity {
if (!ApplicationManager.getApplication().isUnitTestMode &&
!ApplicationManager.getApplication().isHeadlessEnvironment &&
- !AppMode.isRemoteDevHost() &&
- !Registry.`is`("llm.riderNext.enabled", false) && ExperimentalUI.isNewUI()) {
+ !AppMode.isRemoteDevHost() && ExperimentalUI.isNewUI()) {
handleFeedback(project)
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt
index d54ad43bdc0f..381d6ab4d731 100644
--- a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt
@@ -23,7 +23,6 @@ import com.intellij.openapi.ui.OnePixelDivider
import com.intellij.openapi.ui.Splittable
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.SystemInfoRt
-import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.wm.*
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
import com.intellij.openapi.wm.impl.*
@@ -66,7 +65,7 @@ import javax.swing.SwingUtilities
import javax.swing.border.Border
internal class IslandsUICustomization : InternalUICustomization() {
- private val isIslandsAvailable = !Registry.`is`("llm.riderNext.enabled", false) && ExperimentalUI.isNewUI()
+ private val isIslandsAvailable = ExperimentalUI.isNewUI()
private var isManyIslandEnabledCache: Boolean? = null
diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.kt
index efd3e7ad4dd8..dc80291e4e09 100644
--- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.kt
@@ -1136,7 +1136,7 @@ fun CoroutineScope.runInitProjectActivities(project: Project) {
(project.serviceAsync() as StartupManagerImpl).initProject()
}
- launch(CoroutineName("projectOpened event executing") + Dispatchers.UiWithModelAccess) {
+ launch(CoroutineName("projectOpened event executing") + Dispatchers.EDT) {
@Suppress("DEPRECATION", "removal")
ApplicationManager.getApplication().messageBus.syncPublisher(ProjectManager.TOPIC).projectOpened(project)
}
@@ -1148,7 +1148,7 @@ fun CoroutineScope.runInitProjectActivities(project: Project) {
return
}
- launch(CoroutineName("projectOpened component executing") + Dispatchers.UiWithModelAccess) {
+ launch(CoroutineName("projectOpened component executing") + Dispatchers.EDT) {
for (component in projectComponents) {
runCatching {
val componentActivity = StartUpMeasurer.startActivity(component.javaClass.name, ActivityCategory.PROJECT_OPEN_HANDLER)
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.kt
index 8a7732e700ee..4fa870660ccd 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.kt
@@ -414,9 +414,14 @@ class InfoAndProgressPanel internal constructor(
): BalloonHandler {
@Suppress("HardCodedStringLiteral")
val htmlContent = htmlBody.replace("\n", "
")
+ val icon = icon ?: if (type == MessageType.INFO) {
+ null
+ } else {
+ type.defaultIcon
+ }
val balloon = JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder(htmlContent,
- null,
+ icon,
type.titleForeground,
type.popupBackground,
listener)
@@ -1252,6 +1257,7 @@ private class CounterLabel : JPanel() {
label = createLabel()
layout = BorderLayout()
add(label, BorderLayout.CENTER)
+ isOpaque = false
setNumber(0, isProgressVisible =false, isPopupShowing = false)
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/PluginsTabFactory.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/PluginsTabFactory.java
index 0db6298854aa..c0b5756e4eff 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/PluginsTabFactory.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/PluginsTabFactory.java
@@ -86,16 +86,7 @@ public final class PluginsTabFactory implements WelcomeTabFactory {
if (!configurable.isModified()) {
return;
}
- ApplicationManager.getApplication().invokeLater(() -> {
- try {
- configurable.apply();
- WelcomeScreenEventCollector.logPluginsModified();
- InstalledPluginsState.getInstance().runShutdownCallback();
- }
- catch (ConfigurationException exception) {
- Logger.getInstance(PluginsTabFactory.class).error(exception);
- }
- }, ModalityState.nonModal());
+ configurable.scheduleApply();
}
});
diff --git a/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java b/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java
index 558aeea4c264..7a1afd887854 100644
--- a/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java
+++ b/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java
@@ -34,7 +34,7 @@ public class EditorComboBoxEditor implements ComboBoxEditor{
myTextField.setName(NAME);
}
- protected void onEditorCreate(EditorEx editor) {}
+ protected void onEditorCreate(@NotNull EditorEx editor) {}
@Override
public void selectAll() {
diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java
index 0307d009aa0c..a1bf14a7c673 100644
--- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java
+++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java
@@ -7,28 +7,64 @@ import com.jetbrains.JBR;
import org.intellij.lang.annotations.JdkConstants;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
-import javax.swing.*;
import javax.swing.event.MouseInputListener;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
-import static java.awt.Cursor.*;
+import static com.intellij.ui.WindowMouseListenerSupportKt.createWindowMouseListenerSupport;
@ApiStatus.Internal
public abstract class WindowMouseListener extends MouseAdapter implements MouseInputListener {
- private static final boolean moveAfterMouseRelease = StartupUiUtil.isWaylandToolkit();
protected final Component myContent;
- @JdkConstants.CursorType int myCursorType;
- private Point myLocation;
- private Rectangle viewBounds;
- private int mouseButton;
- private boolean wasDragged;
- private boolean leftMouseButtonOnly = false;
+ protected final WindowMouseListenerSupport support = createWindowMouseListenerSupport(new SourceAdapter());
+
+ private class SourceAdapter implements WindowMouseListenerSource {
+ @Override
+ public @NotNull Component getContent(@NotNull MouseEvent event) {
+ return WindowMouseListener.this.getContent(event);
+ }
+
+ @Override
+ public @Nullable Component getView(@NotNull Component event) {
+ return WindowMouseListener.this.getView(event);
+ }
+
+ @Override
+ public boolean isDisabled(@NotNull Component view) {
+ return WindowMouseListener.this.isDisabled(view);
+ }
+
+ @Override
+ public int getCursorType(@Nullable Component view, @Nullable Point location) {
+ return WindowMouseListener.this.getCursorType(view, location);
+ }
+
+ @Override
+ public void setCursor(@NotNull Component content, @Nullable Cursor cursor) {
+ WindowMouseListener.this.setCursor(content, cursor);
+ }
+
+ @Override
+ public void updateBounds(@NotNull Rectangle bounds, @NotNull Component view, int dx, int dy) {
+ WindowMouseListener.this.updateBounds(bounds, view, dx, dy);
+ }
+
+ @Override
+ public void notifyMoved() {
+ WindowMouseListener.this.notifyMoved();
+ }
+
+ @Override
+ public void notifyResized() {
+ WindowMouseListener.this.notifyResized();
+ }
+ }
/**
- * @param content the window content to find a window, or {@code null} to use a component from a mouse event eeeqweq3r
+ * @param content the window content to find a window, or {@code null} to use a component from a mouse event
*/
WindowMouseListener(Component content) {
myContent = content;
@@ -42,6 +78,11 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
@JdkConstants.CursorType
abstract int getCursorType(Component view, Point location);
+ @JdkConstants.CursorType
+ int getCursorType() {
+ return support.getCursorType();
+ }
+
/**
* @param bounds the component bounds, which should be updated
* @param view the component to move/resize
@@ -51,32 +92,32 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
abstract void updateBounds(Rectangle bounds, Component view, int dx, int dy);
public void setLeftMouseButtonOnly(boolean leftMouseButtonOnly) {
- this.leftMouseButtonOnly = leftMouseButtonOnly;
+ support.setLeftMouseButtonOnly(leftMouseButtonOnly);
}
@Override
public void mouseMoved(MouseEvent event) {
- update(event, false);
+ support.update(event, false);
}
@Override
public void mousePressed(MouseEvent event) {
- update(event, true);
+ support.update(event, true);
}
@Override
public void mouseDragged(MouseEvent event) {
- process(event, true);
+ support.process(event, true);
}
@Override
public void mouseReleased(MouseEvent event) {
- process(event, false);
+ support.process(event, false);
}
@Override
public void mouseClicked(MouseEvent event) {
- process(event, false);
+ support.process(event, false);
}
/**
@@ -92,88 +133,6 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
return false;
}
- /**
- * Updates a cursor and starts moving/resizing if the {@code start} is specified.
- */
- private void update(MouseEvent event, boolean start) {
- if (event.isConsumed() || (start && leftMouseButtonOnly && !SwingUtilities.isLeftMouseButton(event))) {
- return;
- }
-
- if (start) wasDragged = false; // reset dragged state when mouse pressed
- if (myLocation == null) {
- Component content = getContent(event);
- Component view = getView(content);
- if (view != null) {
- myCursorType = isDisabled(view) ? CUSTOM_CURSOR : getCursorType(view, event.getLocationOnScreen());
- setCursor(content, getPredefinedCursor(myCursorType == CUSTOM_CURSOR ? DEFAULT_CURSOR : myCursorType));
- if (start && myCursorType != CUSTOM_CURSOR) {
- mouseButton = event.getButton();
- myLocation = event.getLocationOnScreen();
- viewBounds = view.getBounds();
- event.consume();
- }
- }
- }
- }
-
- /**
- * Processes moving/resizing and stops it if not {@code mouseMove}.
- */
- private void process(MouseEvent event, boolean mouseMove) {
- if (event.isConsumed()) return;
- if (mouseMove) wasDragged = true; // set dragged state when mouse dragged
- if (myLocation != null && viewBounds != null) {
- Component content = getContent(event);
- Component view = getView(content);
- if (mouseMove && myCursorType == DEFAULT_CURSOR && jbrMoveSupported(view)) {
- // Enter in move mode only after mouse move, so double click is supported
- JBR.getWindowMove().startMovingTogetherWithMouse((Window)view, mouseButton);
- myLocation = null;
- viewBounds = null;
- return;
- }
-
- if (view != null) {
- Rectangle bounds = new Rectangle(viewBounds);
- int dx = event.getXOnScreen() - myLocation.x;
- int dy = event.getYOnScreen() - myLocation.y;
- if (myCursorType == DEFAULT_CURSOR && view instanceof Frame) {
- int state = ((Frame)view).getExtendedState();
- if (isStateSet(Frame.MAXIMIZED_HORIZ, state)) dx = 0;
- if (isStateSet(Frame.MAXIMIZED_VERT, state)) dy = 0;
- }
- updateBounds(bounds, view, dx, dy);
- Rectangle viewBounds = view.getBounds();
- if (!bounds.equals(viewBounds)) {
- boolean moved = bounds.x != viewBounds.x || bounds.y != viewBounds.y;
- boolean resized = bounds.width != viewBounds.width || bounds.height != viewBounds.height;
- boolean reallyMoveWindow = !moveAfterMouseRelease || !mouseMove;
- if ((moved && reallyMoveWindow) || resized) {
- view.reshape(bounds.x, bounds.y, bounds.width, bounds.height);
- view.invalidate();
- view.validate();
- view.repaint();
- if (moved) notifyMoved();
- if (resized) notifyResized();
- }
- }
- }
- if (!mouseMove) {
- setCursor(content, getPredefinedCursor(DEFAULT_CURSOR));
- myLocation = null;
- if (wasDragged) {
- viewBounds = null; // no mouse clicked when mouse released after mouse dragged
- }
- }
- event.consume();
- }
- else if (!mouseMove && viewBounds != null) {
- viewBounds = null; // consume mouse clicked for consumed mouse released if no mouse dragged
- event.consume();
- }
- }
-
/**
* Returns a window content which is used to find corresponding window and to set a cursor.
* By default, it returns a component from the specified mouse event if the content is not specified.
@@ -206,7 +165,7 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
* Returns {@code true} if a window is now moving/resizing.
*/
public boolean isBusy() {
- return myLocation != null;
+ return support.isBusy();
}
protected void notifyMoved() { }
diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt
new file mode 100644
index 000000000000..56a24866b1f5
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt
@@ -0,0 +1,301 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.ui
+
+import com.intellij.util.ui.StartupUiUtil
+import com.jetbrains.JBR
+import org.intellij.lang.annotations.JdkConstants
+import org.jetbrains.annotations.ApiStatus
+import java.awt.*
+import java.awt.event.MouseEvent
+import javax.swing.SwingUtilities
+
+
+@ApiStatus.Internal
+internal interface WindowMouseListenerSource {
+ fun getContent(event: MouseEvent): Component
+
+ fun getView(event: Component): Component?
+
+ fun isDisabled(view: Component): Boolean
+
+ @JdkConstants.CursorType
+ fun getCursorType(view: Component?, location: Point?): Int
+
+ fun setCursor(content: Component, cursor: Cursor?)
+
+ fun updateBounds(bounds: Rectangle, view: Component, dx: Int, dy: Int)
+
+ fun notifyMoved()
+
+ fun notifyResized()
+}
+
+internal fun createWindowMouseListenerSupport(source: WindowMouseListenerSource): WindowMouseListenerSupport {
+ return if (StartupUiUtil.isWaylandToolkit()) {
+ WaylandWindowMouseListenerSupport(source)
+ }
+ else {
+ RegularWindowMouseListenerSupport(source)
+ }
+}
+
+internal sealed class WindowMouseListenerSupport(private val source: WindowMouseListenerSource) {
+ @get:JdkConstants.CursorType
+ var cursorType: Int = 0
+ private set
+ private var currentSession: WindowListenerSession? = null
+
+ var leftMouseButtonOnly = false
+
+ val isBusy: Boolean
+ get() = currentSession != null
+
+ protected abstract fun moveAfterMouseRelease(): Boolean
+
+ protected abstract fun jbrMoveSupported(component: Component?): Boolean
+
+ /**
+ * Updates a cursor and starts moving/resizing if the `start` is specified.
+ */
+ fun update(event: MouseEvent, start: Boolean) {
+ if (event.isConsumed || (start && leftMouseButtonOnly && !SwingUtilities.isLeftMouseButton(event))) {
+ return
+ }
+
+ if (!isBusy) {
+ val content = source.getContent(event)
+ val view = source.getView(content)
+ if (view != null) {
+ beforeUpdate(event, view)
+ updateCursor(event, content, view)
+ if (start && cursorType != Cursor.CUSTOM_CURSOR) {
+ start(event, view)
+ event.consume()
+ }
+ }
+ }
+ }
+
+ protected open fun beforeUpdate(event: MouseEvent, view: Component) { }
+
+ private fun updateCursor(event: MouseEvent, content: Component, view: Component) {
+ cursorType = if (source.isDisabled(view)) Cursor.CUSTOM_CURSOR else source.getCursorType(view, event.locationOnScreen)
+ source.setCursor(content, Cursor.getPredefinedCursor(if (cursorType == Cursor.CUSTOM_CURSOR) Cursor.DEFAULT_CURSOR else cursorType))
+ }
+
+ private fun start(event: MouseEvent, view: Component) {
+ currentSession = WindowListenerSession(
+ event.locationOnScreen,
+ view.bounds,
+ event.getButton(),
+ )
+ onStarted(event, view)
+ }
+
+ protected open fun onStarted(event: MouseEvent, view: Component) { }
+
+ /**
+ * Processes moving/resizing and stops it if not `mouseMove`.
+ */
+ fun process(event: MouseEvent, mouseMove: Boolean) {
+ // Some points:
+ // 1. Stop events should be handled even if they're already consumed, so the session finishes correctly.
+ // 2. Non-drag unconsumed events should be handled as drag events anyway to support the "move on mouse release" mode.
+ if (!event.isConsumed) {
+ handleDragEvent(event, mouseMove)
+ }
+ if (!mouseMove) {
+ handleStopEvent(event)
+ }
+ }
+
+ @JdkConstants.CursorType
+ open fun getResizeCursor(top: Int, left: Int, bottom: Int, right: Int, resizeArea: Insets): Int {
+ // Wayland doesn't allow to change window's location programmatically,
+ // so resizing from top/left shall be forbidden for now.
+ if (top < resizeArea.top) {
+ if (left < resizeArea.left * 2) return Cursor.NW_RESIZE_CURSOR
+ if (right < resizeArea.right * 2) return Cursor.NE_RESIZE_CURSOR
+ return Cursor.N_RESIZE_CURSOR
+ }
+ if (bottom < resizeArea.bottom) {
+ if (left < resizeArea.left * 2) return Cursor.SW_RESIZE_CURSOR
+ if (right < resizeArea.right * 2) return Cursor.SE_RESIZE_CURSOR
+ return Cursor.S_RESIZE_CURSOR
+ }
+ if (left < resizeArea.left) {
+ if (top < resizeArea.top * 2) return Cursor.NW_RESIZE_CURSOR
+ if (bottom < resizeArea.bottom * 2) return Cursor.SW_RESIZE_CURSOR
+ return Cursor.W_RESIZE_CURSOR
+ }
+ if (right < resizeArea.right) {
+ if (top < resizeArea.top * 2) return Cursor.NE_RESIZE_CURSOR
+ if (bottom < resizeArea.bottom * 2) return Cursor.SE_RESIZE_CURSOR
+ return Cursor.E_RESIZE_CURSOR
+ }
+ return Cursor.CUSTOM_CURSOR
+ }
+
+ private fun handleDragEvent(event: MouseEvent, mouseMove: Boolean) {
+ val session = currentSession ?: return
+ if (mouseMove) {
+ onDraggingStarted(session)
+ }
+
+ val content = source.getContent(event)
+ val view = source.getView(content)
+ // Enter in move mode only after mouse move, so double click is supported
+ if (mouseMove && cursorType == Cursor.DEFAULT_CURSOR && jbrMoveSupported(view)) {
+ enterJbrMoveMode(session, view)
+ return
+ }
+
+ if (view != null) {
+ val bounds = Rectangle(session.viewBounds)
+ val delta = computeOffsetFromInitialLocation(session, event)
+ if (cursorType == Cursor.DEFAULT_CURSOR && view is Frame) {
+ val state = view.extendedState
+ if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_HORIZ, state)) delta.x = 0
+ if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_VERT, state)) delta.y = 0
+ }
+ source.updateBounds(bounds, view, delta.x, delta.y)
+ val currentViewBounds = view.bounds
+ if (bounds != currentViewBounds) {
+ val moved = bounds.x != currentViewBounds.x || bounds.y != currentViewBounds.y
+ val resized = bounds.width != currentViewBounds.width || bounds.height != currentViewBounds.height
+ val reallyMoveWindow = !moveAfterMouseRelease() || !mouseMove
+ if ((moved && reallyMoveWindow) || resized) {
+ setViewBounds(view, bounds, moved, resized)
+ }
+ }
+ }
+ event.consume()
+ }
+
+ protected abstract fun onDraggingStarted(session: WindowListenerSession)
+
+ protected open fun computeOffsetFromInitialLocation(session: WindowListenerSession, event: MouseEvent): Point {
+ val location = session.location
+ val dx: Int = event.xOnScreen - location.x
+ val dy: Int = event.yOnScreen - location.y
+ return Point(dx, dy)
+ }
+
+ private fun setViewBounds(view: Component, bounds: Rectangle, moved: Boolean, resized: Boolean) {
+ view.reshape(bounds.x, bounds.y, bounds.width, bounds.height)
+ view.invalidate()
+ view.validate()
+ view.repaint()
+ if (moved) source.notifyMoved()
+ if (resized) source.notifyResized()
+ }
+
+ private fun handleStopEvent(event: MouseEvent) {
+ val session = currentSession ?: return
+
+ resetCursor(source.getContent(event))
+
+ if (session.expectMouseReleased && event.id == MouseEvent.MOUSE_RELEASED) {
+ session.expectMouseReleased = false
+ event.consume()
+ }
+ if (session.expectMouseClicked && event.id == MouseEvent.MOUSE_CLICKED) {
+ session.expectMouseClicked = false
+ event.consume()
+ }
+
+ if (!session.expectMouseReleased && !session.expectMouseClicked) {
+ stop()
+ }
+ }
+
+ private fun resetCursor(content: Component) {
+ source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR))
+ }
+
+ private fun stop() {
+ currentSession = null
+ }
+
+ private fun enterJbrMoveMode(session: WindowListenerSession, view: Component?) {
+ JBR.getWindowMove().startMovingTogetherWithMouse(view as Window, session.mouseButton)
+ stop()
+ }
+
+ protected data class WindowListenerSession(
+ val location: Point,
+ val viewBounds: Rectangle,
+ val mouseButton: Int,
+ var expectMouseReleased: Boolean = true,
+ var expectMouseClicked: Boolean = true,
+ )
+}
+
+private class RegularWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) {
+ override fun moveAfterMouseRelease(): Boolean = false
+
+ override fun onDraggingStarted(session: WindowListenerSession) {
+ session.expectMouseClicked = false // after dragging starts, there will be only one "released" event
+ }
+
+ override fun jbrMoveSupported(component: Component?): Boolean {
+ return (component is Frame || component is Dialog) && JBR.isWindowMoveSupported()
+ }
+}
+
+private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) {
+ private var isTruePopup = false
+ private var dx = 0
+ private var dy = 0
+
+ override fun beforeUpdate(event: MouseEvent, view: Component) {
+ isTruePopup = view is Window && view.type == Window.Type.POPUP
+ }
+
+ override fun onStarted(event: MouseEvent, view: Component) {
+ dx = 0
+ dy = 0
+ if (isRelativeMovementMode()) {
+ @Suppress("UsePropertyAccessSyntax")
+ JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset()
+ }
+ }
+
+ override fun onDraggingStarted(session: WindowListenerSession) { } // on Wayland, whether dragging has started or not, both "released" and "clicked" events will arrive
+
+ override fun getResizeCursor(top: Int, left: Int, bottom: Int, right: Int, resizeArea: Insets): Int {
+ if (isRelativeMovementMode()) return super.getResizeCursor(top, left, bottom, right, resizeArea)
+ // Wayland doesn't allow to change window's location programmatically,
+ // so resizing from top/left shall be forbidden for now.
+ if (bottom < resizeArea.bottom) {
+ if (right < resizeArea.right * 2) return Cursor.SE_RESIZE_CURSOR
+ return Cursor.S_RESIZE_CURSOR
+ }
+ if (right < resizeArea.right) {
+ if (bottom < resizeArea.bottom * 2) return Cursor.SE_RESIZE_CURSOR
+ return Cursor.E_RESIZE_CURSOR
+ }
+ return Cursor.CUSTOM_CURSOR
+ }
+
+ override fun computeOffsetFromInitialLocation(session: WindowListenerSession, event: MouseEvent): Point {
+ if (isRelativeMovementMode()) {
+ @Suppress("UsePropertyAccessSyntax")
+ val delta = JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset()
+ dx += delta.x
+ dy += delta.y
+ return Point(dx, dy)
+ }
+ else {
+ return super.computeOffsetFromInitialLocation(session, event)
+ }
+ }
+
+ private fun isRelativeMovementMode(): Boolean = isTruePopup && JBR.isRelativePointerMovementSupported()
+
+ override fun moveAfterMouseRelease(): Boolean = !isRelativeMovementMode()
+
+ override fun jbrMoveSupported(component: Component?): Boolean {
+ return component is Window && component.type != Window.Type.POPUP
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java b/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java
index ae4c6fd96bb4..21edf1d407ae 100644
--- a/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java
+++ b/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java
@@ -87,8 +87,8 @@ public class WindowResizeListener extends WindowMouseListener {
if (myCorner != null && right < myCorner.getIconWidth() && bottom < myCorner.getIconHeight()) {
return DEFAULT_CURSOR;
}
- Insets expected = getResizeBorder(view);
- if (expected != null) {
+ Insets resizeArea = getResizeBorder(view);
+ if (resizeArea != null) {
if (view instanceof Frame) {
int state = ((Frame)view).getExtendedState();
if (isStateSet(Frame.MAXIMIZED_HORIZ, state)) {
@@ -101,28 +101,7 @@ public class WindowResizeListener extends WindowMouseListener {
}
}
- // Wayland doesn't allow to change window's location programmatically,
- // so resizing from top/left shall be forbidden for now.
- if (!isWayland && top < expected.top) {
- if (left < expected.left * 2) return NW_RESIZE_CURSOR;
- if (right < expected.right * 2) return NE_RESIZE_CURSOR;
- return N_RESIZE_CURSOR;
- }
- if (bottom < expected.bottom) {
- if (!isWayland && left < expected.left * 2) return SW_RESIZE_CURSOR;
- if (right < expected.right * 2) return SE_RESIZE_CURSOR;
- return S_RESIZE_CURSOR;
- }
- if (!isWayland && left < expected.left) {
- if (top < expected.top * 2 ) return NW_RESIZE_CURSOR;
- if (bottom < expected.bottom * 2) return SW_RESIZE_CURSOR;
- return W_RESIZE_CURSOR;
- }
- if (right < expected.right) {
- if (!isWayland && top < expected.top * 2) return NE_RESIZE_CURSOR;
- if (bottom < expected.bottom * 2) return SE_RESIZE_CURSOR;
- return E_RESIZE_CURSOR;
- }
+ return support.getResizeCursor(top, left, bottom, right, resizeArea);
}
return CUSTOM_CURSOR;
}
@@ -130,6 +109,7 @@ public class WindowResizeListener extends WindowMouseListener {
@Override
void updateBounds(Rectangle bounds, Component view, int dx, int dy) {
Dimension minimum = view.getMinimumSize();
+ var myCursorType = getCursorType();
if (myCursorType == NE_RESIZE_CURSOR || myCursorType == E_RESIZE_CURSOR || myCursorType == SE_RESIZE_CURSOR || myCursorType == DEFAULT_CURSOR) {
bounds.width += fixMinSize(dx, bounds.width, minimum.width);
}
diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java
index a6b2b9cff182..f9273dff3834 100644
--- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java
+++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java
@@ -1401,7 +1401,10 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
WindowRoundedCornersManager.setRoundedCorners(window, roundedCornerParams);
}
- if (myNormalWindowLevel && !window.isDisplayable()) {
+ if (shouldUseTrueWaylandPopups()) {
+ window.setType(Window.Type.POPUP);
+ }
+ else if (myNormalWindowLevel && !window.isDisplayable()) {
window.setType(Window.Type.NORMAL);
}
myWindow = window;
@@ -1526,19 +1529,33 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
}
private static Point getLocationRelativeToParent(Rectangle bounds, Window popupParent) {
+ Rectangle newBounds = new Rectangle(bounds);
+ // The Wayland server may refuse to show a popup whose top-left corner is located outside the parent toplevel's bounds.
+ var toplevelParent = ComponentUtil.findUltimateParent(popupParent);
+ var toplevelParentLocation = toplevelParent.getLocationOnScreen();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "The top-level parent is located at " + toplevelParentLocation +
+ " and has the size " + toplevelParent.getSize()
+ );
+ }
+ Rectangle okBounds = new Rectangle(toplevelParentLocation.x, toplevelParentLocation.y,
+ toplevelParent.getWidth() + newBounds.width, toplevelParent.getHeight() + newBounds.height);
+ ScreenUtil.moveToFit(newBounds, okBounds, new Insets(0, 0, 1, 1));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("The bounds after fitting into the top-level parent: " + newBounds);
+ }
// The "bounds" are "screen" coordinates, which in Wayland means that they
// are relative to the nearest toplevel (Window) in the hierarchy.
// But popups in Wayland are expected to be located relative to popup's "parent" (a toplevel or another popup).
// We need to adjust "bounds" to be relative to the parent.
- Rectangle newBounds = new Rectangle(bounds);
Point parentLocation = popupParent.getLocationOnScreen();
newBounds.x -= parentLocation.x;
newBounds.y -= parentLocation.y;
- // The Wayland server may refuse to show a popup whose top-left corner is located outside the parent toplevel's bounds.
- // TODO: Need to fit into the nearest toplevel, not popupParent that may be another popup.
- Rectangle okBounds = new Rectangle(0, 0,
- popupParent.getWidth() + newBounds.width, popupParent.getHeight() + newBounds.height);
- ScreenUtil.moveToFit(newBounds, okBounds, new Insets(0, 0, 1, 1));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("The direct popup parent is located at " + parentLocation);
+ LOG.debug("The bounds after converting to the parent coordinate system: " + newBounds);
+ }
return newBounds.getLocation();
}
@@ -2833,6 +2850,7 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
}
private @Nullable Point getStoredLocation() {
+ if (shouldUseTrueWaylandPopups()) return null; // not supported at the moment
if (myDimensionServiceKey == null) return null;
return getWindowStateService(getProjectDependingOnKey(myDimensionServiceKey)).getLocation(myDimensionServiceKey);
}
@@ -2909,4 +2927,8 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
}
return event.isConsumed();
}
+
+ private static boolean shouldUseTrueWaylandPopups() {
+ return StartupUiUtil.isWaylandToolkit() && Registry.is("wayland.true.popups", false);
+ }
}
diff --git a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java
index 8787adcb8b5e..3a9fa95d823f 100644
--- a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java
+++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java
@@ -546,6 +546,13 @@ public class PopupFactoryImpl extends JBPopupFactory {
if (rootPane != null) {
return rootPane;
}
+
+ // https://youtrack.jetbrains.com/issue/DPA-3312 Unexpected component for dataContext: org.jetbrains.skiko.SkiaLayer$1
+ // The focus owner may be a non-JComponent, but it resides inside a wrapping JComponent parent
+ Container parent = component == null ? null : component.getParent();
+ if (parent instanceof JComponent c) {
+ return c;
+ }
else {
String componentClass = component == null ? null : component.getClass().getName();
throw new AssertionError("Unexpected component for " + errorInfoSupplier.get() + ": " + componentClass);
diff --git a/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java
index 5604d7fc941e..7f3d9319bc6d 100644
--- a/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java
+++ b/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java
@@ -33,7 +33,9 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.SlowOperations;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBInsets;
+import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.WaylandUtilKt;
import com.intellij.util.ui.accessibility.ScreenReader;
import kotlin.Unit;
import org.jetbrains.annotations.ApiStatus;
@@ -628,7 +630,15 @@ public class ListPopupImpl extends WizardPopup implements ListPopup, NextStepHan
if (!UiInterceptors.tryIntercept(myChild)) {
// Intercept child popup in tests because it is impossible to calculate location on screen there
- myChild.show(container, container.getLocationOnScreen().x + container.getWidth() - STEP_X_PADDING, y, true);
+ var childLocation = new Point(container.getLocationOnScreen().x + container.getWidth() - STEP_X_PADDING, y);
+ if (StartupUiUtil.isWaylandToolkit()) {
+ SwingUtilities.convertPointFromScreen(childLocation, container);
+ var childBounds = new Rectangle(childLocation, myChild.getPreferredContentSize());
+ WaylandUtilKt.moveToFitChildPopupX(childBounds, container);
+ childLocation = childBounds.getLocation();
+ SwingUtilities.convertPointToScreen(childLocation, container);
+ }
+ myChild.show(container, childLocation.x, childLocation.y, true);
}
setIndexForShowingChild(myList.getSelectedIndex());
myMouseMovementTracker.reset();
diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml
index 924ee96ee32a..7b00bfa3788e 100644
--- a/platform/platform-resources/src/META-INF/LangExtensions.xml
+++ b/platform/platform-resources/src/META-INF/LangExtensions.xml
@@ -793,6 +793,8 @@
implementationClass="com.intellij.codeInsight.codeVision.ui.renderers.providers.RichTextCodeVisionEntryPainter"/>
+
diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml
index 1a389f96a3aa..ed7e402d8699 100644
--- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml
+++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml
@@ -1027,6 +1027,7 @@
+
+
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/ProjectOpeningTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/ProjectOpeningTest.kt
index 67913f05312f..c3bbd00d66fb 100644
--- a/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/ProjectOpeningTest.kt
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/ProjectOpeningTest.kt
@@ -1,4 +1,4 @@
-// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.project.impl
import com.intellij.ide.impl.OpenProjectTask
@@ -6,6 +6,8 @@ import com.intellij.ide.impl.ProjectUtil
import com.intellij.ide.impl.ProjectUtilCore
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ProjectManagerListener
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.startup.InitProjectActivity
import com.intellij.testFramework.ExtensionTestUtil
@@ -16,6 +18,7 @@ import com.intellij.testFramework.fixtures.BareTestFixtureTestCase
import com.intellij.testFramework.rules.InMemoryFsRule
import com.intellij.testFramework.rules.TempDirectory
import com.intellij.testFramework.useProject
+import com.intellij.util.application
import com.intellij.util.io.createDirectories
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
@@ -123,4 +126,19 @@ class ProjectOpeningTest : BareTestFixtureTestCase() {
val projectFilePath = project!!.useProject { it.projectFilePath }
assertThat(projectFilePath).isEqualTo(projectFile.invariantSeparatorsPathString)
}
+
+ @Suppress("removal")
+ @Test
+ fun `project listener is called under write-intent lock`() {
+ val listenerRunsUnderWriteIntent = AtomicBoolean(false)
+ val projectDir = tempDir.root.toPath()
+ application.messageBus.connect(testRootDisposable).subscribe(ProjectManager.TOPIC, object : ProjectManagerListener {
+ override fun projectOpened(project: Project) {
+ listenerRunsUnderWriteIntent.set(application.isWriteIntentLockAcquired)
+ }
+ })
+ val project = ProjectUtil.openOrImport(projectDir, OpenProjectTask())
+ project?.useProject { } // closing this project
+ assertThat(listenerRunsUnderWriteIntent.get()).isTrue()
+ }
}
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/impl/PersistentFS_FindFilesTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/impl/PersistentFS_FindFilesTest.java
index 25a3b16fe22f..f8512d44c42e 100644
--- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/impl/PersistentFS_FindFilesTest.java
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/impl/PersistentFS_FindFilesTest.java
@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
import com.intellij.testFramework.junit5.TestApplication;
import com.intellij.testFramework.utils.vfs.CheckVFSHealthExtension;
+import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeAll;
@@ -312,9 +313,7 @@ public class PersistentFS_FindFilesTest {
try {
List extends Future>> futures = ContainerUtil.map(tasks, task -> pool.submit(task));
- for (Future> future : futures) {
- future.get();
- }
+ ConcurrencyUtil.getAll(futures);
}
finally {
pool.shutdown();
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/namecache/FileNameCacheMicroBenchmark.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/namecache/FileNameCacheMicroBenchmark.java
index 4b6f6e853875..e272080bc994 100644
--- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/namecache/FileNameCacheMicroBenchmark.java
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/namecache/FileNameCacheMicroBenchmark.java
@@ -7,6 +7,7 @@ import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.IdeaTestFixture;
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
+import com.intellij.util.ConcurrencyUtil;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
@@ -191,9 +192,7 @@ public final class FileNameCacheMicroBenchmark {
futures.add(ApplicationManager.getApplication().executeOnPooledThread(
() -> testIteration.doTest(finalI, ids, threadRandom, queryCount)));
}
- for (Future> future : futures) {
- future.get();
- }
+ ConcurrencyUtil.getAll(futures);
return System.currentTimeMillis() - start;
}
diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt
index 2034239610d0..473f8cecd6ef 100644
--- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt
+++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt
@@ -107,7 +107,7 @@ class ThreadContextPropagationTest {
val service = EdtScheduledExecutorService.getInstance()
doScheduledExecutorServiceTest(service)
doPropagationTest {
- service.schedule(it.runnable(), ModalityState.any(), 10, TimeUnit.MILLISECONDS)
+ service.schedule(it.runnable(), 10, TimeUnit.MILLISECONDS)
}
}
diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties
index 246deafc97e6..0ff96cb3a120 100644
--- a/platform/util/resources/misc/registry.properties
+++ b/platform/util/resources/misc/registry.properties
@@ -35,12 +35,19 @@ actionSystem.playback.typecommand.delay=200
actionSystem.playback.typecommand.delay.description=Delay after typing and shortcut related commands\
\ being executed by the playback runner.
actionSystem.playback.useDirectActionCall=true
+actionSystem.playback.useDirectActionCall.description=Invoke actions directly instead of dispatching input events during test playback
actionSystem.playback.useTypingTargets=true
+actionSystem.playback.useTypingTargets.description=Send typing events to typing targets during playback
actionSystem.keyGestures.enabled=false
+actionSystem.keyGestures.enabled.description=Enable key gesture shortcuts (multi-stroke) handling
actionSystem.suspendFocusTransferIfApplicationInactive=true
+actionSystem.suspendFocusTransferIfApplicationInactive.description=Suspend focus transfers when the application is inactive
actionSystem.getContextByRecentMouseEvent=false
+actionSystem.getContextByRecentMouseEvent.description=Get DataContext based on the most recent mouse event
actionSystem.secondKeystrokeTimeout=2000
+actionSystem.secondKeystrokeTimeout.description=Timeout in milliseconds to wait for the second keystroke of a multi-stroke shortcut
actionSystem.win.suppressAlt=true
+actionSystem.win.suppressAlt.description=Suppress native Alt key menu activation on Windows
actionSystem.update.actions.async.max.nested.loops=20
actionSystem.update.actions.async.max.nested.loops.description=A maximum number of nested EDT loops
@@ -65,7 +72,9 @@ action.toolbar.position.bottom.on.mac=false
action.toolbar.position.bottom.on.mac.description=Moves the action toolbar to the bottom of the corresponding component. Mac only!
actionSystem.mouseGesturesEnabled=true
+actionSystem.mouseGesturesEnabled.description=Enable mouse gesture support for invoking actions
actionSystem.assertFocusAccessFromEdt=true
+actionSystem.assertFocusAccessFromEdt.description=Assert that focus-related APIs are accessed on the EDT
ide.highlighting.mode.essential=false
ide.highlighting.mode.essential.description=Enable essential highlighting mode
@@ -124,9 +133,9 @@ ide.recent.files.tool.window.list=true
ide.recent.files.tool.window.list.description=Shows a list of tool windows in the Recent Files popup
ide.new.tool.window.resize.area.height=13
-ide.new.tool.window.resize.area.height.description=Size of tool window header top part [1..26] reserved for resize (while the bottom part is for drag-n-drop)
+ide.new.tool.window.resize.area.height.description=Size of the tool window header top part [1..26] reserved for resize (while the bottom part is for drag-n-drop)
ide.new.tool.window.start.drag.deadzone=100
-ide.new.tool.window.start.drag.deadzone.description=Minimal distance from pressed point to start drag (and thus to avoid mistaken one)
+ide.new.tool.window.start.drag.deadzone.description=Minimal distance from the pressed point to start drag (and thus to avoid mistaken one)
ide.switcher.tool.window.list=true
ide.switcher.tool.window.list.description=Shows a list of tool windows in the Switcher popup
ide.allow.tool.window.tabs.reorder=true
@@ -160,9 +169,9 @@ ide.navigate.to.recently.focused.editor.description=Use the last focused editor
By default, navigation (e.g., from tool windows) goes to the main IDE window even if you work with editors in a separate window.
ide.editor.max.pinned.tab.width=2000
-ide.editor.max.pinned.tab.width.description=Max width for pinned editor tab
+ide.editor.max.pinned.tab.width.description=Max width for a pinned editor tab
ide.editor.tabs.interactive.pin.button=true
-ide.editor.tabs.interactive.pin.button.description=Unpin pinned editor tab when 'pin' button is clicked
+ide.editor.tabs.interactive.pin.button.description=Unpin the pinned editor tab when the 'pin' button is clicked
ide.editor.tabs.show.fadeout=true
ide.editor.tabs.show.fadeout.description=Show fadeout overlay to decorate partially hidden (scrolled aside) tabs
ide.editor.tabs.fadeout.width=10
@@ -172,15 +181,15 @@ ide.editor.tabs.visible.in.presentation.mode.description=Keep editor tabs visibl
ide.hide.excluded.files=false
ide.hide.excluded.files.restartRequired=true
-ide.hide.excluded.files.description=Do not show excluded files in Project View and exclude them from VCS
+ide.hide.excluded.files.description=Do not show excluded files in the Project View and exclude them from VCS
use.tty.for.external.tools=false
-use.tty.for.external.tools.description=Use command line with TTY support to run 'External Tools'
+use.tty.for.external.tools.description=Use the command line with TTY support to run 'External Tools'
ide.appIcon.badge=true
ide.appIcon.requestAttention=true
ide.appIcon.requestAttention.after.indexing=false
-ide.appIcon.requestAttention.after.indexing.description=Whether IDE should request user's attention (e.g., by flashing application icon on taskbar) after the end of project analysis
+ide.appIcon.requestAttention.after.indexing.description=Whether IDE should request user's attention (e.g., by flashing the application icon on the taskbar) after the end of project analysis
win.request.focus.delay.ms=20
win.request.focus.delay.ms.description=Time to wait (blocking EDT) before trying to activate the IDE window on Windows. The value around \
@@ -191,7 +200,7 @@ ide.index.image.max.size=10
ide.index.image.max.size.description=Max size of an image to index, in megabytes
no.window.actions.in.editor=false
-no.window.actions.in.editor.description=Disables window actions "Stretch to\u2026" within focused editor as before.
+no.window.actions.in.editor.description=Disables window actions "Stretch to\u2026" within the focused editor as before.
ide.windowSystem.hScrollChars=5
ide.windowSystem.vScrollChars=5
@@ -199,20 +208,30 @@ ide.windowSystem.autoShowProcessPopup=false
ide.windowSystem.showListItemsPopup=true
ide.tree.yieldingUiUpdate=true
+ide.tree.yieldingUiUpdate.description=Perform heavy tree UI updates in yielding mode to keep the UI responsive at the cost of longer updates
ide.tree.showBusyIndicator=true
+ide.tree.showBusyIndicator.description=Show a busy/loading indicator in tree controls while data is being loaded or updated
ide.tree.waitForReadyTimeout=250
+ide.tree.waitForReadyTimeout.description=Maximum time in milliseconds to wait for a tree to become ready before proceeding
ide.tree.clearOnHideTime.description=Time in milliseconds when the hidden project view tree will be deactivated.
ide.tree.autoscrollToVCenter=false
+ide.tree.autoscrollToVCenter.description=Automatically scroll the selected tree node to the vertical center of the visible area
ide.tree.horizontal.default.autoscrolling=true
ide.tree.horizontal.default.autoscrolling.description=Allow automatic horizontal scroll of selected tree nodes to visible area in tree views
ide.tree.ensureSelectionOnFocusGained=true
+ide.tree.ensureSelectionOnFocusGained.description=Ensure that a tree has a valid selection when it gains focus
ide.tree.autoExpandMaxDepth=5
+ide.tree.autoExpandMaxDepth.description=Maximum depth of nodes to auto-expand in trees
ide.tree.expandRecursionDepth=50
+ide.tree.expandRecursionDepth.description=Maximum recursion depth when expanding tree nodes programmatically
ide.tree.checkStructure=false
+ide.tree.checkStructure.description=Enable additional consistency checks for tree structure updates (may impact performance)
ide.tree.uiLockAttempt=250
+ide.tree.uiLockAttempt.description=Maximum time in milliseconds to attempt to acquire the UI lock for tree updates before yielding
ide.tree.deferred.icon.invalidates.cache=true
+ide.tree.deferred.icon.invalidates.cache.description=Whether a change in a deferred icon should invalidate the renderer cache for the corresponding tree node
ide.tree.expand.on.double.click=[DEFAULT*|ALWAYS|NEVER|NAVIGATABLE]
ide.tree.expand.on.double.click.description=Specify the default behavior of expanding/collapsing tree nodes by double-clicking (see ExpandOnDoubleClick enum)
ide.tree.additional.rows.count=1
@@ -224,7 +243,7 @@ ide.rich.cancellation.traces.description=Enables detailed stacktrace collection
edit.source.on.enter.key.disabled=false
edit.source.on.enter.key.disabled.description=Allows preventing navigation on the Enter key pressed
edit.source.on.enter.key.non.source.navigation.enabled=false
-edit.source.on.enter.key.non.source.navigation.enabled.description=Allows navigation even if navigatable object cannot navigate to the editor
+edit.source.on.enter.key.non.source.navigation.enabled.description=Allows navigation even if a navigatable object cannot navigate to the editor
ide.bookmark.show.all.breakpoints=false
ide.bookmark.show.all.breakpoints.description=Shows every breakpoint in the Bookmarks tool window
@@ -236,12 +255,12 @@ ide.bookmark.mnemonic.chooser.cyclic.scrolling.allowed=true
ide.bookmark.mnemonic.chooser.cyclic.scrolling.allowed.description=Allows cyclic scrolling in the Mnemonic Chooser
ide.bookmark.mnemonic.chooser.always.above.gutter=false
-ide.bookmark.mnemonic.chooser.always.above.gutter.description=Always shows above editor's gutter if possible
+ide.bookmark.mnemonic.chooser.always.above.gutter.description=Always shows the above editor's gutter if possible
ide.tree.ui.cyclic.scrolling.allowed=false
ide.tree.ui.cyclic.scrolling.allowed.description=Enables cyclic scrolling in trees if the UISettings.getCycleScrolling is enabled
ide.tree.painter.classic.compact=false
-ide.tree.painter.classic.compact.description=Enables the compact version of classic painter for tree control
+ide.tree.painter.classic.compact.description=Enables the compact version of the classic painter for tree control
ide.tree.painter.compact.default=false
ide.tree.painter.compact.default.description=Enables the default version of compact painter for tree control
@@ -253,7 +272,7 @@ ide.tree.ui.async.batch.size.description=Number of virtual files to process in o
ide.tree.experimental.layout.cache=true
ide.tree.experimental.layout.cache.restartRequired=true
-ide.tree.experimental.layout.cache.description=Use experimental tree layout cache for all trees
+ide.tree.experimental.layout.cache.description=Use the experimental tree layout cache for all trees
ide.tree.experimental.layout.cache.debug=false
ide.tree.experimental.layout.cache.debug.description=Enable debugging (slow!) for DefaultTreeLayoutCache
@@ -272,18 +291,26 @@ ide.main.menu.expand.horizontal=true
ide.main.menu.expand.horizontal.description=Expand the hamburger menu horizontally (Windows and Linux only)
ide.macos.main.menu.alignment.options=[Native*|Aligned|Aligned in group|No icons]
-ide.macos.main.menu.alignment.options.description="Native" - align menu items as macOS does, \
- "Aligned" - align menu items vertically inside a group, \
- "No icons" - hide icons
+ide.macos.main.menu.alignment.options.description="Native" \u2013 align menu items as macOS does, \
+ "Aligned" \u2013 align menu items vertically inside a group, \
+ "No icons" \u2013 hide icons
ide.splitter.mouseZone=6
+ide.splitter.mouseZone.description=The size in pixels of the mouse resize zone around splitters
ide.splitter.chooser.info.panel.height=150
+ide.splitter.chooser.info.panel.height.description=Specifies the height of the Splitter chooser info panel in pixels
ide.splitter.chooser.info.panel.width=250
+ide.splitter.chooser.info.panel.width.description=Specifies the width of the Splitter chooser info panel in pixels
ide.splitter.chooser.info.panel.arc=10
+ide.splitter.chooser.info.panel.arc.description=Specifies the corner arc radius (in pixels) for the info panel\u2019s rounded corners
ide.splitter.chooser.info.panel.arrows.shift.center=-20
+ide.splitter.chooser.info.panel.arrows.shift.center.description=Specifies the offset of the arrows from the panel center in pixels
ide.splitter.chooser.info.panel.arrows.shift.vertical=20
+ide.splitter.chooser.info.panel.arrows.shift.vertical.description=Specifies the additional vertical offset for arrows inside the info panel in pixels
ide.splitter.chooser.info.panel.arrows.shift.horizontal=30
+ide.splitter.chooser.info.panel.arrows.shift.horizontal.description=Specifies the additional horizontal offset for arrows inside the info panel in pixels
ide.splitter.chooser.info.panel.text.shift=50
+ide.splitter.chooser.info.panel.text.shift.description=Specifies the offset of the text inside the info panel in pixels
ide.autoscroll.to.source.delay=100
ide.autoscroll.to.source.delay.description=Delay before performing the AutoscrollToSource action
@@ -307,13 +334,13 @@ ide.scroll.thumb.small.if.opaque=false
ide.scroll.thumb.small.if.opaque.description=Allows tuning an opaque thumb thickness. Affects Windows and Linux only.
ide.onboarding.background.enabled=true
-ide.onboarding.background.enabled.description=Enable background images for License, Import Settings and Update IDE screens dialogs.
+ide.onboarding.background.enabled.description=Enable background images for License, Import Settings, and Update IDE screens dialogs.
ide.update.dialog.new.ui.enabled=false
ide.update.dialog.new.ui.enabled.description=Enable the new UI for IDE Update Dialog
ide.onboarding.background.use.long.timeout=true
-ide.onboarding.background.use.long.timeout.description=If true, long timeout (30 sec) for background images for License and Import Settings screens dialogs is used
+ide.onboarding.background.use.long.timeout.description=If true, the long timeout (30 sec) for background images for License and Import Settings screens dialogs is used
mac.scroll.horizontal.gap=true
mac.scroll.horizontal.gap.description=Shows additional space after the last row of a list or a tree. Affects macOS only.
@@ -328,25 +355,33 @@ ide.preferred.scrollable.viewport.fixed.height=0
ide.preferred.scrollable.viewport.fixed.height.description=Specifies default row height for an empty list
ide.preferred.scrollable.viewport.visible.rows=10
-ide.preferred.scrollable.viewport.visible.rows.description=Specifies default number of visible rows in a list
+ide.preferred.scrollable.viewport.visible.rows.description=Specifies the default number of visible rows in a list
ide.preferred.scrollable.viewport.extra.space=true
ide.preferred.scrollable.viewport.extra.space.description=Adds an extra space to show that a list can be scrolled
ide.suvorov.progress.kind=[None|Bar|Spinning|Overlay|NiceOverlay*]
-ide.suvorov.progress.kind.description=Kind of progress that is used to make an impression of live IDE when it is freezed on acquisition of locks
+ide.suvorov.progress.kind.description=Kind of progress that is used to make an impression of a live IDE when it is freezed on acquisition of locks
ide.suvorov.progress.showing.delay.ms=1000
-ide.suvorov.progress.showing.delay.ms.description=Defines delay until the UI component of progress indicator during a UI freeze starts being visible
+ide.suvorov.progress.showing.delay.ms.description=Defines delay until the UI component of the progress indicator during a UI freeze starts being visible
ide.tooltip.animationCycle=150
+ide.tooltip.animationCycle.description=Duration in milliseconds for one animation cycle of tooltip animations.
ide.tooltip.initialDelay=1200
+ide.tooltip.initialDelay.description=Delay in milliseconds before showing a tooltip after the pointer rests on a component.
ide.tooltip.initialDelay.highlighter=150
+ide.tooltip.initialDelay.highlighter.description=Delay in milliseconds before showing editor highlighter tooltips.
ide.tooltip.reshowDelay=500
+ide.tooltip.reshowDelay.description=Minimum time in milliseconds before a tooltip can be shown again after it was hidden.
ide.tooltip.dismissDelay=4000
+ide.tooltip.dismissDelay.description=Time in milliseconds after which a visible tooltip is automatically hidden if the mouse stays idle.
ide.tooltip.initialReshowDelay=500
+ide.tooltip.initialReshowDelay.description=Delay in milliseconds before showing a tooltip when moving within the same component.
ide.tooltip.autoDismissDeadZone=300
+ide.tooltip.autoDismissDeadZone.description=Grace-period in milliseconds around auto\u2011dismiss to reduce flicker when the pointer is near the tooltip.
ide.tooltip.showAllSeverities=false
+ide.tooltip.showAllSeverities.description=Show all inspection severities in editor tooltips instead of only the most relevant.
ide.disable.editor.tooltips=false
ide.disable.editor.tooltips.description=Prevents tooltips displayed in the editor (provided by inspections etc.)
ide.balloon.shadowEnabled=true
@@ -369,10 +404,10 @@ gutter.tooltip.single.link.description=Shows a single link for all classes in a
ide.helptooltip.regular.dismissDelay=10000
ide.helptooltip.full.dismissDelay=30000
ide.hide.expandable.tooltip.owner.mouse.exit=true
-ide.hide.expandable.tooltip.owner.mouse.exit.description=Provides autohiding for expandable tooltip when mouse leaves visible area of 'owner' component
+ide.hide.expandable.tooltip.owner.mouse.exit.description=Provides autohiding for expandable tooltip when the mouse leaves the visible area of the 'owner' component
ide.expansion.hints.enabled=true
-ide.expansion.hints.enabled.description=Enables expansion hints for lists, trees, and tables to show invisible part of a cell renderer.
+ide.expansion.hints.enabled.description=Enables expansion hints for lists, trees, and tables to show the invisible part of a cell renderer.
ide.browser.jcef.enabled=true
ide.browser.jcef.enabled.restartRequired=true
@@ -391,14 +426,14 @@ ide.browser.jcef.markdownView.osr.enabled.description=Whether the Markdown view
ide.browser.jcef.codeWithMe.osr.enabled=false
ide.browser.jcef.codeWithMe.osr.enabled.description=Whether Code With Me should be displayed in the off-screen mode.
ide.browser.jcef.codeWithMe.inBrowserScreenSharing.enabled=true
-ide.browser.jcef.codeWithMe.inBrowserScreenSharing.enabled.description=Start screen sharing in external browser.
+ide.browser.jcef.codeWithMe.inBrowserScreenSharing.enabled.description=Start screen sharing in an external browser.
ide.browser.jcef.headless.enabled=false
ide.browser.jcef.headless.enabled.description=Enable JCEF browser in the IDE headless mode
ide.browser.jcef.testMode.enabled=false
ide.browser.jcef.testMode.enabled.description=Allows for JBCefApp.getInstance() to re-initialize on a subsequent call.
ide.browser.jcef.debug.port=-1
ide.browser.jcef.debug.port.restartRequired=true
-ide.browser.jcef.debug.port.description=The port number at which remote DevTools will be available(0 - to select a random)
+ide.browser.jcef.debug.port.description=The port number at which remote DevTools will be available (0 \u2013 to select a random)
ide.browser.jcef.preinit=false
ide.browser.jcef.preinit.restartRequired=true
ide.browser.jcef.preinit.description=Force JCEF init at IDE start-up to support co-existence with JavaFX (affects macOS only)
@@ -406,7 +441,7 @@ ide.browser.jcef.jsQueryPoolSize=0
ide.browser.jcef.jsQueryPoolSize.description=Define JBCefJSQuery pool size used after browser creation
ide.browser.jcef.gpu.disable=false
ide.browser.jcef.gpu.disable.restartRequired=true
-ide.browser.jcef.gpu.disable.description=Disables GPU hardware acceleration in JCEF. If software renderer is not in place, then the GPU process won't launch.
+ide.browser.jcef.gpu.disable.description=Disables GPU hardware acceleration in JCEF. If a software renderer is not in place, then the GPU process won't launch.
ide.browser.jcef.gpu.infinitecrash=true
ide.browser.jcef.gpu.infinitecrash.restartRequired=true
ide.browser.jcef.gpu.infinitecrash.description=Disables crash limit for a GPU process. If it crashes too often, then there will be shown a notification (which recommends disabling GPU).
@@ -450,14 +485,14 @@ editor.focus.mode.color.dark=404040
editor.focus.mode.color.dark.description=Dark color for focus mode
editor.position.mouse.cursor.on.doubleclicked.tab=false
-editor.position.mouse.cursor.on.doubleclicked.tab.description=Position mouse cursor on editor tab when the tab was double-clicked \
+editor.position.mouse.cursor.on.doubleclicked.tab.description=Position the mouse cursor on the editor tab when the tab was double-clicked \
to perform 'Maximize Editor' or 'Hide All Tool Windows' actions
editor.max.caret.count=1000
editor.max.caret.count.description=Maximum number of carets that can be created in the editor (performance-related limit)
editor.block.caret.selection.vim-like=false
-editor.block.caret.selection.vim-like.description=On mouse-drag selection in editor with block caret, always include character under caret \
+editor.block.caret.selection.vim-like.description=On mouse-drag selection in the editor with a block caret, always include character under caret \
in selection
editor.caret.update.primary.selection=true
@@ -467,16 +502,16 @@ editor.navigation.history.stack.size=150
editor.navigation.history.stack.size.description=Stack size limit for back/forward and last/next edit location navigation
editor.reformat.on.typing=false
-editor.reformat.on.typing.description=Enable the auto formatting typed handler
+editor.reformat.on.typing.description=Enable the auto-formatting typed handler
editor.move.semicolon.after.paren=true
-editor.move.semicolon.after.paren.description=Moves semicolon typed before closing paren to the end of statement
+editor.move.semicolon.after.paren.description=Moves semicolon typed before closing paren to the end of the statement
editor.virtual.lines=5
editor.virtual.lines.description=Number of virtual empty lines displayed at the editor bottom
editor.scrolling.animation.interval.ms=100
-editor.scrolling.animation.interval.ms.description=Animation interval (in milliseconds) for scrolling in the editor, when mouse is dragged \
- beyond visible area. The smaller the value, the faster is scrolling.
+editor.scrolling.animation.interval.ms.description=Animation interval (in milliseconds) for scrolling in the editor, when the mouse is dragged \
+ beyond the visible area. The smaller the value, the faster is scrolling.
editor.highlight.selected.text.max.occurrences.threshold=50
editor.highlight.selected.text.max.occurrences.threshold.description=Maximum number of highlights in the editor when selecting text. \
@@ -489,13 +524,13 @@ editor.rd.reopen.editors.on.frontend.description=Enables reopening the editors o
consequently, restoring the editor splitters
highlight.caret.line.at.custom.fold=false
-highlight.caret.line.at.custom.fold.description=Whether caret line highlighting is applied when caret is located at a custom fold region
+highlight.caret.line.at.custom.fold.description=Whether caret line highlighting is applied when the caret is located at a custom fold region
doc.render.cache.size=0.02
-doc.render.cache.size.description=Maximum fraction of available heap that will be used to store rendered comments (text content)
+doc.render.cache.size.description=Maximum fraction of the available heap that will be used to store rendered comments (text content)
doc.render.cache.size.restartRequired=true
doc.render.image.cache.size=0.04
-doc.render.image.cache.size.description=Maximum fraction of available heap that will be used to store (unpacked) images in rendered comments
+doc.render.image.cache.size.description=Maximum fraction of the available heap that will be used to store (unpacked) images in rendered comments
doc.render.image.cache.size.restartRequired=true
doc.render.adaptive.image=false
doc.render.adaptive.image.description=Use the new experimental adaptive image loading
@@ -524,9 +559,9 @@ ide.run.blocking.cancellable.assert.in.tests=false
ide.run.blocking.cancellable.assert.in.tests.description=Implement assertions within 'runBlockingCancellable' in tests
ide.unused.symbol.calculation.maxFilesToSearchUsagesIn=10
-ide.unused.symbol.calculation.maxFilesToSearchUsagesIn.description=Max number of scanned files to find usages of symbol
+ide.unused.symbol.calculation.maxFilesToSearchUsagesIn.description=Max number of scanned files to find usages of a symbol
ide.unused.symbol.calculation.maxFilesSizeToSearchUsagesIn=524288
-ide.unused.symbol.calculation.maxFilesSizeToSearchUsagesIn.description=Max files size in bytes to find usages of symbol in case there are a few files
+ide.unused.symbol.calculation.maxFilesSizeToSearchUsagesIn.description=Max files size in bytes to find usages of a symbol in case there are a few files
ide.tabbedPane.dragOutMultiplier=0.1
ide.tabbedPane.dragOutMultiplier.description=A threshold to define if editor tab drag&drop is intentional (ratio of tab size)
@@ -534,7 +569,7 @@ ide.tabbedPane.dragToSplitRatio=0.2
ide.tabbedPane.dragToSplitRatio.description=Ratio of editor tabs area to process drag-n-drop as vertical or horizontal split
ide.warmup.use.predicates=true
-ide.warmup.use.predicates.description=Use predicate-based warmup instead of custom configuration-based one
+ide.warmup.use.predicates.description=Use predicate-based warmup instead of a custom configuration-based one
file.chooser.async.tree.model=true
file.chooser.async.tree.model.description=Use the new implementation based on AsyncTreeModel + FileTreeModel
@@ -575,31 +610,39 @@ ide.mac.pressAndHold.workaround=true
ide.mac.pressAndHold.workaround.description=Enable workaround for press-and-hold input method for accented characters on macOS
ide.mac.pressAndHold.brute.workaround=false
ide.mac.pressAndHold.brute.workaround.description=Enable alternative workaround for press-and-hold input method for accented characters on macOS. \
- It might work in more cases, but known to cause issues with Japanese text input.
+ It might work in more cases, but is known to cause issues with Japanese text input.
debugger.valueTooltipAutoShow=true
debugger.valueTooltipAutoShow.description=Auto show tooltip on mouse over.
debugger.valueTooltipAutoShowOnSelection=false
debugger.valueTooltipAutoShowOnSelection.description=Auto show tooltip when editor selection changes
debugger.mayBringFrameToFrontOnBreakpoint=true
+debugger.mayBringFrameToFrontOnBreakpoint.description=Bring the IDE window to front when a breakpoint is hit
debugger.mayBringDebuggeeWindowToFrontAfterResume=true
+debugger.mayBringDebuggeeWindowToFrontAfterResume.description=Bring the debuggee window to front after resume
# currently enabled only in Rider
debugger.mayBringDebuggeeWindowToFrontAfterResume.supported=false
-debugger.mayBringDebuggeeWindowToFrontAfterResume.supported.description=State of "Bring debuggee to foreground" feature
+debugger.mayBringDebuggeeWindowToFrontAfterResume.supported.description=State of the "Bring debuggee to foreground" feature
debugger.batch.evaluation=true
+debugger.batch.evaluation.description=Evaluate multiple values in a single batch request to the debuggee
debugger.batch.evaluation.force=false
+debugger.batch.evaluation.force.description=Force batch evaluation even when individual evaluation is possible
debugger.compiling.evaluator.magic.accessor=false
+debugger.compiling.evaluator.magic.accessor.description=Allow access to private members via generated accessor methods in the compiling evaluator
debugger.compiling.evaluator.reflection.access.with.java8=true
+debugger.compiling.evaluator.reflection.access.with.java8.description=Allow reflective access in the compiling evaluator compatible with Java 8 rules
debugger.compiling.evaluator.extract.generated.class=true
-debugger.compiling.evaluator.extract.generated.class.description=Extract generated class to a separate file \
+debugger.compiling.evaluator.extract.generated.class.description=Extract the generated class to a separate file \
(only for java versions lower than 11)
debugger.compiling.evaluator.method.refs=false
debugger.compiling.evaluator.method.refs.description=Use the compiling evaluator for the code with method references
debugger.watches.in.variables=true
+debugger.watches.in.variables.description=Show the Watches node inside the Variables view
debugger.auto.fetch.icons=true
+debugger.auto.fetch.icons.description=Automatically request icons for objects shown in debugger views when available
debugger.close.dialog.on.navigate=true
debugger.close.dialog.on.navigate.description=Close the Evaluate dialog on Jump to (type) source action
debugger.async.smart.step.into=true
@@ -613,13 +656,13 @@ debugger.click.disable.breakpoints.description=Use a single click to disable a b
debugger.renderers.file=true
debugger.renderers.file.description=Enable file object renderer
debugger.renderers.arrays=true
-debugger.renderers.arrays.description=Show first elements for primitive and string arrays
+debugger.renderers.arrays.description=Show the first elements for primitive and string arrays
debugger.renderers.arrays.max.primitives=100
debugger.renderers.arrays.max.primitives.description=Number of primitive values to show
debugger.renderers.arrays.max.strings=5
debugger.renderers.arrays.max.strings.description=Number of string values to show
debugger.single.smart.step.force=true
-debugger.single.smart.step.force.description=Do force step-into on single variant smart step-into
+debugger.single.smart.step.force.description=Do force step-into on a single variant smart step-into
debugger.method.breakpoints.entry.default=true
debugger.method.breakpoints.entry.default.description=Stop only on method entry by default
debugger.emulate.method.breakpoints=true
@@ -627,9 +670,13 @@ debugger.emulate.method.breakpoints.description=Emulate method breakpoints with
debugger.intern.string.literals=false
debugger.intern.string.literals.description=Make string literal refer to the same instance of class String
debugger.capture.points.agent.debug=false
+debugger.capture.points.agent.debug.description=Enable debug logging for capture points agent
debugger.collection.breakpoint.agent.debug=false
+debugger.collection.breakpoint.agent.debug.description=Enable debug logging for collection breakpoints agent
debugger.capture.points.agent.annotations=true
+debugger.capture.points.agent.annotations.description=Enable reading capture points from annotations
debugger.collection.watchpoints.enabled=false
+debugger.collection.watchpoints.enabled.description=Enable collection watchpoints support
debugger.async.stacks.coroutines=true
debugger.async.stacks.coroutines.description=Enable async stack traces for coroutines
debugger.async.stacks.flows=true
@@ -643,56 +690,49 @@ debugger.log.async.stacks.description=Log async stack trace when log stack trace
debugger.async.stack.trace.for.exceptions.printing=true
debugger.async.stack.trace.for.exceptions.printing.description=Include async stack trace in exceptions' printed stack trace
debugger.resume.yourkit.threads=true
+debugger.resume.yourkit.threads.description=Automatically resume YourKit profiler helper threads when debugging
debugger.keep.step.requests=false
+debugger.keep.step.requests.description=Keep step requests active after a step completes (for advanced troubleshooting)
debugger.enable.memory.view=true
+debugger.enable.memory.view.description=Enable the Memory view/tool window in the debugger
debugger.enable.overhead.monitor=true
+debugger.enable.overhead.monitor.description=Enable the debugger overhead monitor to track evaluation cost
debugger.memory.agent.debug=false
-debugger.memory.agent.debug.description=Enable debug logging to stdout from memory agent
+debugger.memory.agent.debug.description=Enable debug logging to stdout from a memory agent
debugger.memory.agent.debug.path=
-debugger.memory.agent.debug.path.description=Force the debugger to use the specified memory agent library instead of bundled one (only for debugging purpose). Will be used only if debugger.memory.agent.debug checked
+debugger.memory.agent.debug.path.description=Force the debugger to use the specified memory agent library instead of the bundled one (only for debugging purposes). Will be used only if debugger.memory.agent.debug checked
debugger.memory.agent.action.timeout=-1
-debugger.kotlin.analysis.api.editor.text.provider=true
-debugger.kotlin.analysis.api.editor.text.provider.description=Use Analysis API-based implementation of 'EditorTextProvider'
-debugger.kotlin.inline.stack.trace.enabled=true
-debugger.kotlin.inline.stack.trace.enabled.description=Enable inline stack trace
-debugger.kotlin.show.coroutines.in.threadDumpPanel=true
-debugger.kotlin.show.coroutines.in.threadDumpPanel.description=Merge coroutine dump with thread dump in ThreadDumpPanel
-debugger.kotlin.auto.show.coroutines.view=false
-debugger.kotlin.auto.show.coroutines.view.description=Automatically open coroutines view if stack trace contains coroutine stack frames
debugger.memory.agent.action.timeout.description=Set debugger memory agent action timeout in millis
debugger.memory.agent.use.in.memory.view=true
debugger.memory.agent.use.in.memory.view.description=Use memory agent to estimate retained sizes in the Instances Window
-debugger.enable.kotlin.evaluator.in.java.context=false
-debugger.enable.kotlin.evaluator.in.java.context.description=Enable Kotlin evaluation features when hitting/suspending in Java frames \
- (suggest Kotlin language in evaluation prompts, conditional breakpoints and "Evaluate and log" field; support Kotlin watches)
-debugger.kotlin.multiline.lambda.breakpoints=false
-debugger.kotlin.multiline.lambda.breakpoints.description=Allow installing breakpoint on multiline lambdas
-debugger.kotlin.report.smart.step.into.targets.detection.failure=true
-debugger.kotlin.report.smart.step.into.targets.detection.failure.description=Report an error when smart targets detection in bytecode failed (leads to a failure in tests)
-debugger.kotlin.step.through.inline.lambdas=true
-debugger.kotlin.step.through.inline.lambdas.description=Step over action steps into inline lambdas (e.g., calls of let/run/synchronized/...)
debugger.tree.states.depth=100
+debugger.tree.states.depth.description=Maximum depth for remembering expanded/collapsed state in debugger trees
debugger.new.threads.view=false
+debugger.new.threads.view.description=Enable the experimental threads view in the debugger
debugger.new.tool.window.layout=false
debugger.new.tool.window.layout.restartRequired=true
-debugger.new.tool.window.layout.description=Enable new debugger UI (works only in the old UI)
+debugger.new.tool.window.layout.description=Enable the new debugger UI (works only in the old UI)
debugger.new.tool.window.layout.toolbar=[After*|Vertical]
-debugger.new.tool.window.layout.toolbar.description=Change toolbar position in new debugger UI
+debugger.new.tool.window.layout.toolbar.description=Change toolbar position in the new debugger UI
debugger.new.tool.window.layout.dnd=false
debugger.new.tool.window.layout.dnd.restartRequired=true
debugger.new.tool.window.layout.dnd.description=Enable drag and drop of the debugger tabs in the new debugger UI
debugger.new.tool.window.layout.single.content=false
debugger.new.tool.window.layout.single.content.description=Collapse the debug toolwindow header if there is only one active session
debugger.toolbar.before.tabs=true
-debugger.toolbar.before.tabs.description=Show debugger toolbar before tabs
+debugger.toolbar.before.tabs.description=Show the debugger toolbar before tabs
debugger.default.selected.view.key=Default
+debugger.default.selected.view.key.description=Selects which debugger view is shown by default (e.g., Variables, Console)
debugger.track.instrumentation=true
+debugger.track.instrumentation.description=Track class instrumentation events during debugging
debugger.variablesView.rss=true
debugger.variablesView.rss.description=Enable node expanding in the Variables view speed search
debugger.variablesView.rss.depth=4
-debugger.variablesView.rss.depth.description=Depth of search in variables view
+debugger.variablesView.rss.depth.description=Depth of search in the variables view
debugger.attach.to.process.action=false
+debugger.attach.to.process.action.description=Show the "Attach to Process" action in the run/debug tool window
debugger.sa.jdwp.debug=false
+debugger.sa.jdwp.debug.description=Enable additional JDWP debug logging for the shared agent
debugger.add.rt.jar=true
debugger.add.rt.jar.description=Add rt.jar to the classpath
debugger.evaluate.single.threaded.timeout=1000
@@ -700,61 +740,61 @@ debugger.evaluate.single.threaded.timeout.description=Number of milliseconds to
debugger.evaluate.load.helper.in.separate.classloader=false
debugger.evaluate.load.helper.in.separate.classloader.description=Always load helper classes in a new classloader
debugger.new.invocation.watcher=true
-debugger.new.invocation.watcher.description=When resume all, take into account the debugger model state of contexts and theirs resumed threads
+debugger.new.invocation.watcher.description=When resuming all, take into account the debugger model state of contexts and their resumed threads
debugger.retry.conditional.breakpoints=true
-debugger.retry.conditional.breakpoints.description=Retry to evaluate a condition in conditional breakpoints in case of ObjectCollectedException
+debugger.retry.conditional.breakpoints.description=Retry to evaluate a condition in conditional breakpoints in the case of ObjectCollectedException
debugger.call.tracing=false
+debugger.call.tracing.description=Enable call tracing in the debugger
debugger.call.tracing.arguments=true
+debugger.call.tracing.arguments.description=Include method arguments in call tracing
debugger.renderers.annotations=true
+debugger.renderers.annotations.description=Enable rendering values using annotations when available
debugger.smart.step.inplace=true
+debugger.smart.step.inplace.description=Show smart step into options inline in the editor
debugger.field.pin.to.top=true
+debugger.field.pin.to.top.description=Pin important fields to the top in the Variables view
debugger.jb.jdi=true
debugger.jb.jdi.description=Use the new forked JDI implementation
debugger.async.jdi=true
debugger.async.jdi.description=Use async JDI to speed up JDWP communication
debugger.async.frames=true
+debugger.async.frames.description=Collect and show asynchronous call frames in stack traces
debugger.library.frames.fold.instead.of.hide=true
debugger.library.frames.fold.instead.of.hide.description=Fold library frames into a single placeholder instead of completely hiding them
debugger.merge.pause.and.resume.description=Merge "Pause" and "Resume" buttons on the toolbar
debugger.auto.attach.from.console=false
-debugger.auto.attach.from.console.description=Auto attach to all processes available for debug from the console output
+debugger.auto.attach.from.console.description=Auto-attach to all processes available for debug from the console output
debugger.auto.attach.from.any.console=false
-debugger.auto.attach.from.any.console.description=Auto attach to all processes from any console output
+debugger.auto.attach.from.any.console.description=Auto-attach to all processes from any console output
debugger.preload.event.info=true
+debugger.preload.event.info.description=Preload frequently used JDWP event info to reduce latency
debugger.preload.types.async=true
debugger.preload.types.hierarchy=true
debugger.preload.types.hierarchy.description=Request superclass and interfaces for each loaded class in advance to speed up further operations
debugger.sync.commands.reschedule.delay=50
debugger.sync.commands.max.retries=5
debugger.attach.dialog.enabled=true
-debugger.attach.dialog.enabled.description=Show new "Attach to Process" dialog instead of popup
+debugger.attach.dialog.enabled.description=Show the new "Attach to Process" dialog instead of a popup
debugger.stack.frame.show.code.index=false
-debugger.stack.frame.show.code.index.description=Show bytecode index next to line number for stack frames
+debugger.stack.frame.show.code.index.description=Show the bytecode index next to the line number for stack frames
debugger.show.breakpoints.inline=true
debugger.show.breakpoints.inline.description=Show breakpoints as inline inlays (e.g., before lambdas)
debugger.show.breakpoints.inline.even.trivial=false
-debugger.show.breakpoints.inline.even.trivial.description=Show inline inlay for line breakpoint even if it's the only breakpoint
+debugger.show.breakpoints.inline.even.trivial.description=Show inline inlay for a line breakpoint even if it's the only breakpoint
debugger.report.non.default.inline.breakpoint=true
debugger.report.non.default.inline.breakpoint.description=Report as an error scenario when a user adds a breakpoint via gutter and then enables another one in the same line
debugger.inlayRunToCursor.hover.area=4
-debugger.inlayRunToCursor.hover.area.description=The size of hover area in the editor to show inlay Run-To-Cursor popup (counting in characters)
+debugger.inlayRunToCursor.hover.area.description=The size of the hover area in the editor to show the inlay Run-To-Cursor popup (counting in characters)
debugger.anti.flickering.delay=200
debugger.anti.flickering.delay.description=Use an anti-flickering panel atop of debugger variable tree and freeze repainting on specified ms \
after stepping. Zero value means do not use an anti-flickering panel. Need to restart debug after setting it to zero and back.
debugger.stop.on.graceful.exit=true
-debugger.stop.on.graceful.exit.description=Immediately detach debugger when process is about to terminate
+debugger.stop.on.graceful.exit.description=Immediately detach the debugger when a process is about to terminate
-rider.debugger.enable.collections.visualizers=true
-rider.debugger.enable.collections.visualizers.description=Enable a link next to a collection in local variable's pane and in a hint, which let explore the collection in a table view
-
-execution.java.always.debug=false
-execution.java.always.debug.description=Always run java processes with the debug agent
debugger.save.and.compare=false
debugger.save.and.compare.description=Enable "Save & Compare" feature in the debugger
debugger.show.offsets.in.frames=false
-debugger.show.offsets.in.frames.description=Show instruction offset in frames view
-debugger.log.jdi.in.unit.tests=false
-debugger.log.jdi.in.unit.tests.description=Enable full JDI logging under DebugProcessImpl.LOG.debug for unit tests
+debugger.show.offsets.in.frames.description=Show instruction offset in the frames view
debugger.hotswap.floating.toolbar=true
debugger.hotswap.floating.toolbar.description=Show a floating toolbar with a hot swap button on source change
@@ -774,16 +814,16 @@ build.toolwindow.show.inline.statistics=true
build.toolwindow.show.inline.statistics.description=Show execution time of the build and build steps
compiler.process.jdk=
-compiler.process.jdk.description=Path to a JDK home. Force build process to be run using the JDK specified. It is assumed that the jdk is at least of version 11.
+compiler.process.jdk.description=Path to a JDK home. Force the build process to be run using the JDK specified. It is assumed that the jdk is at least of version 11.
compiler.process.preload=true
-compiler.process.preload.description=After build finishes, the IDE will preload and leave waiting in memory the build process for the next compilation. This will save time for process start and project model loading (may be noticeable for big projects).
+compiler.process.preload.description=After the build finishes, the IDE will preload and leave waiting in memory the build process for the next compilation. This will save time for process start and project model loading (may be noticeable for big projects).
compiler.gc.wsl=true
compiler.gc.wsl.description=Run Build
compiler.process.debug.port=
-compiler.process.debug.port.description=Specifies build port at which the build process will wait for debugger connections\n\
+compiler.process.debug.port.description=Specifies the build port at which the build process will wait for debugger connections\n\
if 'Debug Build Process' (available via Find Action) is enabled.\n\
-1 means that a random available port will be chosen.
@@ -797,7 +837,7 @@ compiler.automake.build.while.idle.timeout=60000
compiler.automake.build.while.idle.timeout.description=Application inactivity period, after which automatic builds may be triggered, if necessary, for all currently opened projects. Set to negative value to disable the feature.
compiler.automake.allow.parallel=true
-compiler.automake.allow.parallel.description=The option allows to force automatically started builds to run in single-threaded mode even if 'Compile independent modules in parallel' is on\n\
+compiler.automake.allow.parallel.description=The option allows forcing automatically started builds to run in single-threaded mode even if 'Compile independent modules in parallel' is on\n\
This might help to save CPU resources for the foreground processes.
compiler.document.save.enabled=false
@@ -809,33 +849,26 @@ compiler.build.data.unused.threshold=30
compiler.build.data.unused.threshold.description=If a project is not opened for the specified number of days, its build data will be cleared to save disk space
compiler.build.report.statistics=false
-compiler.build.report.statistics.description=Turn on to display builder execution times as 'Information' messages in compiler toolwindow.
-
-compiler.build.can.use.eel=true
-compiler.build.can.use.eel.description=Allow using Eel API to run build processes.
-
-compiler.external.javac.keep.alive.timeout=300000
-compiler.external.javac.keep.alive.timeout.description=If not used for the specified period of time or longer, the IDE will shut down the \
- cached javac compilation process
+compiler.build.report.statistics.description=Turn on to display builder execution times as 'Information' messages in the compiler tool window.
compiler.natural.int.multimap.impl=false
compiler.natural.int.multimap.impl.description=Experimental implementation for class-to-class dependency multimap in JPS caches.\nIf turned off after being turned on, project rebuild is required.
compiler.process.use.portable.caches=false
compiler.process.use.portable.caches.restartRequired=true
-compiler.process.use.portable.caches.description=Warning: for Intellij project only! \nTurn on to download compilation caches for reducing build time.
+compiler.process.use.portable.caches.description=Warning: for Intellij project only! \nTurn on to download compilation caches to reduce build time.
lvcs.show.activity.view=true
lvcs.show.activity.view.description=Show new ui for the "Local History"
lvcs.open.diff.automatically=true
-lvcs.open.diff.automatically.description=Open diff tab (if it is configured to be opened in the editor) when "Local History" tab is shown.
+lvcs.open.diff.automatically.description=Open the diff tab (if it is configured to be opened in the editor) when the "Local History" tab is shown.
vcs.showConsole=true
-vcs.showConsole.description=Show 'Console' tab in VCS toolwindow that logs all write-commands performed by IDE.
+vcs.showConsole.description=Show the 'Console' tab in the VCS toolwindow that logs all write-commands performed by IDE.
vcs.log.bek.sort.disabled=false
vcs.log.bek.sort.disabled.description=Disable IntelliSort
vcs.log.linear.bek.sort=false
-vcs.log.linear.bek.sort.description=Third kind of IntelliSort that simplifies merges and tries to show history as linear
+vcs.log.linear.bek.sort.description=A third kind of IntelliSort that simplifies merges and tries to show history as linear
vcs.log.recent.commits.count=1000
vcs.log.recent.commits.count.description=The number of recent commits is loaded to be shown quickly (before the full log is loaded, which can take some time)
vcs.log.recent.commits.count.restartRequired=true
@@ -871,7 +904,7 @@ vcs.log.index.postpone.on.heavy.activity.or.power.save.description=Postpone Vcs
vcs.log.index.durable.enumerator=true
vcs.log.index.durable.enumerator.description=Use DurableEnumerator in the Vcs Log paths index instead of PersistentBTreeEnumerator.
vcs.log.speedsearch=false
-vcs.log.speedsearch.description=Enable speedsearch in Log (warning: performance is not excellent)
+vcs.log.speedsearch.description=Enable speedsearch in the Log (warning: performance is not excellent)
vcs.log.filter.messages.by.hash=true
vcs.log.filter.messages.by.hash.description=Find hashes in commit messages when filtering by hash.
vcs.log.copy.filters.to.new.tab=true
@@ -894,20 +927,20 @@ vcs.log.max.tags.shown.description=Limit for showing tags in the details panel.
vcs.log.max.tags.shown.restartRequired=true
vcs.log.use.indexed.details=false
vcs.log.use.indexed.details.description=Load commit details directly from the index in the EDT thread. \
- Enabling this key allows to load commit information in the Vcs Log instantly, but it may negatively affect table scrolling performance.
+ Enabling this key allows loading commit information in the Vcs Log instantly, but it may negatively affect table scrolling performance.
vcs.log.git.committers.robots=
vcs.log.git.committers.robots.description=Non-human git committers. E-mail addresses, comma delimited.
vcs.log.git.committers.robots.restartRequired=true
vcs.new.history=true
-vcs.new.history.description=Enable new file history that uses Log
+vcs.new.history.description=Enable the new file history that uses Log
vcs.history.refine=true
vcs.history.refine.description=Use a refining algorithm on file history that eliminates commits for different files having the same name.
vcs.history.remove.trivial.merges=true
-vcs.history.remove.trivial.merges.description=Remove trivial merge commits from file history.
+vcs.history.remove.trivial.merges.description=Remove trivial merge commits from the file history.
vcs.history.use.index=true
-vcs.history.use.index.description=Use index to show new file history
+vcs.history.use.index.description=Use index to show the new file history
vcs.show.affected.files.as.tab=true
-vcs.show.affected.files.as.tab.description=Use 'Changes' toolwindow instead of dialogs for 'Show Affected Files' and similar actions
+vcs.show.affected.files.as.tab.description=Use the 'Changes' tool window instead of dialogs for 'Show Affected Files' and similar actions
vcs.blame.show.affected.files.in.log=true
vcs.blame.show.affected.files.in.log.description=Jump to the corresponding commit in the 'Log' view when clicking an entry in the 'Annotate with Git Blame' pane instead of opening a dialog with affected files.
@@ -926,12 +959,12 @@ vcs.root.auto.add.nofity=false
vcs.root.auto.add.nofity.description=Display a notification that we detected and added some Git roots.
vcs.enable.partial.changelists.persist.file.contents=false
-vcs.enable.partial.changelists.persist.file.contents.description=Persist file contents on disk to restore partial changes for files modified while IDE is closed
+vcs.enable.partial.changelists.persist.file.contents.description=Persist file contents on disk to restore partial changes for files modified while the IDE is closed
vcs.enable.partial.changelists.undo=true
vcs.enable.partial.changelists.undo.description=Restore original markers state on Undo action
vcs.enable.partial.changelists.undo.restartRequired=true
vcs.enable.partial.changelists.redo=false
-vcs.enable.partial.changelists.redo.description=Restore original markers state on Redo action
+vcs.enable.partial.changelists.redo.description=Restore the original markers state on Redo action
vcs.enable.partial.changelists.redo.restartRequired=true
vcs.force.non.modal.commit=false
@@ -941,10 +974,10 @@ vcs.disable.changelists.description=Disable tracking assigned changelists for Lo
vcs.non.modal.commit.legend.compact=false
vcs.non.modal.commit.legend.compact.description=Use compact commit legend presentation in Local Changes
vcs.changes.tree.use.fixed.height.renderer=true
-vcs.changes.tree.use.fixed.height.renderer.description=Use faster fixed-height tree renderer for VCS trees
+vcs.changes.tree.use.fixed.height.renderer.description=Use a faster fixed-height tree renderer for VCS trees
vcs.prefer.checkboxes.over.selection=false
-vcs.prefer.checkboxes.over.selection.description=Prefer changes checked with checkboxes over usual selection. \
- Currently, it is only used for Rollback action.
+vcs.prefer.checkboxes.over.selection.description=Prefer changes checked with checkboxes over the usual selection. \
+ Currently, it is only used for the Rollback action.
vcs.unversioned.files.max.intree=1000
vcs.unversioned.files.max.intree.description=Maximum number of unversioned and ignored files displayed in the Local Changes file tree. \
If there are more unversioned or ignored files, they are substituted with a link which opens a dialog.
@@ -952,8 +985,8 @@ vcs.code.author.inlay.hints=false
vcs.code.author.inlay.hints.description=Show inlay hints with code author (based on vcs annotations) for classes, methods
vcs.code.analysis.before.checkin.show.only.new.threshold=-1
-vcs.code.analysis.before.checkin.show.only.new.threshold.description=Show only newly introduced warnings and errors in results \
- of before checkin analysis if the number of changed files is less or equals than the threshold
+vcs.code.analysis.before.checkin.show.only.new.threshold.description=Show only newly introduced warnings and errors in the results \
+ of before checkin analysis if the number of changed files is less or equal than the threshold
vcs.code.analysis.before.checkin.check.unused.only.changed.properties=true
vcs.code.analysis.before.checkin.check.unused.only.changed.properties.description=Perform the check if properties are used \
@@ -969,7 +1002,7 @@ vcs.process.externally.added.files.description=Process externally added files. \
vcs.enable.add.ignored.directories.to.exclude=true
vcs.enable.add.ignored.directories.to.exclude.description=Enables synchronization of ignored (but not excluded) directories with excluded directories on VCS ignored files rescan.
vcs.propose.add.ignored.directories.to.exclude=true
-vcs.propose.add.ignored.directories.to.exclude.description=Show notification with a proposal to add ignored (but not excluded) directories to exclude.
+vcs.propose.add.ignored.directories.to.exclude.description=Show a notification with a proposal to add ignored (but not excluded) directories to exclude.
vcs.resolve.symlinks.for.vcs.operations=prefer_target
vcs.resolve.symlinks.for.vcs.operations.restartRequired=true
@@ -1009,7 +1042,7 @@ keymap.windows.as.meta=false
keymap.windows.as.meta.description='Windows' key press/release events would be processed as 'Meta' modifier for shortcuts
read.system.shortcuts=true
-read.system.shortcuts.description=Read system shortcuts and check conflicts with selected keymap
+read.system.shortcuts.description=Read system shortcuts and check conflicts with the selected keymap
caches.scanningThreadsCount=-1
caches.scanningThreadsCount.description=Specify the number of threads used for scanning files to index. -1 means the default value ("all available threads count" minus 1).
@@ -1024,13 +1057,13 @@ projectView.showHierarchyErrors=true
projectView.hide.dot.idea=true
projectView.show.base.dir=true
-projectView.show.base.dir.description=Include files from project's base dir even if they do not belong to any content root.
+projectView.show.base.dir.description=Include files from the project's base dir even if they do not belong to any content root.
projectView.choose.directory.on.compacted.middle.packages=false
projectView.choose.directory.on.compacted.middle.packages.description=Show "Choose destination directory" when creating a new file with a node selected that corresponds to multiple compacted packages
projectView.always.show.inner.classes=false
-projectView.always.show.inner.classes.description=Shows inner classes in Project View even if Show Members is switched off
+projectView.always.show.inner.classes.description=Shows inner classes in the Project View even if Show Members is switched off
show.live.templates.in.completion=true
show.live.templates.in.completion.description=Show live templates in completion
@@ -1071,10 +1104,10 @@ documentation.component.highlighting.saturation.for.rendered.docs=65
documentation.component.highlighting.saturation.for.rendered.docs.description=Allows reducing saturation of highlighting colors in documentation rendered view hints to lower distraction from the main code. Here 100 is the normal saturation (bright tones) and 0 is zero saturation (gray tones)
documentation.show.toolbar=false
-documentation.show.toolbar.description=Show navigation toolbar in the documentation window
+documentation.show.toolbar.description=Show the navigation toolbar in the documentation window
documentation.v2.tw.focus.invocation.timeout=500
documentation.v2.tw.focus.invocation.timeout.description=How many milliseconds to wait for action invocation, which should focus the TW. \
- When "Auto-Update from Source" is turned off, the action invocation will refresh the TW content without focusing the TW right away. \
+ When "Auto-Update from Source" is turned off, the action invocation will refresh the TW content without focusing on the TW right away. \
During this timeout the subsequent action invocation will focus the TW. \
When "Auto-Update from Source" is turned on, there is no need to invoke the action to refresh the TW content, \
so the action invocation moves focus to the TW without the need for this timeout.
@@ -1090,7 +1123,7 @@ ide.completion.show.live.templates.on.top=false
ide.completion.show.live.templates.on.top.description=In completion, show suitable live templates before other suggestions
ide.lookup.preview.insertion=false
-ide.lookup.preview.insertion.description=Show preview of the approximate insertion result for the currently selected lookup suggestion.
+ide.lookup.preview.insertion.description=Show a preview of the approximate insertion result for the currently selected lookup suggestion.
ide.completion.middle.matching=true
ide.completion.middle.matching.description=Suggest items in completion that contain the entered string somewhere in the middle.
@@ -1102,14 +1135,11 @@ ide.completion.variant.limit=1000
ide.completion.variant.limit.description=The completion list will never show a number of (most relevant) items >= this number
ide.completion.max.width=99999
-ide.completion.max.width.description=Maximum width for completion popup in pixels. It can be used for making screenshots for blog posts and tweets.
+ide.completion.max.width.description=Maximum width for the completion popup in pixels. It can be used for making screenshots for blog posts and tweets.
ide.completion.show.visibility.icon=false
ide.completion.show.visibility.icon.description=Show member visibility type icon on the right of the base icon
-ide.java.completion.suggest.static.after.instance=false
-ide.java.completion.suggest.static.after.instance.description=Suggest static methods/fields after instance qualifier in Java completion
-
ide.goto.middle.matching=true
ide.goto.middle.matching.description=Suggest items in goto actions that contain the entered string somewhere in the middle.
@@ -1126,7 +1156,7 @@ ide.structural.navigation.visit.fields=false
ide.structural.navigation.visit.fields.description=Whether fields should be stopped at when navigating to the nex/previous structural member by Alt+Down/Up.
ide.optimize.inspection.visitors=true
-ide.optimize.inspection.visitors.description=Whether to skip elements that are not suitable for a visitor during inspections run
+ide.optimize.inspection.visitors.description=Whether to skip elements that are not suitable for a visitor during the inspections run
ide.dfa.time.limit.online=1000
ide.dfa.time.limit.online.description=Time limit (in milliseconds) that is allowed to analyze data flow for one method
@@ -1144,7 +1174,7 @@ idea.report.nullity.missing.in.generated.overriders=true
idea.report.nullity.missing.in.generated.overriders.description=Whether "@NotNull/@Nullable problems" inspection should complain about overriding methods or parameters missing @NotNull, which occur in generated code
ide.ignore.call.result.inspection.honor.inferred.pure=true
-ide.ignore.call.result.inspection.honor.inferred.pure.description=Whether inferred @Contract(pure=true) annotations should be taken into account in "Result of method call ignored" inspection
+ide.ignore.call.result.inspection.honor.inferred.pure.description=Whether inferred @Contract(pure=true) annotations should be taken into account in the "Result of method call ignored" inspection
ide.enable.toolwindow.stack=false
ide.enable.toolwindow.stack.description=Remember which toolwindows were shown before the currently active one on the same side, and show them when the active one is hidden
@@ -1177,7 +1207,7 @@ editor.loading.spinner.delay.ms=300
editor.loading.spinner.delay.ms.description=Delay before the loading icon is shown if the editor is not shown (in milliseconds)
refactorings.use.updated.conflicts.detected.dialog=true
-refactorings.use.updated.conflicts.detected.dialog.description=Show updated dialog when conflicts are detected during refactoring. Shows the old dialog when false.
+refactorings.use.updated.conflicts.detected.dialog.description=Show an updated dialog when conflicts are detected during refactoring. Shows the old dialog when false.
ide.find.show.tooltip.in.preview=true
ide.find.show.tooltip.in.preview.description=Show tooltip balloons in the find toolwindow preview when a tooltip is set for the usage
@@ -1185,20 +1215,22 @@ ide.find.show.tooltip.in.preview.description=Show tooltip balloons in the find t
floating.codeToolbar.verticalOffset=2
floating.codeToolbar.verticalOffset.description=Vertical offset between line and floating toolbar for code
floating.codeToolbar.showBelow=true
-floating.codeToolbar.showBelow.description=Always show floating code toolbar below the selection when possible
+floating.codeToolbar.showBelow.description=Always show the floating code toolbar below the selection when possible
ide.goto.implementation.show.interfaces=true
ide.goto.implementation.show.interfaces.description=Whether to show sub-interfaces when invoking the \
'Go to Implementation' action (Ctrl+Alt+B) on an interface.
actionSystem.force.alt.gr=false
+actionSystem.force.alt.gr.description=Force treating AltGraph as Ctrl+Alt on Windows/Linux
actionSystem.fix.alt.gr=true
+actionSystem.fix.alt.gr.description=Work around layout-specific issues with AltGraph key handling
completion.show.intention.icon=true
always.show.intention.above.current.line=false
-always.show.intention.above.current.line.description=Avoid showing intention popup on the line where caret stays (e.g., show it above current line)
+always.show.intention.above.current.line.description=Avoid showing the intention popup on the line where caret stays (e.g., show it above current line)
introduce.variable.prefer.statements=false
-introduce.variable.prefer.statements.description=If true, select line at caret if there is no selection on introduce; \
+introduce.variable.prefer.statements.description=If true, select the line at caret if there is no selection on introduce; \
when false expressions chooser would be shown
add.past.participle.to.suggested.names=true
@@ -1255,19 +1287,19 @@ ui.inspector.accessibility.audit.description=Show accessibility audit functional
comment.by.line.bulk.lines.trigger=100
diff.divider.repainting.disable.blitting=true
-diff.divider.repainting.disable.blitting.description=Fix painting glitch on scrolling in diff - disable BLIT_SCROLL_MODE to force repainting with RepaintManager
+diff.divider.repainting.disable.blitting.description=Fix the painting glitch on scrolling in diff - disable BLIT_SCROLL_MODE to force repainting with RepaintManager
diff.divider.width=24
-diff.divider.width.description=Width of the divider area in side-by-side diff viewer
+diff.divider.width.description=Width of the divider area in the side-by-side diff viewer
enable.patch.file.diff.viewer=true
-enable.patch.file.diff.viewer.description=Enable patch file viewer that allows to show changed files as a diff-like panel
+enable.patch.file.diff.viewer.description=Enable patch file viewer that allows showing changed files as a diff-like panel
combined.diff.files.limit=1000
-combined.diff.files.limit.description=If the limit is reached, show simple diff instead "All in One Diff".\ Set "-1" to turn the limit off.
+combined.diff.files.limit.description=If the limit is reached, show the simple diff instead of "All in One Diff".\ Set "-1" to turn the limit off.
combined.diff.loaded.content.limit=20
combined.diff.loaded.content.limit.description=Max number of loaded files diff contents to keep.\ Set "-1" to turn limit off and keep contents only inside viewport (+ viewport.delta).
combined.diff.visible.viewport.delta=3
-combined.diff.visible.viewport.delta.description=Set additionally number of blocks which need to be loaded before and beyond the viewport.
+combined.diff.visible.viewport.delta.description=Set additionally the number of blocks which need to be loaded before and beyond the viewport.
show.diff.as.frame=false
-show.diff.as.frame.description=Always force diff in separate window frame implementation in places, where editor diff preview is not available. Overrides the "Open Diff as Editor Tab" action.
+show.diff.as.frame.description=Always force diff in a separate window frame implementation in places, where editor diff preview is not available. Overrides the "Open Diff as Editor Tab" action.
show.diff.preview.as.editor.tab.with.single.click=false
show.diff.preview.as.editor.tab.with.single.click.description=If enabled, and editor diff preview is used for Local Changes and Shelf, then \
it is opened with single click.
@@ -1278,21 +1310,10 @@ blank.diff.history.max.memory.description=Amount of memory (approximate, order-o
diff.external.tool.file.limit=20
diff.external.tool.file.limit.description=Limit the opening of too many files in an external diff tool simultaneously
diff.use.aggressive.text.file.detection=false
-diff.use.aggressive.text.file.detection.description=Ignore IDE 'File Types' setting when trying to show files in a diff viewer.
+diff.use.aggressive.text.file.detection.description=Ignore the IDE 'File Types' setting when trying to show files in a diff viewer.
diff.unified.enable.imaginary.editor=true
diff.unified.enable.imaginary.editor.description=Enable syntax highlighting in 'Unified Diff Viewer'
-jupyter.diff.viewer=true
-jupyter.diff.viewer.output=false
-jupyter.diff.viewer.output.description=Show outputs in the Jupyter diff viewer
-jupyter.diff.viewer.description=Implementation of diff tool for Jupyter Notebooks, which instead of diff for JSON shows a diff for notebook representations
-jupyter.merge.viewer=false
-jupyter.merge.viewer.description=Implementation of merge tool for Jupyter Notebooks, which helps to deal with conflicts during git activities
-jupyter.llm.autopilot=true
-jupyter.llm.autopilot.description=Enable Autopilot for Jupyter Notebooks
-dataspell.interpreter.widget=true
-dataspell.interpreter.widget.description=Interpreter widget for lightweight interpreter management
-
llm.welcome.guide=true
llm.welcome.guide.description=Enables Welcome Guide feature
@@ -1322,15 +1343,6 @@ ide.message.dialogs.as.swing.alert.show.title.bar.description=Show platform wind
windows.jumplist=true
windows.jumplist.description=Enables JumpLists on Windows
-# via ExternalSystemApiUtil.isInProcessMode
-# suppress inspection "UnusedProperty"
-GRADLE.system.in.process=true
-GRADLE.system.in.process.description=Whether IDEA should use 'in-process' mode for interaction with gradle api
-
-# via ExternalSystemApiUtil.isInProcessMode
-# suppress inspection "UnusedProperty"
-MAVEN.system.in.process=true
-MAVEN.system.in.process.description=Whether IDEA should use 'in-process' mode for interaction with maven api
vcs.ignorefile.generation=false
vcs.ignorefile.generation.description=Whether native VCS ignored files (e.g., .gitignore) are generated on first project open \
@@ -1345,7 +1357,7 @@ ide.notification.visible.count=2
ide.notification.visible.count.description=Max number of visible notifications
ide.open.file.in.temp.project.dir=true
-ide.open.file.in.temp.project.dir.description=Enable file opening in temp project directory
+ide.open.file.in.temp.project.dir.description=Enable file opening in the temp project directory
embed.scene.builder=true
embed.scene.builder.description=Show SceneBuilder embedded into IntelliJ IDEA frame
@@ -1357,10 +1369,10 @@ ide.scratch.buffers=5
ide.scratch.buffers.description=Number of scratch buffer files
project.tree.structure.show.url=true
-project.tree.structure.show.url.description=Show URL in the ProjectView nodes for project and libraries
+project.tree.structure.show.url.description=Show URL in the Project View nodes for a project and libraries
ide.show.progress.without.status.bar=true
-ide.show.progress.without.status.bar.description=Disables transparent progress indicator when the status bar is switched off
+ide.show.progress.without.status.bar.description=Disables the transparent progress indicator when the status bar is switched off
editor.injected.highlighting.enabled=true
editor.injected.highlighting.enabled.description=Disables injected fragments highlighting (requires project reopening)
@@ -1374,7 +1386,7 @@ run.processes.with.pty=FALSE
run.processes.with.pty.description=Allocate pseudo-terminal for running and debugging programs. \
This affects the result of isatty() native call, which is usually consulted by various command-line tools \
to check if they can operate in an interactive mode.\n\
- Note that not all IDEs check this option and support PTY at all.\
+ Note that not all IDEs check this option and support PTY completely.\
Currently, this option is used in CLion and PyCharm.
run.processes.using.pty.helper.on.mac.intel=true
@@ -1384,7 +1396,7 @@ terminal.use.conpty.on.windows=true
terminal.use.conpty.on.windows.description=Use ConPTY API to run processes with PTY on Windows
run.processes.with.redirectedErrorStream=false
-run.processes.with.redirectedErrorStream.description=Merge processes standard streams (error and output). \
+run.processes.with.redirectedErrorStream.description=Merge standard streams of processes (error and output). \
This makes it easier to correlate error messages with the corresponding output. \
However, as a result, it won't be possible to distinguish between stdout and stderr. \
This option is supported only for certain run/debug configurations. \
@@ -1411,9 +1423,9 @@ allow.dialog.based.popups.description=Allows using a JDialog as popup toplevel
popup.resize.zone.macos=8
popup.resize.zone.macos.description=The size of mouse resize zone on macOS
popup.resize.zone.windows=4
-popup.resize.zone.windows.description=The size of mouse resize zone on Windows
+popup.resize.zone.windows.description=The size of the mouse resize zone on Windows
popup.resize.zone.linux=4
-popup.resize.zone.linux.description=The size of mouse resize zone on Linux
+popup.resize.zone.linux.description=The size of the mouse resize zone on Linux
popup.fix.ide.frame.owner=false
popup.fix.ide.frame.owner.description=Use a correct owner for IdeFrame, but can break some popups
@@ -1432,10 +1444,10 @@ enable.rename.options.inplace=true
enable.rename.options.inplace.description=Show search in comments/text occurrences as inlays in the editor
ide.event.queue.dispatch.threshold=10
-ide.event.queue.dispatch.threshold.description=A positive number of milliseconds starts to measure time of event processing.
+ide.event.queue.dispatch.threshold.description=A positive number of milliseconds starts to measure the time of event processing.
ide.settings.keymap.input.method.enabled=false
-ide.settings.keymap.input.method.enabled.description=Use input method instead of simple key event to enter shortcuts.
+ide.settings.keymap.input.method.enabled.description=Use an input method instead of a simple key event to enter shortcuts.
ide.settings.configurable.loading.threshold=0
ide.settings.configurable.loading.threshold.description=A positive number of milliseconds starts debugging of Settings loading.
@@ -1444,10 +1456,15 @@ ide.settings.replace.group.with.single.configurable=true
ide.settings.replace.group.with.single.configurable.description=Used to generate a more compact tree for Settings/Preferences.
ide.titled.border.top=7
+ide.titled.border.top.description=Top inset of titled borders (pixels)
ide.titled.border.left=0
+ide.titled.border.left.description=Left inset of titled borders (pixels)
ide.titled.border.right=0
+ide.titled.border.right.description=Right inset of titled borders (pixels)
ide.titled.border.bottom=10
+ide.titled.border.bottom.description=Bottom inset of titled borders (pixels)
ide.titled.border.indent=20
+ide.titled.border.indent.description=Horizontal indentation for titled border content (pixels)
html.prefer.short.notation.of.boolean.attributes=true
@@ -1529,7 +1546,7 @@ idea.editor.smooth.scrolling.navigation.duration=100
idea.editor.smooth.scrolling.navigation.duration.description=Change the duration of animated scrolling in the editor using Page Up/Page Down keys
idea.latching.scrolling.enabled=true
-idea.latching.scrolling.enabled.description=When enabled, scroll sticks vertically or horizontally when precise scroll is on
+idea.latching.scrolling.enabled.description=When enabled, scroll sticks vertically or horizontally when the precise scroll is on
idea.latching.scrolling.time=200
idea.latching.scrolling.time.description=Maximum event lifetime in event series (ms)
idea.latching.scrolling.angle=45.0
@@ -1583,13 +1600,13 @@ lcd.contrast.value=0
lcd.contrast.value.description=Set LCD text contrast value from 100 to 250
autocheck.availability.welcome.screen.projects=true
-autocheck.availability.welcome.screen.projects.description=Auto-checks recent projects availability in the Welcome screen
+autocheck.availability.welcome.screen.projects.description=Auto-checks recent projects' availability on the Welcome screen
ide.find.use.search.in.selection.keyboard.shortcut.for.replace=false
ide.find.use.search.in.selection.keyboard.shortcut.for.replace.description=Always use the keyboard shortcut for Search in Selection instead of the Replace keyboard shortcut when in Replace mode
ide.find.word.based.preserve.case=true
ide.find.word.based.preserve.case.description=New word-based preserve case implementation
ide.find.max.rows=3
-ide.find.max.rows.description=Max rows to be shown in search and replace fields in 'Find in Files' / 'Replace in Files' popup and Find / Replace in editor
+ide.find.max.rows.description=Max rows to be shown in search and replace fields in the 'Find in Files' / 'Replace in Files' popup and Find / Replace in the editor
ide.find.result.count.warning.limit=1000
ide.find.result.count.warning.limit.restartRequired=true
ide.find.result.count.warning.limit.description=The limit at which the "Too many results found" warning is shown
@@ -1604,15 +1621,15 @@ ide.find.select.word.at.caret.description=Select and find word at caret when the
ide.find.escape.selected.text.for.regex=true
ide.find.escape.selected.text.for.regex.description=Automatically escapes selected text for Find / Find in Files when the Regex option in ON
ide.find.use.editor.font=false
-ide.find.use.editor.font.description=Switch search fields in editor and in 'Find in Files' to using editor font
+ide.find.use.editor.font.description=Switch search fields in the editor and in 'Find in Files' to using editor font
ide.remove.help.button.from.dialogs=false
-ide.remove.help.button.from.dialogs.description=Removes help button from dialogs
+ide.remove.help.button.from.dialogs.description=Removes the help button from dialogs
ide.find.in.file.highlight.usages=true
ide.find.in.file.highlight.usages.description=Delegate FindUsagesInFile action to HighlightUsagesInFile
ide.symbol.url.references=false
-ide.symbol.url.references.description=Symbol API based issue references
+ide.symbol.url.references.description=Symbol API-based issue references
ide.rename.preview.dialog=false
-ide.rename.preview.dialog.description=Show preview dialog after Usage View
+ide.rename.preview.dialog.description=Show the preview dialog after Usage View
ide.instant.theme.switch=true
ide.instant.theme.switch.description=Switch theme in the quick switcher instantly, upon selection
ide.instant.theme.switch.delay=500
@@ -1628,12 +1645,13 @@ ide.usages.popup.analyzing.indicator.enable=true
ide.usages.popup.analyzing.indicator.enable.description=Show analyzing indicator in the show usages popup
ide.ruler.guide.allowed.gap=2
-ide.ruler.guide.allowed.gap.description=Gap in pixels considered as an error in the layout
+ide.ruler.guide.allowed.gap.description=Gap in pixels that is considered as an error in the layout
ide.debug.in.title=false
+ide.debug.in.title.description=Show the debug session name in the title bar
ide.tab.tooltip.module=false
-ide.tab.tooltip.module.description=Show module name in editor tab tooltip
+ide.tab.tooltip.module.description=Show the module name in the editor tab tooltip
darcula.fix.native.flickering=false
darcula.fix.native.flickering.description=Fixes white flash on first window appearance
@@ -1642,10 +1660,10 @@ terminal.persistent.tabs=true
terminal.persistent.tabs.description=Save state of terminal tabs between IDE restarts
string.value.max.number.of.strings=132
-string.value.max.number.of.strings.description=Limit for number of different strings in StringValue
+string.value.max.number.of.strings.description=Limit for the number of different strings in StringValue
search.everywhere.pattern.checking=true
-search.everywhere.pattern.checking.description=Avoid searching classes and symbols if pattern contains only one dot
+search.everywhere.pattern.checking.description=Avoid searching for classes and symbols if a pattern contains only one dot
search.everywhere.fuzzy.file.search.enabled=false
search.everywhere.fuzzy.file.search.enabled.description=Enable fuzzy file search
@@ -1657,7 +1675,7 @@ custom.folding.max.lookup.depth=50
custom.folding.max.lookup.depth.description=Max lookup depth for custom folding comments in a PSI tree.
project.structure.add.tools.jar.to.new.jdk=false
-project.structure.add.tools.jar.to.new.jdk.description=Automatically add tools.jar to 'Classpath' when new JDK is created
+project.structure.add.tools.jar.to.new.jdk.description=Automatically add tools.jar to 'Classpath' when a new JDK is created
project.qualified.module.names=true
project.qualified.module.names.description=Organize module nodes accordingly to their qualified (dot-separated) names
@@ -1666,17 +1684,9 @@ external.system.configuration.import.enabled=true
external.system.configuration.import.enabled.description=Enable import of IntelliJ specific settings from an external build system
external.system.auto.import.disabled=false
external.system.auto.import.disabled.description=Disable auto-import build system model to idea
-gradle.auto.auto.jdk.fix.disabled=false
-gradle.auto.auto.jdk.fix.disabled.description=Disable attempt to fix unknown JDK in Gradle configuration on project open
application.deactivation.timeout=1500
-android.gradle.project.startup.activity.disabled=false
-android.gradle.project.startup.activity.disabled.description = Disable gradle post-startup activity for Android projects
-
-android.sdk.local.properties.update.disabled=false
-android.sdk.local.properties.update.disabled.description=Disable update android sdk for local.properties
-
editor.breadcrumbs.highlight.on.hover=false
editor.breadcrumbs.highlight.on.hover.description=Highlight corresponding ranges in the editor on breadcrumb item hover (requires project reopening)
@@ -1727,7 +1737,7 @@ ide.new.run.config.junit.description=New JUnit Configuration UI
select.run.configuration.from.context=true
select.run.configuration.from.context.description=Select run configuration when run from context (gutter icon, popup menu)
run.popup.move.folders.to.top=false
-run.popup.move.folders.to.top.description=Move folders in run popup to top or spread them by type groups
+run.popup.move.folders.to.top.description=Move folders in the run popup to the top or spread them by type groups
run.popup.show.inlines.for.active.configurations=true
run.popup.show.inlines.for.active.configurations.description=Always show inline Run/Debug actions for active configurations
update.run.configuration.actions.from.cache=false
@@ -1735,22 +1745,22 @@ update.run.configuration.actions.from.cache.description=Disable run actions on t
run.configuration.update.timeout=100
run.configuration.update.timeout.description=The timeout in milliseconds for context run configuration actions to determine their applicability
run.configuration.max.name.length=80
-run.configuration.max.name.length.description=When the name of run configuration exceeds the limit, it will be trimmed in UI like 'LongNamedRunCo...'
+run.configuration.max.name.length.description=When the name of the run configuration exceeds the limit, it will be trimmed in UI like 'LongNamedRunCo...'
run.configuration.use.tree.popup.to.add.new=true
run.configuration.use.tree.popup.to.add.new.description=Enables tree structure instead of a plain list in the "Add New Configuration" popup
suggest.all.run.configurations.from.context=false
-suggest.all.run.configurations.from.context.description=With the option enabled, all run configuration from context will be suggested to\
+suggest.all.run.configurations.from.context.description=With the option enabled, all run configurations from context will be suggested to\
run or create in spite of existing run configuration in the context
run.current.file.item.in.run.configurations.combobox=false
-run.current.file.item.in.run.configurations.combobox.description=Adds 'Current File' item to the run configurations list in the combobox on the toolbar
+run.current.file.item.in.run.configurations.combobox.description=Adds the 'Current File' item to the run configurations list in the combobox on the toolbar
allow.macros.for.run.configurations=true
allow.macros.for.run.configurations.description=Enables context-dependent macros like $FileName$ or $SelectedText$ in Run Configurations
lock.run.executor.for.before.run.tasks=false
-lock.run.executor.for.before.run.tasks.description=Locks execution of 'before run' tasks to standard 'run' executor (e.g., prevents debugging)
+lock.run.executor.for.before.run.tasks.description=Locks execution of 'before run' tasks to the standard 'run' executor (e.g., prevents debugging)
focus.follows.mouse.workarounds=false
focus.follows.mouse.workarounds.description=Enables some workarounds for Focus-follows-mouse issues, like IDEA-112015
tests.view.node.expanding.search=true
-tests.view.node.expanding.search.description=Enable node expanding in tests view speed search
+tests.view.node.expanding.search.description=Enable node expanding in the tests view speed search
test.view.hide.progress.bar.on.tests.finished=false
test.view.hide.progress.bar.on.tests.finished.description=Hides the colored progress bar when tests have finished running
@@ -1760,19 +1770,6 @@ ide.enable.edt.lock.load.monitor.description=Enable monitoring of lock usage on
ide.enable.edt.lock.load.fus.monitor=true
ide.enable.edt.lock.load.fus.monitor.description=Enable monitoring of lock usage on EDT for reporting to FUS
-cidr.xcode.derived.data.override=true
-cidr.xcode.derived.data.override.restartRequired=true
-cidr.xcode.derived.data.override.description=Ignore Xcode DerivedData location settings
-appcode.always.use.xcodebuild=false
-appcode.always.use.xcodebuild.description=Force AppCode to always use xcodebuild tool for building
-appcode.simulator.debugger.launch.timeout=180
-appcode.simulator.debugger.launch.timeout.description=How long (in seconds) the debugger session should wait for the simulator to launch the process
-appcode.compiler.invocations.disk.cache=false
-appcode.compiler.invocations.disk.cache.description=Force AppCode to cache the result of compiler invocations on disk
-appcode.resolve.effective.platform=true
-appcode.resolve.effective.platform.restartRequired=true
-appcode.resolve.effective.platform.description=Enable resolving according to effective platform
-
ide.text.effect.new=true
ide.text.effect.new.description=Enables new effect painter for text
highlighting.delay.invalid.psi.info.kill.ms=300
@@ -1805,9 +1802,6 @@ ide.remote.interpreters.rsync.enabled.on.windows=false
ide.remote.interpreters.use.rsync=true
ide.remote.interpreters.use.rsync.description=Use rsync utility to synchronize remote interpreters and local stubs
-javafx.fxml.controller.from.loader=false
-javafx.fxml.controller.from.loader.description=Resolve FXML controllers specified with FXMLLoader.setController()
-
inplace.change.signature=false
inplace.change.signature.description=Enables change signature without a dialog, inplace in the editor
@@ -1892,19 +1886,19 @@ document.async.commit.with.coroutines=true
document.async.commit.with.coroutines.description=Enables the use of coroutines for asynchronous document commit. Non-modal commit will use background write action
document.save.in.background.allowed=true
-document.save.in.background.allowed.description=Allows saving documents in background
+document.save.in.background.allowed.description=Allows saving documents in the background
filetype.reparse.with.coroutines=true
filetype.reparse.with.coroutines.description=Whether to reparse files asynchronously with background write action
file.system.trace.loading=/proc/cpuinfo
-file.system.trace.loading.description=Semicolon-separated paths that should not be loaded into IDE's file system. Their loading stack traces will be logged
+file.system.trace.loading.description=Semicolon-separated paths that should not be loaded into the IDE's file system. Their loading stack traces will be logged
file.system.trace.loading.restartRequired=true
ssr.template.from.selection.builder=false
ssr.template.from.selection.builder.description=Allows building template with live-template-like placeholders when invoking SSR from the editor with selection
ssr.in.editor.problem.highlighting=true
-ssr.in.editor.problem.highlighting.description=Show location of search template problems in the editor
+ssr.in.editor.problem.highlighting.description=Show the location of search template problems in the editor
jdk.regex.soe.workaround=true
jdk.regex.soe.workaround.description=In regular expression pattern replace choice \\n|. with . (and DOT_ALL option) to prevent stack overflow during matching
@@ -1932,18 +1926,13 @@ duplicates.inspection.only.extractable=false
duplicates.inspection.only.extractable.description=Highlight duplicate code only if it can be extracted into a method
duplicates.index.file.maxSize=102400
-duplicates.index.file.maxSize.description=Max size of a file indexed by duplicates analysis
+duplicates.index.file.maxSize.description=Max size of a file indexed by duplicate analysis
duplicates.index.file.test.enable=false
duplicates.index.file.test.enable.description=Enable duplicate index in test files
-ide.jvm.run.marker=false
-ide.jvm.run.marker.description=Enable JVM-lang based run line markers
ide.create.field.enable.shortening=true
ide.create.field.enable.shortening.description=Always enable shortening of FQNs in templates in Create Field fixes
-ide.jvm.command.line.clear.all.environments=true
-ide.jvm.command.line.clear.all.environments.description=Clear all environments when running a JVM command line application
-
editor.adjust.right.margin=false
editor.adjust.right.margin.description=Whether the right margin position for each visual line is shifted according to folded regions' and \
inlays' widths on that line
@@ -1957,9 +1946,9 @@ editor.text.fractional.metrics.description=Do not round glyph advances to intege
editor.text.xcode.vertical.spacing=false
editor.text.xcode.vertical.spacing.description=Enable the same vertical spacing as Xcode editor uses
editor.text.vertical.spacing.correct.rounding=false
-editor.text.vertical.spacing.correct.rounding.description=Make line spacing closer to that in other applications, when scaling factor is set
+editor.text.vertical.spacing.correct.rounding.description=Make line spacing closer to that in other applications when the scaling factor is set
editor.disable.drag.with.right.button=false
-editor.disable.drag.with.right.button.description=Disable any mouse drag processing in the editor with right mouse button pressed
+editor.disable.drag.with.right.button.description=Disable any mouse drag processing in the editor with the right mouse button pressed
editor.drag.sensitivity=5
editor.drag.sensitivity.description=How far away must the mouse be moved for the move to be interpreted as a drag
@@ -1967,30 +1956,30 @@ idea.invalidate.caches.invalidates.vfs=false
idea.invalidate.caches.invalidates.vfs.description=Invalidate caches tries to invalidate everything, including Local History
editor.keep.completion.hints.longer=false
-editor.keep.completion.hints.longer.description=Don't remove hints for parameter names added on method call completion until caret moves \
+editor.keep.completion.hints.longer.description=Don't remove hints for parameter names added on method call completion until the caret moves \
to another line
editor.completion.hints.per.call.limit=256
editor.completion.hints.per.call.limit.description=Limits the number or parameter placeholders/hints, populated on method call completion. \
Further parameters appear on Tab presses.
editor.completion.hints.virtual.comma=true
-editor.completion.hints.virtual.comma.description=Don't insert commas between completion hints for parameters, display comma as part of hint
+editor.completion.hints.virtual.comma.description=Don't insert commas between completion hints for parameters, display comma as part of the hint
editor.codeVision.new=true
editor.codeVision.new.description=New code vision hints (inlays)
editor.codeVision.more.inlay=false
-editor.codeVision.more.inlay.description=Show `More\u2026` hint on hover over code vision entities
+editor.codeVision.more.inlay.description=Show the `More \u2026` hint on hover over code vision entities
folding.signature.validation=false
folding.signature.validation.description=Validate that signature for a PSI element, for which a folding region is generated, can be used to \
find exactly the same PSI element
try.starting.dumb.mode.where.many.files.changed=true
-try.starting.dumb.mode.where.many.files.changed.description=Enables automatic start of dumb mode for all opened projects once more than certain number of files is changed
+try.starting.dumb.mode.where.many.files.changed.description=Enables automatic start of dumb mode for all opened projects once more than a certain number of files is changed
editor.highlight.foldings=true
editor.highlight.foldings.description=Highlight folded regions containing certain highlighters (e.g., for search results)
ide.hide.real.module.name=false
-ide.hide.real.module.name.description=Removes real module name from Project View in square brackets
+ide.hide.real.module.name.description=Removes the real module name from the Project View in square brackets
ide.show.folder.name.instead.of.module.name=false
ide.show.folder.name.instead.of.module.name.description=Use module folder name instead of module name in renderers
@@ -2001,27 +1990,24 @@ skip.move.resize.events.restartRequired=true
skip.move.resize.events.description=Skip move/resize events from list/table/tree renderers during painting
ide.content.manager.listeners.order.fix=true
-ide.content.manager.listeners.order.fix.description=Fixes historically reversed order of ContentManager listeners.
+ide.content.manager.listeners.order.fix.description=Fixes historically reversed the order of ContentManager listeners.
run.anything.use.pty=true
run.anything.use.pty.description=Determines if a PTY should be used for executing commands within the 'Run Anything' feature.
ast.loading.filter=false
-ast.loading.filter.description=Enables the mechanism that disables AST loading in particular thread
+ast.loading.filter.description=Enables the mechanism that disables AST loading in a particular thread
marketplace.certificate.signature.check=true
marketplace.certificate.signature.check.description=Verify signatures for plugins downloaded from plugins.jetbrains.com
custom-repository.certificate.signature.check=false
custom-repository.certificate.signature.check.description=Verify signatures for plugins downloaded from custom plugin repositories and installed from disk
-batch.inspections.ignored.jvm.languages=Groovy,Scala
-batch.inspections.ignored.jvm.languages.description=Disable UAST processing for JVM languages; a comma separated list of UAST capable language ids.
-
batch.inspections.startup.activities.timeout=180
batch.inspections.startup.activities.timeout.description=Timeout of processing all startup activities in a batch inspections run (in minutes)
ide.project.view.bookmarks.icon.before=false
-ide.project.view.bookmarks.icon.before.description=Shows bookmark icon before the Project View node icon
+ide.project.view.bookmarks.icon.before.description=Shows the bookmark icon before the Project View node icon
ide.project.view.bookmarks.icon.hide=true
ide.project.view.bookmarks.icon.hide.description=Hides bookmark icon for bookmarked nodes in the Project View
@@ -2039,7 +2025,7 @@ ide.project.view.persist.cached.presentation=true
ide.project.view.persist.cached.presentation.description=Save and restore Project View presentation to quickly show nodes after restart before they're actually loaded
ide.linux.enter.on.dialog.triggers.focused.button=true
-ide.linux.enter.on.dialog.triggers.focused.button.description=IDEA-125537 Pushing Enter in dialog windows chooses default focus instead of manually selected one on Linux
+ide.linux.enter.on.dialog.triggers.focused.button.description=IDEA-125537 Pushing Enter in dialog windows chooses the default focus instead of the manually selected one on Linux
ide.linux.hide.native.title=true
ide.linux.hide.native.title.description=Enables hiding native title feature for Linux
@@ -2071,18 +2057,18 @@ ide.edt.update.context.only.on.key.pressed.event=false
ide.edt.update.context.only.on.key.pressed.event.description=Update context in IdeKeyEventDispatcher#updateCurrentContext only on key press
new.search.everywhere.font.size.delta=0
-new.search.everywhere.font.size.delta.description=Sets how much font size should be increased in Search Everywhere popup text field
+new.search.everywhere.font.size.delta.description=Sets how much font size should be increased in the Search Everywhere popup text field
new.search.everywhere.use.editor.font=false
-new.search.everywhere.use.editor.font.description=Switch Search Everywhere dialog to using editor font
+new.search.everywhere.use.editor.font.description=Switch the Search Everywhere dialog to using editor font
search.everywhere.disable.history.for.all=false
-search.everywhere.disable.history.for.all.description=Disable saving previous search string for All tab
+search.everywhere.disable.history.for.all.description=Disable saving the previous search string for the All tab
search.everywhere.show.weights=false
-search.everywhere.show.weights.description=Show item weights in description bar
+search.everywhere.show.weights.description=Show item weights in the description bar
search.everywhere.detect.slow.contributors=true
search.everywhere.detect.slow.contributors.description=Enables logging info about slow SE contributors
toolwindow.active.tab.use.contrast.background=false
-toolwindow.active.tab.use.contrast.background.description=When enabled contrast color is used for selected tab background in tool windows
+toolwindow.active.tab.use.contrast.background.description=When enabled, contrast color is used for the selected tab background in tool windows
toolwindow.active.tab.contrast.background.color=808080
toolwindow.active.tab.contrast.background.color.description=Contrast color for the selected tab background in tool windows
toolwindow.disable.overlay.by.double.key=false
@@ -2091,20 +2077,15 @@ toolwindow.open.tab.in.editor=false
toolwindow.open.tab.in.editor.description=Enables "Open in Editor Tab" action
linux.kernel.variables.validate.every.time=true
-linux.kernel.variables.validate.every.time.description=Check that kernel variables required to run profiler are set before every profiler start
+linux.kernel.variables.validate.every.time.description=Check that kernel variables required to run the profiler are set before every profiler start
-idea.profiler.jvm.dtrace=false
-idea.profiler.jvm.dtrace.description=Enable JVM DTrace based profiler on macOS
-idea.profiler.jfr.max.events.of.type.per.chunk=100
-idea.profiler.jfr.max.events.of.type.per.chunk.description=Maximum number of events of a particular type (i.e. "JVM Information") \
- per chunk to be shown in the Events tab
idea.profiler.hprof.autoload.snapshot.size=1000
idea.profiler.hprof.autoload.snapshot.size.description=Enable snapshot analysis on-the-fly when hprof snapshot is less than the specified value in MB
idea.profiler.hprof.show.objectAddress=false
idea.profiler.hprof.show.objectAddress.description=Whether to attach object snapshot's address in UI: tables, trees, etc.
idea.profiler.hprof.show.objectAddress.restartRequired=true
idea.profiler.hprof.show.duplicated.strings=true
-idea.profiler.hprof.show.duplicated.strings.description=Show 'Duplicated Strings' tab in the Profiler Tool Window
+idea.profiler.hprof.show.duplicated.strings.description=Show the 'Duplicated Strings' tab in the Profiler Tool Window
idea.profiler.hprof.show.duplicated.strings.restartRequired=false
idea.profiler.kotlin.inline=true
idea.profiler.kotlin.inline.description=Enable Kotlin inline functions support
@@ -2131,7 +2112,7 @@ idea.profiler.allocations.in.bytes=false
idea.profiler.allocations.in.bytes.description=Show allocations in bytes (without conversion to kB/MB/GB)
recent.locations.lines.before.and.after=2
-recent.locations.lines.before.and.after.description=Defines the number of lines that are shown before and after recent location.
+recent.locations.lines.before.and.after.description=Defines the number of lines that are shown before and after a recent location.
clipboard.history.max.items=100
clipboard.history.max.items.description=Maximum number of items in clipboard history
@@ -2142,10 +2123,10 @@ plugins.show.implementation.details=false
plugins.show.implementation.details.description=Show plugins which are marked as implementation-detail
error.navigation.show.tooltip=true
-error.navigation.show.tooltip.description=Show error description after navigating to next/previous error
+error.navigation.show.tooltip.description=Show error description after navigating to the next / previous error
copy.tbx.reference.enabled=false
-copy.tbx.reference.enabled.description=Enable "Copy TBX Reference URL" action
+copy.tbx.reference.enabled.description=Enable the "Copy TBX Reference URL" action
ide.project.frame.as.splash=false
ide.project.frame.as.splash.description=Whether to replace the splash screen with a project frame
@@ -2153,16 +2134,16 @@ ide.project.frame.as.splash.description=Whether to replace the splash screen wit
ide.project.frame.screen.bounds.tolerance=10
ide.project.frame.screen.bounds.tolerance.description=How far (in pixels) beyond the screen the IDE frame is allowed to extend when restoring its previously saved bounds
ide.project.frame.auto.fix.size.windows=true
-ide.project.frame.auto.fix.size.windows.description=Automatically fix the frame size and location on Windows in case it becomes very small
+ide.project.frame.auto.fix.size.windows.description=Automatically fix the frame size and location on Windows in case it becomes tiny
ide.project.loading.show.last.state=false
-ide.project.loading.show.last.state.description=Show last state screenshot instead of project loading progress
+ide.project.loading.show.last.state.description=Show the last state screenshot instead of project loading progress
contrast.scrollbars.alpha.level=-1
-contrast.scrollbars.alpha.level.description=Alpha level for contrast scrollbars (accessibility option). If alpha<0 default value will be used
+contrast.scrollbars.alpha.level.description=Alpha level for contrast scrollbars (accessibility option). If alpha < 0 default value will be used
ide.background.post.startup.activity.delay=5000
-ide.background.post.startup.activity.delay.description=Delay in milliseconds between the end of normal IDE startup and the start of executing background post-startup activities
+ide.background.post.startup.activity.delay.description=Delay in milliseconds between the end of a normal IDE startup and the start of executing background post-startup activities
ide.plugins.allow.unload=true
ide.plugins.allow.unload.description=Allow loading and unloading plugins without a restart
@@ -2173,7 +2154,7 @@ ide.plugins.snapshot.on.unload.fail.description=Capture memory snapshot when unl
ide.plugins.analyze.snapshot=true
ide.plugins.analyze.snapshot.description=Automatically analyze snapshots produced when plugin unload fails
ide.plugins.unload.timeout=5000
-ide.plugins.unload.timeout.description=Time to wait for classloader to be garbage-collected on plugin unloading (ms)
+ide.plugins.unload.timeout.description=Time to wait for a classloader to be garbage-collected on plugin unloading (ms)
ide.plugins.load.automatically=false
ide.plugins.load.automatically.description=Automatically load required plugins when possible
ide.plugins.suggest.install.optional.dependencies=false
@@ -2192,7 +2173,7 @@ ide.activity.tracking.enable.debug=false
ide.activity.tracking.enable.debug.description=Record debug information for `Observation.awaitConfiguration`
light.edit.file.open.enabled=true
-light.edit.file.open.enabled.description=Open files in off-project editor instead of creating a temporary project
+light.edit.file.open.enabled.description=Open files in the off-project editor instead of creating a temporary project
system.file.type.associations.enabled=true
system.file.type.associations.enabled.description=Enable Tools|Associate File Types with... action
@@ -2232,7 +2213,7 @@ ide.animate.toolwindows.description=Use slide animation when showing/hiding unpi
editor.show.notification.after.reformat=true
editor.show.notification.after.reformat.description=Show hint notification after Reformat Code action
editor.show.notification.after.optimize.imports=true
-editor.show.notification.after.optimize.imports.description=Show hint notification after Optimize Imports action
+editor.show.notification.after.optimize.imports.description=Show hint notification after the Optimize Imports action
editor.scope.based.formatting.ui=false
editor.scope.based.formatting.ui.description=Enables UI for deprecated scope-based formatting
@@ -2245,17 +2226,17 @@ ide.widget.toolbar.description=Widget toolbar UI
ide.widget.toolbar.logging=false
ide.widget.toolbar.logging.description=Log startup info for the run widget
ide.widget.toolbar.experimentalUpdating=true
-ide.widget.toolbar.experimentalUpdating.description=If enabled, run widget startup will be faster, but update will become unstable
+ide.widget.toolbar.experimentalUpdating.description=If enabled, run widget startup will be faster, but the update will become unstable
ide.widget.toolbar.is.settings.available=false
ide.widget.toolbar.is.settings.available.description=If enabled, run widget settings will be shown in the preferences
ide.lookup.shrink=false
-ide.lookup.shrink.description=Reduce completion popup width automatically while scrolling, if visible items all fit
+ide.lookup.shrink.description=Reduce completion popup width automatically while scrolling if visible items all fit
ide.undo.fallback=false
-ide.undo.fallback.description=Fallback on local stack if global command is blocking
+ide.undo.fallback.description=Fallback on the local stack if the global command is blocking
ide.undo.transparent.caret.movement=false
-ide.undo.transparent.caret.movement.description=When enabled caret movement will no longer be a separate undo step
+ide.undo.transparent.caret.movement.description=When enabled, caret movement will no longer be a separate undo step
ide.undo.frontend.if.possible=true
ide.undo.frontend.if.possible.restartRequired=true
ide.undo.frontend.if.possible.description=Feature flag for IJPL-185748 (WIP!). If enabled, undo/redo is performed on the frontend side if possible.
@@ -2270,7 +2251,7 @@ ide.experimental.ui.inter.font.description=Use Inter font for context menus in E
ide.experimental.ui.animations=true
ide.experimental.ui.animations.description=Animate resizing of editor splits
ide.experimental.ui.meetNewUi=true
-ide.experimental.ui.meetNewUi.description=Turns on Meet New UI tool window
+ide.experimental.ui.meetNewUi.description=Turns on the Meet New UI tool window
ide.experimental.ui.meetNewUi.restartRequired=true
ide.experimental.ui.tree.selection=true
ide.experimental.ui.tree.selection.description=New UI for selected tree nodes
@@ -2283,7 +2264,7 @@ ide.experimental.ui.editor.tabs.scrollbar.description=Add scrollbar to editor ta
ide.web.preview.enabled=true
ide.web.preview.enabled.restartRequired=true
-ide.web.preview.enabled.description=Ability to preview HTML content directly in IDE
+ide.web.preview.enabled.description=Ability to preview HTML content directly in the IDE
ide.color.scheme.for.descriptor.preview.enabled=false
ide.color.scheme.for.descriptor.preview.enabled.description=Use IDE colors for the editor preview in toolwindows
@@ -2330,7 +2311,7 @@ ide.slow.operations.assertion.vcs.line-status-tracker-provider=false
ide.slow.operations.assertion.vcs.line-status-tracker-provider.description=An `ide.slow.operations.assertion` sub-flag for LineStatusTrackerManager
ide.show.open.button.in.tip.dialog=false
-ide.show.open.button.in.tip.dialog.description=Shows Open button in Tip of the Day dialog to load a custom HTML file to preview
+ide.show.open.button.in.tip.dialog.description=Shows the Open button in the Tip of the Day dialog to load a custom HTML file to preview
ide.save.missing.jb.colors=false
ide.save.missing.jb.colors.description=A missing named color will be added to UIDefaults with !!! suffix
@@ -2354,13 +2335,13 @@ ide.disable.intellij.project.analytics=false
ide.disable.intellij.project.analytics.description=Option to disable fine-tuned analytics for IntelliJ project
ide.text.editor.with.preview.show.floating.toolbar=true
-ide.text.editor.with.preview.show.floating.toolbar.description=Show floating toolbar with layouts instead of permanent toolbar
+ide.text.editor.with.preview.show.floating.toolbar.description=Show a floating toolbar with layouts instead of a permanent toolbar
ide.project.icon.size=20
ide.project.icon.size.description=Icon size for project icons
ide.problems.view.empty.status.actions=true
-ide.problems.view.empty.status.actions.description=Project Errors in Problems View shows predefined actions in empty text
+ide.problems.view.empty.status.actions.description=Project Errors in the Problems View show predefined actions in empty text
ide.problems.view.notify.later=true
ide.problems.view.notify.later.description=Problems View notifies listeners later on EDT
@@ -2372,9 +2353,6 @@ ide.problems.view.provider.class.filter.description=Specify qualified class name
ide.presentation.assistant.theme.enabled=false
ide.presentation.assistant.theme.enabled.description=If true, Presentation Assistant popups will use alternative colors from their color scheme
-eclipse.scan.home.directory=true
-eclipse.scan.home.directory.description=Scan home directory for existing Eclipse projects
-
ide.diagnostics.show.context.menu.invocation.time=false
ide.diagnostics.show.context.menu.invocation.time.description=Show time in ms between mouse event and context menu appearance on screen
@@ -2396,7 +2374,7 @@ editor.text.field.init.on.shown=true
editor.text.field.init.on.shown.description=Initialize EditorTextField when first shown
ide.gtd.show.error=true
-ide.gtd.show.error.description=Whether to show "Cannot find declaration to go" error by "Go To Declaration" action
+ide.gtd.show.error.description=Whether to show the "Cannot find declaration to go" error by "Go To Declaration" action
ide.ui.new.file.chooser=false
ide.ui.new.file.chooser.description=Experimental file chooser implementation (IJPL-338)
@@ -2407,22 +2385,19 @@ ide.editor.tab.selection.animation=false
ide.editor.tab.selection.animation.description=Animate tab underline when switching editor tabs
calculate.gutter.actions.always=false
-calculate.gutter.actions.always.description=Calculate all gutter icons/actions even if Editor|General|Gutter Icons|Show icons is set to "off", to be able to show these actions in Alt-Enter
+calculate.gutter.actions.always.description=Calculate all gutter icons/actions even if Editor|General|Gutter Icons|Show icons is set to "off" to be able to show these actions in Alt-Enter
ide.debug.gutter.area=false
ide.debug.gutter.area.description=Shows gutter areas on hover
ide.file.colors.at.left=false
-ide.file.colors.at.left.description=Paints file colors as stripe at left instead of background
+ide.file.colors.at.left.description=Paints file colors as stripe at left instead of the background
ide.wsl.use_wsl_sync=true
-ide.wsl.use_wsl_sync.description=Use wsl sync engine to copy data between Linux and Windows
+ide.wsl.use_wsl_sync.description=Use the WSL sync engine to copy data between Linux and Windows
ide.heartbeat.delay=60000
-ide.heartbeat.delay.description=Delay before heartbeat thread starts reporting performance data
-
-static.analysis.use.js.configurator=false
-static.analysis.use.js.configurator.description=Whether to autoconfigure java-script project
+ide.heartbeat.delay.description=Delay before the heartbeat thread starts reporting performance data
ide.ui.wrap.colors=true
ide.ui.wrap.colors.description=Wrap UI colors to colors with name
@@ -2443,7 +2418,7 @@ ide.scale.discrete.take.floor=false
ide.scale.discrete.take.floor.description=Use floor from Float when discretion the ide scale value. That would make icon scaling more aligned with font scaling.
ide.popup.align.by.content=true
-ide.popup.align.by.content.description=When popup is shown under a component use vertical alignment not by their border but by their content
+ide.popup.align.by.content.description=When a popup is shown under a component, use vertical alignment not by their border but by their content
ide.mac.stage.manager.support=false
ide.mac.stage.manager.support.description=Support for Stage Manager in macOS Ventura. If enabled, dialogs stop jumping
@@ -2473,7 +2448,7 @@ idea.ignore.disabled.plugins.description=Ignores disabled plugin state
editor.readerMode.vfmt.removeZombies=true
editor.readerMode.vfmt.removeZombies.description=Enables fix for IDEA-313274. It prevents erroneously cached visual formattingfoldings \
- from being loaded into editor.
+ from being loaded into the editor.
editor.readerMode.vfmt.removeZombies.restartRequired=true
ide.speed.search.allow.custom.location=false
@@ -2486,7 +2461,7 @@ ide.colorful.toolbar.gradient.type=[Just Icon|Linear Gradient and Icon|Circular
ide.colorful.toolbar.gradient.type.description=Defines types of main toolbar decorations
ide.colorful.toolbar.gradient.radius.description=Radius of the gradient in Main Toolbar
ide.colorful.toolbar.gradient.saturation=0.85
-ide.colorful.toolbar.gradient.saturation.description=Saturation of the gradient color in Main Toolbar
+ide.colorful.toolbar.gradient.saturation.description=Saturation of the gradient color in the Main Toolbar
ide.colorful.toolbar.force=true
ide.colorful.toolbar.force.description=Show 'Colorful toolbar' even if there is one project open (works only if the feature is enabled). \
Reopen project or restart IDE to apply the setting
diff --git a/platform/util/src/com/intellij/util/EnvironmentUtil.java b/platform/util/src/com/intellij/util/EnvironmentUtil.java
index 59604316f374..1782ffc61bf9 100644
--- a/platform/util/src/com/intellij/util/EnvironmentUtil.java
+++ b/platform/util/src/com/intellij/util/EnvironmentUtil.java
@@ -112,7 +112,8 @@ public final class EnvironmentUtil {
ourEnvGetter.set(loader);
}
- private static Map getSystemEnv() {
+ @ApiStatus.Internal
+ public static Map getSystemEnv() {
if (OS.CURRENT == OS.Windows) {
return Collections.unmodifiableMap(CollectionFactory.createCaseInsensitiveStringMap(System.getenv()));
}
diff --git a/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderTest.java b/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderTest.java
index f8151cbe8f2c..d42dc59679dc 100644
--- a/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderTest.java
+++ b/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderTest.java
@@ -5,6 +5,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.ijent.community.buildConstants.IjentBuildScriptsConstantsKt;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.rules.TempDirectory;
+import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.lang.ClassPath;
@@ -121,9 +122,7 @@ public class UrlClassLoaderTest {
}));
}
- for (Future> future : futures) {
- future.get();
- }
+ ConcurrencyUtil.getAll(futures);
}
}
finally {
diff --git a/platform/util/ui/src/com/intellij/util/ui/WaylandUtil.kt b/platform/util/ui/src/com/intellij/util/ui/WaylandUtil.kt
new file mode 100644
index 000000000000..58859a898ad2
--- /dev/null
+++ b/platform/util/ui/src/com/intellij/util/ui/WaylandUtil.kt
@@ -0,0 +1,91 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.util.ui
+
+import com.intellij.openapi.diagnostic.debug
+import com.intellij.openapi.diagnostic.fileLogger
+import com.intellij.ui.ComponentUtil
+import org.jetbrains.annotations.ApiStatus
+import java.awt.Component
+import java.awt.Rectangle
+import java.awt.Window
+import javax.swing.SwingUtilities
+
+@ApiStatus.Internal
+fun moveToFitChildPopupX(childBounds: Rectangle, parent: Component) {
+ if (!parent.isShowing) {
+ LOG.warn("Impossible to fit the child popup to the main window because the parent is not showing: $parent")
+ return
+ }
+
+ val childLocation = childBounds.location
+ LOG.debug { "The initial child bounds are $childBounds" }
+ SwingUtilities.convertPointToScreen(childLocation, parent)
+ LOG.debug { "The initial child location relative to the screen is $childLocation" }
+
+ val topLevelWindow = ComponentUtil.findUltimateParent(parent)
+ if (topLevelWindow !is Window) return
+ val topLevelBounds = Rectangle(topLevelWindow.locationOnScreen, topLevelWindow.size)
+ LOG.debug { "The top level bounds are $topLevelBounds" }
+
+ val parentLocation = parent.location
+ LOG.debug { "The relative parent location is $parentLocation" }
+ SwingUtilities.convertPointToScreen(parentLocation, parent.parent)
+ val parentBounds = Rectangle(parentLocation, parent.size)
+ LOG.debug { "The screen parent bounds are $parentBounds" }
+
+ childLocation.x = fitValue(
+ location = childLocation.x,
+ width = childBounds.width,
+ start1 = topLevelBounds.x,
+ end1 = parentBounds.x,
+ start2 = parentBounds.x + parentBounds.width,
+ end2 = topLevelBounds.x + topLevelBounds.width,
+ preferLess = childLocation.x < parentBounds.x + parentBounds.width / 2,
+ )
+
+ SwingUtilities.convertPointFromScreen(childLocation, parent)
+ childBounds.location = childLocation
+ LOG.debug { "The final result is $childBounds" }
+}
+
+private fun fitValue(location: Int, width: Int, start1: Int, end1: Int, start2: Int, end2: Int, preferLess: Boolean): Int {
+ LOG.debug { "The available intervals are $start1..$end1 and $start2..$end2, the popup size is $width" }
+ if (location >= start1 && location + width < end1) {
+ LOG.debug { "The initial location already fits within the first interval" }
+ return location
+ }
+ if (location >= start2 && location + width < end2) {
+ LOG.debug { "The initial location already fits within the second interval" }
+ return location
+ }
+ val space1 = end1 - start1
+ val space2 = end2 - start2
+ if (space1 >= width && space2 >= width) {
+ LOG.debug { "We have enough space on both sides, preferring the ${if (preferLess) "first" else "second"}" }
+ return if (preferLess) end1 - width else start2
+ }
+ else if (space1 >= width) {
+ val result = end1 - width
+ LOG.debug { "We have enough space the first side: $space1 >= $width, the result is $end1-$width=$result" }
+ return result
+ }
+ else if (space2 >= width) {
+ LOG.debug { "We have enough space the first side: $space2 >= $width, the result is $start2" }
+ return start2
+ }
+ else if (space1 > 0 && space1 > space2) {
+ LOG.debug { "We have more space the first side: $space1 > $space2, the result is $start1" }
+ return start1
+ }
+ else if (space2 > 0) {
+ val result = end2 - width
+ LOG.debug { "We have more space the second side: $space1 <= $space2, the result is $end2-$width=$result" }
+ return result
+ }
+ else {
+ LOG.debug { "We don't have any space at all, falling back to the initial value" }
+ return location
+ }
+}
+
+private val LOG = fileLogger()
diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt
index 3cf7f15b4611..13c0e7f122c1 100644
--- a/platform/vcs-impl/api-dump.txt
+++ b/platform/vcs-impl/api-dump.txt
@@ -755,22 +755,6 @@ a:com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor$Wrapper
f:com.intellij.openapi.vcs.changes.ChangesPreprocess
- ():V
- s:preprocessChangesRemoveDeletedForDuplicateMoved(java.util.List):java.util.List
-a:com.intellij.openapi.vcs.changes.ChangesTreeEditorDiffPreview
-- com.intellij.openapi.vcs.changes.EditorTabDiffPreview
-- (com.intellij.openapi.vcs.changes.ui.ChangesTree,javax.swing.JComponent):V
-- b:(com.intellij.openapi.vcs.changes.ui.ChangesTree,javax.swing.JComponent,I,kotlin.jvm.internal.DefaultConstructorMarker):V
-- pf:getTree():com.intellij.openapi.vcs.changes.ui.ChangesTree
-- handleDoubleClick(java.awt.event.MouseEvent):Z
-- handleEnterKey():Z
-- p:handleEscapeKey():V
-- p:handleNextDiffShortcut():V
-- p:handleSingleClick():V
-- p:isOpenPreviewWithNextDiffShortcut():Z
-- p:isOpenPreviewWithSingleClick():Z
-- p:isOpenPreviewWithSingleClickEnabled():Z
-- p:isPreviewOnDoubleClick():Z
-- p:isPreviewOnEnter():Z
-- p:returnFocusToTree():V
com.intellij.openapi.vcs.changes.ChangesViewEx
- com.intellij.openapi.vcs.changes.ChangesViewI
- a:getCommitWorkflowHandler():com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler
@@ -781,13 +765,12 @@ com.intellij.openapi.vcs.changes.ChangesViewI
- a:scheduleRefresh():V
- a:selectChanges(java.util.List):V
- a:selectFile(com.intellij.openapi.vfs.VirtualFile):V
-- a:setBusy(Z):V
- a:setGrouping(java.lang.String):V
- a:updateProgressComponent(java.util.List):V
c:com.intellij.openapi.vcs.changes.ChangesViewManager
- com.intellij.openapi.Disposable
- com.intellij.openapi.vcs.changes.ChangesViewEx
-- (com.intellij.openapi.project.Project):V
+- (com.intellij.openapi.project.Project,kotlinx.coroutines.CoroutineScope):V
- closeEditorPreview(Z):V
- s:createTextStatusFactory(java.lang.String,Z):com.intellij.openapi.util.Factory
- diffPreviewChanged(Z):V
@@ -802,28 +785,8 @@ c:com.intellij.openapi.vcs.changes.ChangesViewManager
- scheduleRefresh(java.lang.Runnable):V
- selectChanges(java.util.List):V
- selectFile(com.intellij.openapi.vfs.VirtualFile):V
-- setBusy(Z):V
- setGrouping(java.lang.String):V
- updateProgressComponent(java.util.List):V
-f:com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel
-- com.intellij.openapi.ui.SimpleToolWindowPanel
-- com.intellij.openapi.Disposable
-- addListener(com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel$Listener,com.intellij.openapi.Disposable):V
-- dispose():V
-- getActions(Z):java.util.List
-- isAllowExcludeFromCommit():Z
-- scheduleRefresh():V
-- selectChanges(java.util.List):V
-- selectFile(com.intellij.openapi.vfs.VirtualFile):V
-- uiDataSnapshot(com.intellij.openapi.actionSystem.DataSink):V
-- updateCommitWorkflow():V
-com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel$Listener
-- java.util.EventListener
-- a:allowExcludeFromCommitChanged():V
-c:com.intellij.openapi.vcs.changes.ChangesViewManager$ContentPreloader
-- com.intellij.openapi.vcs.changes.ui.ChangesViewContentProvider$Preloader
-- (com.intellij.openapi.project.Project):V
-- preloadTabContent(com.intellij.ui.content.Content):V
c:com.intellij.openapi.vcs.changes.ChangesViewManager$ContentProvider
- com.intellij.openapi.vcs.changes.ui.ChangesViewContentProvider
- (com.intellij.openapi.project.Project):V
@@ -2644,7 +2607,7 @@ c:com.intellij.openapi.vcs.changes.ui.TreeHandlerDiffRequestProcessor
- pf:selectChange(com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor$Wrapper):V
- p:showAllChangesForEmptySelection():Z
a:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview
-- com.intellij.openapi.vcs.changes.ChangesTreeEditorDiffPreview
+- com.intellij.openapi.vcs.changes.EditorTabDiffPreview
- sf:Companion:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview$Companion
- (com.intellij.openapi.vcs.changes.ui.ChangesTree,com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler):V
- (com.intellij.openapi.vcs.changes.ui.ChangesTree,javax.swing.JComponent,com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler):V
@@ -2655,7 +2618,19 @@ a:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview
- pf:getEditorTabName(com.intellij.diff.impl.DiffEditorViewer):java.lang.String
- a:getEditorTabName(com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor$Wrapper):java.lang.String
- pf:getHandler():com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler
+- pf:getTree():com.intellij.openapi.vcs.changes.ui.ChangesTree
+- handleDoubleClick(java.awt.event.MouseEvent):Z
+- handleEnterKey():Z
+- p:handleEscapeKey():V
+- p:handleNextDiffShortcut():V
+- p:handleSingleClick():V
- f:hasContent():Z
+- p:isOpenPreviewWithNextDiffShortcut():Z
+- p:isOpenPreviewWithSingleClick():Z
+- p:isOpenPreviewWithSingleClickEnabled():Z
+- p:isPreviewOnDoubleClick():Z
+- p:isPreviewOnEnter():Z
+- p:returnFocusToTree():V
f:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview$Companion
- f:createDefaultViewer(com.intellij.openapi.vcs.changes.ui.ChangesTree,com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler,java.lang.String):com.intellij.diff.impl.DiffEditorViewer
a:com.intellij.openapi.vcs.changes.ui.VcsToolWindowFactory
@@ -3969,24 +3944,37 @@ a:com.intellij.vcs.commit.ChangeListViewCommitPanel
- f:getEditedCommit():com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation
- f:getIncludedChanges():java.util.List
- f:getIncludedUnversionedFiles():java.util.List
-- f:getInclusionModel():com.intellij.openapi.vcs.changes.InclusionModel
- f:select(java.lang.Object):V
- f:selectFirst(java.util.Collection):V
- f:setCompletionContext(java.util.List):V
- f:setEditedCommit(com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation):V
-- f:setInclusionModel(com.intellij.openapi.vcs.changes.InclusionModel):V
+- setInclusionModel(com.intellij.openapi.vcs.changes.InclusionModel):V
f:com.intellij.vcs.commit.ChangesViewCommitPanel
-- com.intellij.vcs.commit.ChangeListViewCommitPanel
+- com.intellij.vcs.commit.NonModalCommitPanel
+- com.intellij.vcs.commit.ChangesViewCommitWorkflowUi
- activate():Z
- deactivate(Z):V
+- dispose():V
+- endExecution():V
+- expand(java.lang.Object):V
+- getCommitProgressUi():com.intellij.vcs.commit.CommitProgressUi
+- getDisplayedChanges():java.util.List
+- getDisplayedUnversionedFiles():java.util.List
+- getEditedCommit():com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation
+- getIncludedChanges():java.util.List
+- getIncludedUnversionedFiles():java.util.List
- isActive():Z
- refreshChangesViewBeforeCommit(kotlin.coroutines.Continuation):java.lang.Object
+- select(java.lang.Object):V
+- selectFirst(java.util.Collection):V
+- setCompletionContext(java.util.List):V
+- setEditedCommit(com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation):V
+- setInclusionModel(com.intellij.openapi.vcs.changes.InclusionModel):V
com.intellij.vcs.commit.ChangesViewCommitWorkflowUi
- com.intellij.vcs.commit.NonModalCommitWorkflowUi
- a:deactivate(Z):V
- a:endExecution():V
- a:expand(java.lang.Object):V
-- a:getInclusionModel():com.intellij.openapi.vcs.changes.InclusionModel
- a:isActive():Z
- a:refreshChangesViewBeforeCommit(kotlin.coroutines.Continuation):java.lang.Object
- a:select(java.lang.Object):V
@@ -4093,6 +4081,7 @@ c:com.intellij.vcs.commit.CommitProgressPanel
- com.intellij.openapi.vcs.changes.InclusionListener
- com.intellij.vcs.commit.CommitProgressUi
- (com.intellij.openapi.project.Project):V
+- (com.intellij.openapi.project.Project,com.intellij.vcs.commit.CommitWorkflowUi,com.intellij.ui.EditorTextComponent):V
- addCommitCheckFailure(com.intellij.vcs.commit.CommitCheckFailure):V
- p:buildErrorText():java.lang.String
- clearCommitCheckFailures():V
diff --git a/platform/vcs-impl/frontend/resources/intellij.platform.vcs.impl.frontend.xml b/platform/vcs-impl/frontend/resources/intellij.platform.vcs.impl.frontend.xml
index 1372026c7620..e45ac31736c8 100644
--- a/platform/vcs-impl/frontend/resources/intellij.platform.vcs.impl.frontend.xml
+++ b/platform/vcs-impl/frontend/resources/intellij.platform.vcs.impl.frontend.xml
@@ -11,7 +11,8 @@
-
+
+
diff --git a/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/ChangesViewSplitComponentProvider.kt b/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/ChangesViewSplitComponentProvider.kt
new file mode 100644
index 000000000000..8872df310abe
--- /dev/null
+++ b/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/ChangesViewSplitComponentProvider.kt
@@ -0,0 +1,41 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.vcs.impl.frontend.changes
+
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.changes.*
+import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
+import com.intellij.ui.split.SplitComponentBinding
+import com.intellij.ui.split.SplitComponentProvider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import javax.swing.JComponent
+
+internal class ChangesViewSplitComponentProvider : SplitComponentProvider {
+ override val binding: SplitComponentBinding = ChangesViewSplitComponentBinding
+
+ override fun createComponent(project: Project, scope: CoroutineScope, modelId: ChangesViewId): JComponent {
+ val tree = LocalChangesListView(project)
+ val panel = CommitChangesViewWithToolbarPanel(tree, scope)
+ panel.id = modelId
+ panel.initPanel(object : CommitChangesViewWithToolbarPanel.ModelProvider {
+ override fun getModelData(): CommitChangesViewWithToolbarPanel.ModelProvider.ModelData {
+ val changeLists = ChangeListsViewModel.getInstance(project).changeLists.value
+ return CommitChangesViewWithToolbarPanel.ModelProvider.ModelData(changeLists.lists,
+ emptyList(),
+ emptyList()) { true }
+ }
+
+ override fun synchronizeInclusion(changeLists: List, unversionedFiles: List) {
+ }
+ })
+
+ scope.launch {
+ ChangeListsViewModel.getInstance(project).changeLists.collect {
+ panel.scheduleRefresh()
+ }
+ }
+
+ return panel
+ }
+}
diff --git a/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/LocalChangesViewContentProvider.kt b/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/LocalChangesViewContentProvider.kt
deleted file mode 100644
index 0f840e9c552a..000000000000
--- a/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/LocalChangesViewContentProvider.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package com.intellij.platform.vcs.impl.frontend.changes
-
-import com.intellij.openapi.components.Service
-import com.intellij.openapi.components.service
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vcs.FilePath
-import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel
-import com.intellij.openapi.vcs.changes.LocalChangeList
-import com.intellij.openapi.vcs.changes.LocalChangesListView
-import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory
-import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder
-import com.intellij.platform.vcs.impl.shared.RdLocalChanges
-import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
-import com.intellij.ui.content.Content
-import com.intellij.util.cancelOnDispose
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import org.jetbrains.annotations.NonNls
-import javax.swing.tree.DefaultTreeModel
-
-internal class LocalChangesViewContentProvider : FrontendChangesViewContentProvider {
- override fun matchesTabName(tabName: @NonNls String): Boolean = tabName == "Local Changes" || tabName == "Commit"
-
- override fun isAvailable(project: Project): Boolean = RdLocalChanges.isEnabled()
-
- override fun initTabContent(project: Project, content: Content) {
- val tree = LocalChangesListView(project)
- val panel = CommitChangesViewWithToolbarPanel(tree, content)
- panel.initPanel(object : CommitChangesViewWithToolbarPanel.ModelProvider {
- override fun getModel(grouping: ChangesGroupingPolicyFactory): CommitChangesViewWithToolbarPanel.ModelProvider.ExtendedTreeModel {
- val changeLists = ChangeListsViewModel.getInstance(project).changeLists.value
- return CommitChangesViewWithToolbarPanel.ModelProvider.ExtendedTreeModel(
- changeLists.lists, emptyList(), buildModel(project, grouping, changeLists)
- )
- }
-
- override fun synchronizeInclusion(changeLists: List, unversionedFiles: List) {
- }
- })
-
- content.component = panel
-
- project.service().cs.launch {
- ChangeListsViewModel.getInstance(project).changeLists.collect {
- panel.scheduleRefresh()
- }
- }.cancelOnDispose(content)
- }
-
- private fun buildModel(project: Project, grouping: ChangesGroupingPolicyFactory, lists: ChangeListsViewModel.ChangeLists): DefaultTreeModel =
- TreeModelBuilder(project, grouping).setChangeLists(lists.lists, false, null).build()
-
- @Service(Service.Level.PROJECT)
- internal class ScopeProvider(val cs: CoroutineScope)
-}
diff --git a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewId.kt b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewId.kt
new file mode 100644
index 000000000000..4afb5759033c
--- /dev/null
+++ b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewId.kt
@@ -0,0 +1,15 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.openapi.vcs.changes
+
+import com.intellij.platform.rpc.Id
+import com.intellij.platform.rpc.UID
+import com.intellij.ui.split.SplitComponentBinding
+import kotlinx.serialization.Serializable
+import org.jetbrains.annotations.ApiStatus
+
+@ApiStatus.Internal
+val ChangesViewSplitComponentBinding: SplitComponentBinding = SplitComponentBinding("ChangesView", ::ChangesViewId)
+
+@Serializable
+@ApiStatus.Internal
+data class ChangesViewId(override val uid: UID) : Id
diff --git a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewPanel.kt b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewPanel.kt
index b9e0cbf3dc3d..2c6e912b0546 100644
--- a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewPanel.kt
+++ b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewPanel.kt
@@ -21,7 +21,7 @@ import kotlin.properties.Delegates.observable
open class ChangesViewPanel(val changesView: ChangesListView) : BorderLayoutPanel() {
val toolbarActionGroup = DefaultActionGroup()
- var isToolbarHorizontal: Boolean by observable(false) { _, oldValue, newValue ->
+ var isToolbarHorizontal: Boolean by observable(true) { _, oldValue, newValue ->
if (oldValue != newValue) {
addToolbar(newValue) // this also removes toolbar from previous parent
}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt
similarity index 58%
rename from platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt
rename to platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt
index 937a325475e8..60e99deb0cd2 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt
+++ b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt
@@ -1,54 +1,41 @@
-// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.vcs.changes
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
-import com.intellij.openapi.project.InitialVfsRefreshService
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.FilePath
import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator
-import com.intellij.openapi.vcs.changes.ui.ChangesBrowserUnversionedLoadingPendingNode
import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory
import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder
+import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
import com.intellij.platform.vcs.impl.shared.changes.TreeModelBuilderEx
import com.intellij.platform.vcs.impl.shared.commit.PartialCommitChangeNodeDecorator
import org.jetbrains.annotations.ApiStatus
import javax.swing.tree.DefaultTreeModel
-// TODO make shared
+private typealias DecoratorProvider = (ChangeNodeDecorator) -> ChangeNodeDecorator
+
@ApiStatus.Internal
object ChangesViewUtil {
- private fun getChangeDecoratorProvider(project: Project, isAllowExcludeFromCommit: () -> Boolean): (ChangeNodeDecorator?) -> PartialCommitChangeNodeDecorator {
- return { baseDecorator: ChangeNodeDecorator? -> PartialCommitChangeNodeDecorator(project, baseDecorator, isAllowExcludeFromCommit) }
- }
+ private fun getChangeDecoratorProvider(project: Project, isAllowExcludeFromCommit: () -> Boolean): DecoratorProvider =
+ { baseDecorator -> PartialCommitChangeNodeDecorator(project, baseDecorator, isAllowExcludeFromCommit) }
fun createTreeModel(
project: Project,
grouping: ChangesGroupingPolicyFactory,
changeLists: List,
unversionedFiles: List,
- showIgnoredFiles: Boolean,
+ ignoredFiles: List,
isAllowExcludeFromCommit: () -> Boolean
): DefaultTreeModel {
- val changeListManager = ChangeListManagerImpl.getInstanceImpl(project)
-
- val shouldShowUntrackedLoading = unversionedFiles.isEmpty() &&
- !project.getService(InitialVfsRefreshService::class.java).isInitialVfsRefreshFinished() &&
- changeListManager.isUnversionedInUpdateMode
- val skipSingleDefaultChangeList = !changeListManager.areChangeListsEnabled()
-
+ val skipSingleDefaultChangeList = !ChangeListsViewModel.getInstance(project).areChangeListsEnabled.value
val treeModelBuilder = TreeModelBuilder(project, grouping)
.setChangeLists(changeLists, skipSingleDefaultChangeList, getChangeDecoratorProvider(project, isAllowExcludeFromCommit))
.also { TreeModelBuilderEx.getInstanceOrNull(project)?.modifyTreeModelBuilder(it) }
.setUnversioned(unversionedFiles)
- if (showIgnoredFiles) {
- val ignoredFilePaths: List = changeListManager.getIgnoredFilePaths()
- treeModelBuilder.setIgnored(ignoredFilePaths)
- }
- if (shouldShowUntrackedLoading) {
- treeModelBuilder.insertSubtreeRoot(ChangesBrowserUnversionedLoadingPendingNode())
- }
+ treeModelBuilder.setIgnored(ignoredFiles)
for (extension in ChangesViewModifier.KEY.getExtensions(project)) {
try {
diff --git a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/CommitChangesViewWithToolbarPanel.kt b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/CommitChangesViewWithToolbarPanel.kt
index 060a63b083ba..4213b1bf9b2a 100644
--- a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/CommitChangesViewWithToolbarPanel.kt
+++ b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/CommitChangesViewWithToolbarPanel.kt
@@ -3,25 +3,19 @@ package com.intellij.openapi.vcs.changes
import com.intellij.ide.CommonActionsManager
import com.intellij.ide.ui.customization.CustomActionsSchema
-import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.UI
-import com.intellij.openapi.components.Service
-import com.intellij.openapi.components.service
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.progress.checkCanceled
import com.intellij.openapi.project.Project
-import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vcs.FilePath
import com.intellij.openapi.vcs.changes.ChangesViewModifier.ChangesViewModifierListener
-import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory
import com.intellij.openapi.vcs.changes.ui.ChangesListView
import com.intellij.openapi.vcs.changes.ui.ChangesTree
import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder
import com.intellij.platform.diagnostic.telemetry.TelemetryManager
import com.intellij.platform.diagnostic.telemetry.helpers.use
-import com.intellij.platform.util.coroutines.childScope
import com.intellij.platform.vcs.impl.shared.SingleTaskRunner
import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings
@@ -30,57 +24,66 @@ import com.intellij.platform.vcs.impl.shared.telemetry.ChangesView
import com.intellij.platform.vcs.impl.shared.telemetry.VcsScope
import com.intellij.ui.ExperimentalUI.Companion.isNewUI
import com.intellij.util.application
+import com.intellij.util.asDisposable
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
import com.intellij.util.concurrency.annotations.RequiresEdt
-import com.intellij.util.ui.EdtInvocationManager.invokeLaterIfNeeded
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.CalledInAny
import java.lang.Runnable
-import javax.swing.tree.DefaultTreeModel
import kotlin.time.Duration.Companion.milliseconds
+private val REFRESH_DELAY = 100.milliseconds
+
@ApiStatus.Internal
-class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisposable: Disposable) : ChangesViewPanel(changesView), Disposable {
+class CommitChangesViewWithToolbarPanel(
+ changesView: ChangesListView,
+ private val cs: CoroutineScope,
+) : ChangesViewPanel(changesView) {
val project: Project get() = changesView.project
private val settings get() = ChangesViewSettings.getInstance(project)
- private val scope = project.service().cs.childScope("CommitChangesListWithToolbarPanel")
- private val refresher = SingleTaskRunner(scope, 100.milliseconds) {
+ private val refresher = SingleTaskRunner(cs) {
refreshView()
}
private var modelProvider: ModelProvider? = null
- private var disposed = false
+ var id: ChangesViewId? = null
+ set(value) {
+ if (field == null) field = value
+ else error("Id is already assigned")
+ }
init {
refresher.start()
- Disposer.register(parentDisposable, this)
- }
-
- @CalledInAny
- fun setBusy(busy: Boolean) {
- invokeLaterIfNeeded { changesView.setPaintBusy(busy) }
+ cs.launch(Dispatchers.UI) {
+ refresher.getIdleFlow().collect { idle ->
+ changesView.setPaintBusy(!idle)
+ }
+ }
}
@RequiresEdt
fun initPanel(modelProvider: ModelProvider) {
+ checkNotNull(id)
+
this.modelProvider = modelProvider
- scope.launch(Dispatchers.UI) {
+ cs.launch(Dispatchers.UI) {
ChangeListsViewModel.getInstance(project).changeListManagerState.collectLatest {
changesView.repaint()
}
}
- scope.launch(Dispatchers.UI) {
+ cs.launch(Dispatchers.UI) {
PartialChangesHolder.getInstance(project).updates.collectLatest {
changesView.repaint()
}
}
+ changesView.isShowCheckboxes = true
changesView.installPopupHandler(
ActionManager.getInstance().getAction("ChangesViewPopupMenuShared") as ActionGroup
)
@@ -92,16 +95,16 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
toolbarActionGroup.addAll(createChangesToolbarActions(changesView))
- Initializer.EP_NAME.forEachExtensionSafe { it.init(scope, this) }
+ Initializer.EP_NAME.forEachExtensionSafe { it.init(cs, this) }
- ChangesViewModifier.KEY.addChangeListener(project, { resetViewImmediatelyAndRefreshLater() }, this)
- project.messageBus.connect(scope).subscribe(ChangesViewModifier.TOPIC, ChangesViewModifierListener { scheduleRefresh() })
+ ChangesViewModifier.KEY.addChangeListener(project, { resetViewImmediatelyAndRefreshLater() }, cs.asDisposable())
+ project.messageBus.connect(cs).subscribe(ChangesViewModifier.TOPIC, ChangesViewModifierListener { scheduleRefresh() })
scheduleRefresh()
}
@CalledInAny
- fun scheduleRefresh() {
+ fun scheduleRefresh(){
scheduleRefresh(withDelay = true)
}
@@ -118,35 +121,43 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
@CalledInAny
private fun scheduleRefresh(withDelay: Boolean, @RequiresBackgroundThread callback: Runnable? = null) {
- setBusy(true)
- if (withDelay) {
+ if (!withDelay && callback == null) {
refresher.request()
+ return
}
- else {
- refresher.requestNow()
- }
- scope.launch {
+
+ cs.launch {
+ if (withDelay) delay(REFRESH_DELAY)
+ refresher.request()
refresher.awaitNotBusy()
callback?.run()
- setBusy(false)
}
}
@RequiresBackgroundThread
private suspend fun refreshView() {
- if (disposed || !project.isInitialized || application.isUnitTestMode) return
+ if (!cs.isActive || !project.isInitialized || application.isUnitTestMode) return
val modelProvider = modelProvider ?: return
- val model = TRACER.spanBuilder(ChangesView.ChangesViewRefreshBackground.name).use {
- modelProvider.getModel(changesView.grouping)
+ val (modelData, model) = TRACER.spanBuilder(ChangesView.ChangesViewRefreshBackground.name).use {
+ val modelData = modelProvider.getModelData()
+
+ modelData to ChangesViewUtil.createTreeModel(
+ project,
+ changesView.grouping,
+ modelData.changeLists,
+ modelData.unversionedFiles,
+ modelData.ignoredFiles,
+ modelData.isAllowExcludeFromCommit,
+ )
}
checkCanceled()
withContext(Dispatchers.EDT) {
TRACER.spanBuilder(ChangesView.ChangesViewRefreshEdt.getName()).use {
- changesView.updateTreeModel(model.treeModel, ChangesViewTreeStateStrategy())
+ changesView.updateTreeModel(model, ChangesViewTreeStateStrategy())
checkCanceled()
- modelProvider.synchronizeInclusion(model.changeLists, model.unversionedFiles)
+ modelProvider.synchronizeInclusion(modelData.changeLists, modelData.unversionedFiles)
}
}
}
@@ -162,11 +173,6 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
scheduleRefreshNow()
}
- override fun dispose() {
- scope.cancel()
- disposed = true
- }
-
private companion object {
private val TRACER
get() = TelemetryManager.getInstance().getTracer(VcsScope)
@@ -192,11 +198,14 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
}
interface ModelProvider {
- fun getModel(grouping: ChangesGroupingPolicyFactory): ExtendedTreeModel
+ fun getModelData(): ModelData
fun synchronizeInclusion(changeLists: List, unversionedFiles: List)
- class ExtendedTreeModel(val changeLists: List, val unversionedFiles: List, val treeModel: DefaultTreeModel)
+ class ModelData(val changeLists: List,
+ val unversionedFiles: List,
+ val ignoredFiles: List,
+ val isAllowExcludeFromCommit: () -> Boolean)
}
interface Initializer {
@@ -206,7 +215,4 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
internal val EP_NAME = ExtensionPointName("com.intellij.vcs.commitChangesViewInitializer")
}
}
-
- @Service(Service.Level.PROJECT)
- internal class ScopeProvider(val cs: CoroutineScope)
-}
\ No newline at end of file
+}
diff --git a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/SingleTaskRunner.kt b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/SingleTaskRunner.kt
index dff47ebea0ed..326a614c45ce 100644
--- a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/SingleTaskRunner.kt
+++ b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/SingleTaskRunner.kt
@@ -6,10 +6,7 @@ import com.intellij.openapi.diagnostic.getOrHandleException
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.checkCanceled
import kotlinx.coroutines.*
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.*
import org.jetbrains.annotations.ApiStatus
import kotlin.time.Duration
@@ -21,47 +18,39 @@ import kotlin.time.Duration
@ApiStatus.Internal
class SingleTaskRunner(
cs: CoroutineScope,
- private val delay: Duration = Duration.Companion.ZERO,
private val task: suspend () -> Unit,
) {
- private val requested = MutableStateFlow(false)
- private val busy = MutableStateFlow(false)
+ /**
+ * Represents the state of the runner:
+ * 0 - idle,
+ * 1 - a single task is either queued or running,
+ * 2 - has one running task and one queued task.
+ */
+ private val requestCounter = MutableStateFlow(0)
- private val runNow = Channel(capacity = Channel.CONFLATED)
+ fun getIdleFlow(): Flow = requestCounter.map { it == 0 }
private val processorJob = cs.launch(Dispatchers.Default, CoroutineStart.LAZY) {
try {
while (true) {
checkCanceled()
- requested.first { it }
- if (delay.isPositive()) {
- withTimeoutOrNull(delay) { runNow.receive() }
- }
- busy.value = true
- requested.value = false
+ requestCounter.first { it > 0 }
checkCanceled()
runCatching {
task()
}.getOrHandleException { LOG.error("Task failed", it) }
- busy.value = false
+ requestCounter.update { it.dec() }
}
}
finally {
- requested.value = false
- busy.value = false
+ requestCounter.value = 0
LOG.debug { "Task processing finished" }
}
}
fun request() {
if (processorJob.isCancelled) return
- requested.value = true
- }
-
- fun requestNow() {
- if (processorJob.isCancelled) return
- runNow.trySend(Unit)
- requested.value = true
+ requestCounter.update { it.inc().coerceAtMost(2) }
}
fun start() {
@@ -73,10 +62,15 @@ class SingleTaskRunner(
* Await the state where the task is not executed and there are no requests to do so.
*/
suspend fun awaitNotBusy() {
- requested.combine(busy) { requested, busy -> !requested && !busy }.first { it }
+ getIdleFlow().first { it }
}
companion object {
private val LOG = logger()
+
+ fun delayedTaskRunner(cs: CoroutineScope, delay: Duration, task: suspend () -> Unit): SingleTaskRunner = SingleTaskRunner(cs) {
+ delay(delay)
+ task()
+ }
}
}
\ No newline at end of file
diff --git a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangeListsViewModel.kt b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangeListsViewModel.kt
index 3b6d91481528..7b6f4206dc8c 100644
--- a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangeListsViewModel.kt
+++ b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangeListsViewModel.kt
@@ -22,7 +22,7 @@ class ChangeListsViewModel(
) {
val areChangeListsEnabled: StateFlow = changeListsApiFlow(checkRegistry = false) { api, projectId ->
emitAll(api.areChangeListsEnabled(projectId))
- }.stateIn(cs, SharingStarted.Eagerly, false)
+ }.stateIn(cs, SharingStarted.Eagerly, true)
val changeListManagerState: StateFlow = changeListsApiFlow(checkRegistry = false) { api, projectId ->
emitAll(api.getChangeListManagerState(projectId))
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java
index 00df025bb963..0f1b4d0e1dc8 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java
@@ -525,7 +525,6 @@ public final class ChangeListManagerImpl extends ChangeListManagerEx implements
isInitialUpdate = myInitialUpdate;
myInitialUpdate = false;
}
- changesView.setBusy(true);
changesView.scheduleRefresh();
SensitiveProgressWrapper vcsIndicator = new SensitiveProgressWrapper(ProgressManager.getInstance().getProgressIndicator());
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesTreeEditorDiffPreview.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesTreeEditorDiffPreview.kt
deleted file mode 100644
index 7ebc64eb9df3..000000000000
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesTreeEditorDiffPreview.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package com.intellij.openapi.vcs.changes
-
-import com.intellij.openapi.actionSystem.ActionManager
-import com.intellij.openapi.actionSystem.ActionUpdateThread
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.actionSystem.IdeActions
-import com.intellij.openapi.application.ApplicationManager.getApplication
-import com.intellij.openapi.project.DumbAwareAction
-import com.intellij.openapi.vcs.VcsConfiguration
-import com.intellij.openapi.vcs.changes.ui.ChangesTree
-import com.intellij.ui.ExpandableItemsHandler
-import com.intellij.util.EditSourceOnDoubleClickHandler
-import com.intellij.util.Processor
-import com.intellij.util.ui.UIUtil
-import java.awt.event.MouseEvent
-import javax.swing.JComponent
-
-abstract class ChangesTreeEditorDiffPreview(
- protected val tree: ChangesTree,
- targetComponent: JComponent = tree,
-) : EditorTabDiffPreview(tree.project) {
-
- init {
- tree.doubleClickHandler = Processor { e -> handleDoubleClick(e) }
- tree.enterKeyHandler = Processor { e -> handleEnterKey() }
- tree.addSelectionListener { handleSingleClick() }
- PreviewOnNextDiffAction().registerCustomShortcutSet(targetComponent, this)
-
- UIUtil.putClientProperty(tree, ExpandableItemsHandler.IGNORE_ITEM_SELECTION, true)
- }
-
- protected open fun isPreviewOnDoubleClick(): Boolean = true
- open fun handleDoubleClick(e: MouseEvent): Boolean {
- if (!isPreviewOnDoubleClick()) return false
- if (EditSourceOnDoubleClickHandler.isToggleEvent(tree, e)) return false
- return performDiffAction()
- }
-
- protected open fun isPreviewOnEnter(): Boolean = true
- open fun handleEnterKey(): Boolean {
- if (!isPreviewOnEnter()) return false
- return performDiffAction()
- }
-
- protected open fun isOpenPreviewWithSingleClickEnabled(): Boolean = false
- protected open fun isOpenPreviewWithSingleClick(): Boolean {
- if (!isOpenPreviewWithSingleClickEnabled()) return false
- if (!VcsConfiguration.getInstance(project).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN) return false
- return true
- }
-
- protected open fun handleSingleClick() {
- if (!isOpenPreviewWithSingleClick()) return
- val opened = openPreview(false)
- if (!opened) {
- closePreview() // auto-close editor tab if nothing to preview
- }
- }
-
- protected open fun isOpenPreviewWithNextDiffShortcut(): Boolean = true
- protected open fun handleNextDiffShortcut() {
- if (!isOpenPreviewWithNextDiffShortcut()) return
- openPreview(true)
- }
-
- override fun handleEscapeKey() {
- closePreview()
- // closing the diff preview editor schedules focus switch to "some" other editor in JBTabsImpl
- // we can't disable that, so we have to manually override the focus
- getApplication().invokeLater {
- returnFocusToTree()
- }
- }
-
- protected open fun returnFocusToTree() = Unit
-
- private inner class PreviewOnNextDiffAction : DumbAwareAction() {
- init {
- copyShortcutFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_DIFF))
- }
-
- override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
-
- override fun update(e: AnActionEvent) {
- e.presentation.isEnabledAndVisible = isOpenPreviewWithNextDiffShortcut()
- }
-
- override fun actionPerformed(e: AnActionEvent) {
- handleNextDiffShortcut()
- }
- }
-}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewI.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewI.java
index a346c1c66e26..2d8eef78d365 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewI.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewI.java
@@ -27,7 +27,6 @@ public interface ChangesViewI {
void selectFile(VirtualFile vFile);
void selectChanges(@NotNull List extends Change> changes);
void updateProgressComponent(@NotNull List> progress);
- void setBusy(boolean b);
void setGrouping(@NotNull String groupingKey);
}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java
index 0dbc571a5acc..209822edfbc7 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java
@@ -23,23 +23,23 @@ import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vcs.*;
+import com.intellij.openapi.vcs.ProjectLevelVcsManager;
+import com.intellij.openapi.vcs.VcsApplicationSettings;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.changes.actions.diff.ShowDiffFromLocalChangesActionProvider;
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
import com.intellij.openapi.vcs.changes.ui.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.platform.vcs.impl.shared.RdLocalChanges;
import com.intellij.platform.vcs.impl.shared.changes.ChangesViewDataKeys;
import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings;
import com.intellij.platform.vcs.impl.shared.changes.PreviewDiffSplitterComponent;
-import com.intellij.ui.ComponentUtil;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.content.Content;
import com.intellij.util.*;
-import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
import com.intellij.util.concurrency.annotations.RequiresEdt;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBusConnection;
@@ -47,23 +47,18 @@ import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.components.BorderLayoutPanel;
-import com.intellij.util.ui.tree.TreeUtil;
+import com.intellij.vcs.changes.BackendChangesView;
import com.intellij.vcs.commit.*;
-import com.intellij.vcsUtil.VcsUtil;
+import kotlinx.coroutines.CoroutineScope;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreePath;
-import java.awt.*;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
-import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -71,26 +66,25 @@ import static com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager.*;
import static com.intellij.openapi.vcs.changes.ui.ChangesViewContentManagerKt.isCommitToolWindowShown;
import static com.intellij.openapi.vcs.changes.ui.ChangesViewContentManagerKt.subscribeOnVcsToolWindowLayoutChanges;
import static com.intellij.util.ui.JBUI.Panels.simplePanel;
-import static java.util.Arrays.asList;
public class ChangesViewManager implements ChangesViewEx, Disposable {
private static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION";
+ private final @NotNull CoroutineScope myScope;
private final @NotNull Project myProject;
- private @Nullable CommitChangesViewWithToolbarPanel myChangesPanel;
+ private @Nullable BackendChangesView myChangesView;
private @Nullable ChangesViewToolWindowPanel myToolWindowPanel;
@NotNull
@RequiresEdt
- CommitChangesViewWithToolbarPanel initChangesPanel() {
- if (myChangesPanel == null) {
+ BackendChangesView initChangesView() {
+ if (myChangesView == null) {
Activity activity = StartUpMeasurer.startActivity("ChangesViewPanel initialization");
- ChangesListView tree = new LocalChangesListView(myProject);
- myChangesPanel = new CommitChangesViewWithToolbarPanel(tree, this);
+ myChangesView = BackendChangesView.create(myProject, myScope);
activity.end();
}
- return myChangesPanel;
+ return myChangesView;
}
@RequiresEdt
@@ -98,9 +92,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
if (myToolWindowPanel == null) {
Activity activity = StartUpMeasurer.startActivity("ChangesViewToolWindowPanel initialization");
- // ChangesViewPanel is used for a singular ChangesViewToolWindowPanel instance. Cleanup is not needed.
- CommitChangesViewWithToolbarPanel changesViewPanel = initChangesPanel();
- ChangesViewToolWindowPanel panel = new ChangesViewToolWindowPanel(myProject, changesViewPanel);
+ BackendChangesView changesView = initChangesView();
+ ChangesViewToolWindowPanel panel = new ChangesViewToolWindowPanel(myProject, changesView);
Disposer.register(this, panel);
panel.updateCommitWorkflow();
@@ -108,7 +101,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
myToolWindowPanel = panel;
Disposer.register(panel, () -> {
// Content is removed from TW
- myChangesPanel = null;
+ myChangesView = null;
myToolWindowPanel = null;
});
@@ -117,7 +110,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
return myToolWindowPanel;
}
- public ChangesViewManager(@NotNull Project project) {
+ public ChangesViewManager(@NotNull Project project, @NotNull CoroutineScope coroutineScope) {
+ myScope = coroutineScope;
myProject = project;
MessageBusConnection busConnection = project.getMessageBus().connect(this);
@@ -159,10 +153,10 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
public void dispose() {
}
- public static class ContentPreloader implements ChangesViewContentProvider.Preloader {
+ static class ContentPreloader implements ChangesViewContentProvider.Preloader {
private final @NotNull Project myProject;
- public ContentPreloader(@NotNull Project project) {
+ ContentPreloader(@NotNull Project project) {
myProject = project;
}
@@ -182,27 +176,26 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
@Override
public void scheduleRefresh(@NotNull Runnable callback) {
- if (myToolWindowPanel != null) {
- myToolWindowPanel.scheduleRefreshNow(callback);
- }
+ if (myChangesView == null) return;
+ myChangesView.getViewModel().scheduleRefreshNow(callback);
}
@Override
public void scheduleRefresh() {
- if (myToolWindowPanel == null) return;
- myToolWindowPanel.scheduleRefresh();
+ if (myChangesView == null) return;
+ myChangesView.getViewModel().scheduleDelayedRefresh();
}
@Override
public void selectFile(VirtualFile vFile) {
- if (myToolWindowPanel == null) return;
- myToolWindowPanel.selectFile(vFile);
+ if (myChangesView == null) return;
+ myChangesView.getViewModel().selectFile(vFile);
}
@Override
public void selectChanges(@NotNull List extends Change> changes) {
- if (myToolWindowPanel == null) return;
- myToolWindowPanel.selectChanges(changes);
+ if (myChangesView == null) return;
+ myChangesView.getViewModel().selectChanges(new ArrayList<>(changes));
}
@Override
@@ -211,16 +204,10 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
myToolWindowPanel.updateProgressComponent(progress);
}
- @Override
- public void setBusy(boolean b) {
- if (myToolWindowPanel == null) return;
- myToolWindowPanel.myChangesPanel.setBusy(b);
- }
-
@Override
public void setGrouping(@NotNull String groupingKey) {
- if (myToolWindowPanel == null) return;
- myToolWindowPanel.myChangesPanel.setGrouping(groupingKey);
+ if (myChangesView == null) return;
+ myChangesView.getViewModel().setGrouping(groupingKey);
}
private void updateCommitWorkflow() {
@@ -241,8 +228,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
@Override
public void resetViewImmediatelyAndRefreshLater() {
- if (myToolWindowPanel != null) {
- myToolWindowPanel.myChangesPanel.resetViewImmediatelyAndRefreshLater();
+ if (myChangesView != null) {
+ myChangesView.getViewModel().resetViewImmediatelyAndRefreshLater();
}
}
@@ -255,25 +242,12 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
@Override
public void initTabContent(@NotNull Content content) {
- if (RdLocalChanges.isEnabled()) {
- // Action isn't executed unless a context component is showing,
- // which makes things complicated in RD scenario because:
- // * For Commit TW we still use PROJECTOR_STEALING hacking content of the particular tab.
- // * The current content component in a backend TW is selected as the context component.
- // (see `ToolWindowComponentIdProvider` and `ComponentIdOwner` for more insights).
- // * An empty panel is used for this tab content on the backend.
- //
- // As a workaround, it's explicitly marked as shown.
- ComponentUtil.markAsShowing(content.getComponent(), true);
- return;
- }
-
ChangesViewManager viewManager = (ChangesViewManager)getInstance(myProject);
ChangesViewToolWindowPanel panel = viewManager.initToolWindowPanel();
content.setHelpId(ChangesListView.HELP_ID);
content.setComponent(panel);
- content.setPreferredFocusableComponent(panel.myView);
+ content.setPreferredFocusableComponent(panel.myChangesView.getViewModel().getPreferredFocusableComponent());
}
}
@@ -318,7 +292,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
}
}
- public static final class ChangesViewToolWindowPanel extends SimpleToolWindowPanel implements Disposable {
+ private static final class ChangesViewToolWindowPanel extends SimpleToolWindowPanel implements Disposable {
private static final @NotNull RegistryValue isOpenEditorDiffPreviewWithSingleClick =
Registry.get("show.diff.preview.as.editor.tab.with.single.click");
@@ -328,8 +302,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
private final @NotNull Wrapper myMainPanelContent;
private final @NotNull BorderLayoutPanel myContentPanel;
- private final @NotNull CommitChangesViewWithToolbarPanel myChangesPanel;
- private final @NotNull ChangesListView myView;
+
+ private final @NotNull BackendChangesView myChangesView;
private final @NotNull ChangesViewCommitPanelSplitter myCommitPanelSplitter;
private final @NotNull ChangesViewEditorDiffPreview myEditorDiffPreview;
@@ -345,18 +319,16 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
private boolean myDisposed = false;
private ChangesViewToolWindowPanel(@NotNull Project project,
- @NotNull CommitChangesViewWithToolbarPanel changesViewPanel) {
+ @NotNull BackendChangesView backendChangesView) {
super(false, true);
myProject = project;
myChangesViewSettings = ChangesViewSettings.getInstance(project);
- myChangesPanel = changesViewPanel;
- changesViewPanel.initPanel(new ModelProvider(project, () -> myCommitWorkflowHandler));
+ myChangesView = backendChangesView;
+ myChangesView.getViewModel().initPanel();
MessageBusConnection busConnection = myProject.getMessageBus().connect(this);
myVcsConfiguration = VcsConfiguration.getInstance(myProject);
- // ChangesViewPanel is used for a singular ChangesViewToolWindowPanel instance. Cleanup is not needed.
- myView = myChangesPanel.getChangesView();
registerShortcuts(this);
ApplicationManager.getApplication().getMessageBus().connect(project)
@@ -372,16 +344,9 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
myCommitPanelSplitter = new ChangesViewCommitPanelSplitter(myProject);
Disposer.register(this, myCommitPanelSplitter);
- myCommitPanelSplitter.setFirstComponent(myChangesPanel);
+ myCommitPanelSplitter.setFirstComponent(backendChangesView.getChangesPanel());
- myContentPanel = new BorderLayoutPanel() {
- @Override
- public Dimension getMinimumSize() {
- return isMinimumSizeSet() || myChangesPanel.isToolbarHorizontal()
- ? super.getMinimumSize()
- : myChangesPanel.getToolbar().getComponent().getPreferredSize();
- }
- };
+ myContentPanel = new BorderLayoutPanel();
myContentPanel.addToCenter(myCommitPanelSplitter);
myMainPanelContent = new Wrapper(myContentPanel);
JPanel mainPanel = simplePanel(myMainPanelContent)
@@ -391,17 +356,17 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
Disposer.register(this, myEditorDiffPreview);
// Override the handlers registered by editorDiffPreview
- myView.setDoubleClickHandler(e -> {
- if (EditSourceOnDoubleClickHandler.isToggleEvent(myView, e)) return false;
+ myChangesView.getViewModel().getTree().setDoubleClickHandler(e -> {
+ if (EditSourceOnDoubleClickHandler.isToggleEvent(myChangesView.getViewModel().getTree(), e)) return false;
if (performHoverAction()) return true;
if (myEditorDiffPreview.handleDoubleClick(e)) return true;
- OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myView), true);
+ OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myChangesView.getViewModel().getTree()), true);
return true;
});
- myView.setEnterKeyHandler(e -> {
+ myChangesView.getViewModel().getTree().setEnterKeyHandler(e -> {
if (performHoverAction()) return true;
if (myEditorDiffPreview.handleEnterKey()) return true;
- OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myView), false);
+ OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myChangesView.getViewModel().getTree()), false);
return true;
});
@@ -412,7 +377,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
}
private boolean performHoverAction() {
- ChangesBrowserNode> selected = VcsTreeModelData.selected(myView).iterateNodes().single();
+ ChangesBrowserNode> selected = VcsTreeModelData.selected(myChangesView.getViewModel().getTree()).iterateNodes().single();
if (selected == null) return false;
for (ChangesViewNodeAction extension : ChangesViewNodeAction.EP_NAME.getExtensions(myProject)) {
@@ -453,7 +418,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
private class ChangesViewEditorDiffPreview extends TreeHandlerEditorDiffPreview {
private ChangesViewEditorDiffPreview() {
- super(myView, myContentPanel, ChangesViewDiffPreviewHandler.INSTANCE);
+ super(myChangesView.getViewModel().getTree(), myContentPanel, ChangesViewDiffPreviewHandler.INSTANCE);
}
@Override
@@ -491,7 +456,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
@Override
protected boolean isOpenPreviewWithSingleClick() {
- if (myChangesPanel.getChangesView().isModelUpdateInProgress()) return false;
+ if (myChangesView.getViewModel().isModelUpdateInProgress()) return false;
if (mySplitterDiffPreview != null && myVcsConfiguration.LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN) return false;
return super.isOpenPreviewWithSingleClick();
}
@@ -544,8 +509,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
}
private ChangesViewDiffPreviewProcessor createDiffPreviewProcessor(boolean isInEditor) {
-
- ChangesViewDiffPreviewProcessor processor = new ChangesViewDiffPreviewProcessor(myView, isInEditor);
+ ChangesViewDiffPreviewProcessor processor = new ChangesViewDiffPreviewProcessor(myChangesView.getViewModel().getTree(), isInEditor);
this.addListener(new Listener() {
@Override
public void allowExcludeFromCommitChanged() {
@@ -583,6 +547,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
myCommitWorkflowHandler = null;
myCommitPanel = null;
}
+ myChangesView.getViewModel().setCommitWorkflowHandler(myCommitWorkflowHandler);
myDispatcher.getMulticaster().allowExcludeFromCommitChanged();
configureToolbars();
@@ -598,12 +563,12 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
private void configureToolbars() {
boolean isToolbarHorizontal = CommitModeManager.getInstance(myProject).getCurrentCommitMode().useCommitToolWindow();
- myChangesPanel.setToolbarHorizontal(isToolbarHorizontal);
+ myChangesView.getViewModel().setToolbarHorizontal(isToolbarHorizontal);
}
@Override
public @NotNull List getActions(boolean originalProvider) {
- return asList(myChangesPanel.getToolbarActionGroup().getChildren(ActionManager.getInstance()));
+ return myChangesView.getViewModel().getActions();
}
@Override
@@ -611,7 +576,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
super.uiDataSnapshot(sink);
sink.set(DiffDataKeys.EDITOR_TAB_DIFF_PREVIEW, myEditorDiffPreview);
sink.set(ChangesViewDataKeys.SETTINGS, myChangesViewSettings);
- sink.set(ChangesViewDataKeys.REFRESHER, () -> scheduleRefreshNow(null));
+ sink.set(ChangesViewDataKeys.REFRESHER, () -> myChangesView.getViewModel().scheduleRefreshNow(null));
// This makes COMMIT_WORKFLOW_HANDLER available anywhere in "Local Changes" - so commit executor actions are enabled.
DataSink.uiDataSnapshot(sink, myCommitPanel);
@@ -638,42 +603,6 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
});
}
- public void scheduleRefresh() {
- myChangesPanel.scheduleRefresh();
- }
-
- private void scheduleRefreshNow(@Nullable @RequiresBackgroundThread Runnable callback) {
- myChangesPanel.scheduleRefreshNow(callback);
- }
-
- public void selectFile(@Nullable VirtualFile vFile) {
- if (vFile == null) return;
-
- ChangesBrowserNode> node = findNodeForFile(vFile);
- if (node == null) return;
-
- TreeUtil.selectNode(myView, node);
- }
-
- public void selectChanges(@NotNull List extends Change> changes) {
- List paths = new ArrayList<>();
-
- for (Change change : changes) {
- ContainerUtil.addIfNotNull(paths, myView.findNodePathInTree(change));
- }
-
- TreeUtil.selectPaths(myView, paths);
- }
-
- private @Nullable ChangesBrowserNode> findNodeForFile(@NotNull VirtualFile file) {
- FilePath filePath = VcsUtil.getFilePath(file);
- DefaultMutableTreeNode root = (DefaultMutableTreeNode)myView.getModel().getRoot();
- return (ChangesBrowserNode>)TreeUtil.findNode(root, node -> {
- FilePath nodeFilePath = VcsTreeModelData.mapUserObjectToFilePath(node.getUserObject());
- return Objects.equals(filePath, nodeFilePath);
- });
- }
-
private void invokeLaterIfNeeded(Runnable runnable) {
ModalityUiUtil.invokeLaterIfNeeded(ModalityState.nonModal(), myProject.getDisposed(), runnable);
}
@@ -693,43 +622,6 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
return ChangesViewWorkflowManager.getInstance(myProject).getCommitWorkflowHandler();
}
- private static class ModelProvider implements CommitChangesViewWithToolbarPanel.ModelProvider {
- private final @NotNull Project project;
- private final @NotNull Supplier<@Nullable ChangesViewCommitWorkflowHandler> commitWorkflowHandlerSupplier;
-
- private ModelProvider(@NotNull Project project,
- @NotNull Supplier<@Nullable ChangesViewCommitWorkflowHandler> commitWorkflowHandlerSupplier) {
- this.project = project;
- this.commitWorkflowHandlerSupplier = commitWorkflowHandlerSupplier;
- }
-
- @Override
- public @NotNull ExtendedTreeModel getModel(@NotNull ChangesGroupingPolicyFactory grouping) {
- ChangeListManagerImpl changeListManager = ChangeListManagerImpl.getInstanceImpl(project);
- List changeLists = changeListManager.getChangeLists();
- List unversionedFiles = changeListManager.getUnversionedFilesPaths();
-
- DefaultTreeModel treeModel = ChangesViewUtil.INSTANCE.createTreeModel(
- project,
- grouping,
- changeLists,
- unversionedFiles,
- ChangesViewSettings.getInstance(project).getShowIgnored(),
- () -> isAllowExcludeFromCommit(commitWorkflowHandlerSupplier.get())
- );
- return new ExtendedTreeModel(changeLists, unversionedFiles, treeModel);
- }
-
- @Override
- public void synchronizeInclusion(@NotNull List extends @NotNull LocalChangeList> changeLists,
- @NotNull List extends @NotNull FilePath> unversionedFiles) {
- ChangesViewCommitWorkflowHandler commitWorkflowHandler = commitWorkflowHandlerSupplier.get();
- if (commitWorkflowHandler != null) {
- commitWorkflowHandler.synchronizeInclusion(changeLists, unversionedFiles);
- }
- }
- }
-
private static boolean isAllowExcludeFromCommit(@Nullable ChangesViewCommitWorkflowHandler handler) {
return handler != null && handler.isActive();
}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewWorkflowManagerImpl.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewWorkflowManagerImpl.kt
index 96d29e0a784f..4f25ed86856e 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewWorkflowManagerImpl.kt
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewWorkflowManagerImpl.kt
@@ -40,9 +40,9 @@ internal class ChangesViewWorkflowManagerImpl(
val activity = StartUpMeasurer.startActivity("ChangesViewWorkflowManager initialization")
// ChangesViewPanel can be reused between workflow instances -> should clean up after ourselves
- val changesPanel = (ChangesViewManager.getInstance(project) as ChangesViewManager).initChangesPanel()
+ val changesView = (ChangesViewManager.getInstance(project) as ChangesViewManager).initChangesView()
val workflow = ChangesViewCommitWorkflow(project)
- val commitPanel = ChangesViewCommitPanel(project, changesPanel.changesView)
+ val commitPanel = ChangesViewCommitPanel(project, changesView.viewModel)
_commitWorkflowHandler = ChangesViewCommitWorkflowHandler(workflow, commitPanel)
project.messageBus.syncPublisher(TOPIC).commitWorkflowChanged()
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DummyChangesView.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DummyChangesView.java
index f1bc1734e2a7..176e5b2f1737 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DummyChangesView.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DummyChangesView.java
@@ -48,10 +48,6 @@ class DummyChangesView implements ChangesViewEx {
public void updateProgressComponent(@NotNull List> progress) {
}
- @Override
- public void setBusy(boolean b) {
- }
-
@Override
public void setGrouping(@NotNull String groupingKey) {
}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendChangesViewModelBuilderService.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendChangesViewModelBuilderService.kt
index 8118685a9390..8b18454502af 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendChangesViewModelBuilderService.kt
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendChangesViewModelBuilderService.kt
@@ -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 com.intellij.openapi.vcs.changes.ui
+import com.intellij.openapi.project.InitialVfsRefreshService
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.FileStatus
import com.intellij.openapi.vcs.changes.*
@@ -24,6 +25,12 @@ internal class BackendChangesViewModelBuilderService(private val project: Projec
override fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder) {
val changeListManager = ChangeListManagerImpl.getInstanceImpl(project)
+ val shouldShowUntrackedLoading = changeListManager.unversionedFilesPaths.isEmpty() &&
+ !project.getService(InitialVfsRefreshService::class.java).isInitialVfsRefreshFinished() &&
+ changeListManager.isUnversionedInUpdateMode
+ if (shouldShowUntrackedLoading) {
+ modelBuilder.insertSubtreeRoot(ChangesBrowserUnversionedLoadingPendingNode())
+ }
modelBuilder.setLocallyDeletedPaths(changeListManager.deletedFiles)
.setModifiedWithoutEditing(changeListManager.modifiedWithoutEditing)
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendCommitChangesViewWithToolbarInitializer.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendCommitChangesViewWithToolbarInitializer.kt
index 8cfc7c82c501..6166a61f9400 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendCommitChangesViewWithToolbarInitializer.kt
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendCommitChangesViewWithToolbarInitializer.kt
@@ -38,7 +38,6 @@ internal class BackendCommitChangesViewWithToolbarInitializer : CommitChangesVie
}
override fun changedFileStatusChanged() {
- panel.setBusy(false)
panel.scheduleRefresh()
val changeListManager = ChangeListManagerImpl.getInstanceImpl(panel.project)
ChangesViewManager.getInstance(panel.project).updateProgressComponent(changeListManager.getAdditionalUpdateInfo())
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeHandlerDiffRequestProcessor.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeHandlerDiffRequestProcessor.kt
index 79452a6da280..2a650762398c 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeHandlerDiffRequestProcessor.kt
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeHandlerDiffRequestProcessor.kt
@@ -7,13 +7,23 @@ import com.intellij.diff.impl.DiffEditorViewer
import com.intellij.diff.tools.combined.*
import com.intellij.diff.util.DiffUtil
import com.intellij.openapi.ListSelection
+import com.intellij.openapi.actionSystem.ActionManager
+import com.intellij.openapi.actionSystem.ActionUpdateThread
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.actionSystem.IdeActions
+import com.intellij.openapi.application.ApplicationManager.getApplication
+import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.vcs.VcsConfiguration
import com.intellij.openapi.vcs.changes.Change
import com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor
import com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor.Wrapper
-import com.intellij.openapi.vcs.changes.ChangesTreeEditorDiffPreview
+import com.intellij.openapi.vcs.changes.EditorTabDiffPreview
import com.intellij.openapi.vcs.changes.actions.diff.WrapperCombinedBlockProducer
import com.intellij.openapi.vcs.changes.actions.diff.prepareCombinedBlocksFromWrappers
+import com.intellij.ui.ExpandableItemsHandler
+import com.intellij.util.EditSourceOnDoubleClickHandler
+import com.intellij.util.Processor
import com.intellij.util.containers.JBIterable
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.tree.TreeUtil
@@ -24,6 +34,7 @@ import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.CalledInAny
import java.awt.event.FocusAdapter
import java.awt.event.FocusEvent
+import java.awt.event.MouseEvent
import java.beans.PropertyChangeListener
import javax.swing.JComponent
import javax.swing.JTree
@@ -191,12 +202,64 @@ open class TreeHandlerChangesTreeTracker(
abstract class TreeHandlerEditorDiffPreview(
- tree: ChangesTree,
+ protected val tree: ChangesTree,
targetComponent: JComponent = tree,
protected val handler: ChangesTreeDiffPreviewHandler
-) : ChangesTreeEditorDiffPreview(tree, targetComponent) {
+) : EditorTabDiffPreview(tree.project) {
constructor(tree: ChangesTree, handler: ChangesTreeDiffPreviewHandler) : this(tree, tree, handler)
+ init {
+ tree.doubleClickHandler = Processor { e -> handleDoubleClick(e) }
+ tree.enterKeyHandler = Processor { _ -> handleEnterKey() }
+ tree.addSelectionListener { handleSingleClick() }
+ PreviewOnNextDiffAction().registerCustomShortcutSet(targetComponent, this)
+
+ UIUtil.putClientProperty(tree, ExpandableItemsHandler.IGNORE_ITEM_SELECTION, true)
+ }
+
+ protected open fun isPreviewOnDoubleClick(): Boolean = true
+ open fun handleDoubleClick(e: MouseEvent): Boolean {
+ if (!isPreviewOnDoubleClick()) return false
+ if (EditSourceOnDoubleClickHandler.isToggleEvent(tree, e)) return false
+ return performDiffAction()
+ }
+
+ protected open fun isPreviewOnEnter(): Boolean = true
+ open fun handleEnterKey(): Boolean {
+ if (!isPreviewOnEnter()) return false
+ return performDiffAction()
+ }
+
+ protected open fun isOpenPreviewWithSingleClickEnabled(): Boolean = false
+ protected open fun isOpenPreviewWithSingleClick(): Boolean {
+ if (!isOpenPreviewWithSingleClickEnabled()) return false
+ if (!VcsConfiguration.getInstance(project).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN) return false
+ return true
+ }
+
+ protected open fun handleSingleClick() {
+ if (!isOpenPreviewWithSingleClick()) return
+ val opened = openPreview(false)
+ if (!opened) {
+ closePreview() // auto-close editor tab if nothing to preview
+ }
+ }
+
+ protected open fun isOpenPreviewWithNextDiffShortcut(): Boolean = true
+ protected open fun handleNextDiffShortcut() {
+ if (!isOpenPreviewWithNextDiffShortcut()) return
+ openPreview(true)
+ }
+
+ override fun handleEscapeKey() {
+ closePreview()
+ getApplication().invokeLater {
+ returnFocusToTree()
+ }
+ }
+
+ protected open fun returnFocusToTree() = Unit
+
final override fun hasContent(): Boolean {
return handler.hasContent(tree)
}
@@ -223,6 +286,22 @@ abstract class TreeHandlerEditorDiffPreview(
@CalledInAny
abstract fun getEditorTabName(wrapper: Wrapper?): String?
+ private inner class PreviewOnNextDiffAction : DumbAwareAction() {
+ init {
+ copyShortcutFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_DIFF))
+ }
+
+ override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
+
+ override fun update(e: AnActionEvent) {
+ e.presentation.isEnabledAndVisible = isOpenPreviewWithNextDiffShortcut()
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ handleNextDiffShortcut()
+ }
+ }
+
companion object {
fun createDefaultViewer(changesTree: ChangesTree, previewHandler: ChangesTreeDiffPreviewHandler, place: String): DiffEditorViewer {
val processor = if (CombinedDiffRegistry.isEnabled()) {
diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt
new file mode 100644
index 000000000000..1d4261dc700f
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt
@@ -0,0 +1,45 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.changes
+
+import com.intellij.openapi.components.Service
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vcs.changes.ChangesViewId
+import com.intellij.openapi.vcs.changes.ChangesViewSplitComponentBinding
+import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel
+import com.intellij.openapi.vcs.changes.LocalChangesListView
+import com.intellij.platform.kernel.ids.storeValueGlobally
+import com.intellij.platform.vcs.impl.shared.RdLocalChanges
+import com.intellij.ui.split.createComponent
+import com.intellij.vcs.changes.viewModel.BackendCommitChangesViewModel
+import com.intellij.vcs.changes.viewModel.BackendLocalCommitChangesViewModel
+import com.intellij.vcs.changes.viewModel.BackendRemoteCommitChangesViewModel
+import kotlinx.coroutines.CoroutineScope
+import javax.swing.JComponent
+
+internal class BackendChangesView private constructor(
+ val changesPanel: JComponent,
+ val viewModel: BackendCommitChangesViewModel,
+) {
+ companion object {
+ @JvmStatic
+ fun create(project: Project, scope: CoroutineScope): BackendChangesView {
+ if (RdLocalChanges.isEnabled()) {
+ val viewModel = BackendRemoteCommitChangesViewModel(project)
+ val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType)
+ val panel = ChangesViewSplitComponentBinding.createComponent(project, scope, id)
+ return BackendChangesView(panel, viewModel)
+ }
+ else {
+ val panel = CommitChangesViewWithToolbarPanel(LocalChangesListView(project), scope)
+ val viewModel = BackendLocalCommitChangesViewModel(panel)
+ val backendChangesView = BackendChangesView(panel, viewModel)
+ val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType)
+ panel.id = id
+ return backendChangesView
+ }
+ }
+ }
+
+ @Service(Service.Level.PROJECT)
+ internal class ScopeProvider(val cs: CoroutineScope)
+}
\ No newline at end of file
diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt
new file mode 100644
index 000000000000..46c5548adf83
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt
@@ -0,0 +1,8 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.changes
+
+import com.intellij.openapi.vcs.changes.ChangesViewId
+import com.intellij.platform.kernel.ids.BackendValueIdType
+import com.intellij.vcs.changes.viewModel.BackendCommitChangesViewModel
+
+internal object BackendChangesViewValueIdType : BackendValueIdType(::ChangesViewId)
\ No newline at end of file
diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt
new file mode 100644
index 000000000000..ff0535956cda
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt
@@ -0,0 +1,52 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.changes.viewModel
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vcs.changes.InclusionModel
+import com.intellij.openapi.vcs.changes.ui.ChangesListView
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler
+import kotlinx.coroutines.flow.SharedFlow
+import org.jetbrains.annotations.ApiStatus
+import javax.swing.JComponent
+
+// TODO IJPL-173924 cleanup methods returning tree/component
+internal interface BackendCommitChangesViewModel {
+ val inclusionChanged: SharedFlow
+
+ fun initPanel()
+
+ fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?)
+
+ fun setToolbarHorizontal(horizontal: Boolean)
+ fun getActions(): List
+ fun isModelUpdateInProgress(): Boolean
+
+ fun scheduleRefreshNow(callback: Runnable?)
+ fun scheduleDelayedRefresh()
+ fun setGrouping(groupingKey: String)
+ fun resetViewImmediatelyAndRefreshLater()
+
+ fun setInclusionModel(model: InclusionModel?)
+ fun setShowCheckboxes(value: Boolean)
+
+ fun getDisplayedChanges(): List
+ fun getIncludedChanges(): List
+ fun getDisplayedUnversionedFiles(): List
+ fun getIncludedUnversionedFiles(): List
+
+ fun expand(item: Any)
+ fun select(item: Any)
+ fun selectFirst(items: Collection)
+
+ fun selectFile(vFile: VirtualFile?)
+ fun selectChanges(changes: List)
+
+ @ApiStatus.Obsolete
+ fun getTree(): ChangesListView
+
+ @ApiStatus.Obsolete
+ fun getPreferredFocusableComponent(): JComponent
+}
diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt
new file mode 100644
index 000000000000..c860cf1f89e7
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt
@@ -0,0 +1,126 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.changes.viewModel
+
+import com.intellij.openapi.actionSystem.ActionManager
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.changes.*
+import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode.UNVERSIONED_FILES_TAG
+import com.intellij.openapi.vcs.changes.ui.ChangesListView
+import com.intellij.openapi.vcs.changes.ui.VcsTreeModelData.*
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings
+import com.intellij.util.ui.tree.TreeUtil.*
+import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import javax.swing.JComponent
+import javax.swing.tree.TreePath
+
+internal class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel) : BackendCommitChangesViewModel {
+ private var commitWorkflowHandler: ChangesViewCommitWorkflowHandler? = null
+ private val _inclusionChanged = MutableSharedFlow()
+
+ override val inclusionChanged = _inclusionChanged.asSharedFlow()
+
+ override fun setInclusionModel(model: InclusionModel?) {
+ panel.changesView.setInclusionModel(model)
+ }
+
+ override fun initPanel() {
+ panel.initPanel(ModelProvider())
+ panel.changesView.setInclusionListener { _inclusionChanged.tryEmit(Unit) }
+ }
+
+ override fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) {
+ commitWorkflowHandler = handler
+ }
+
+ override fun setToolbarHorizontal(horizontal: Boolean) {
+ panel.isToolbarHorizontal = horizontal
+ }
+
+ override fun getActions(): List = panel.toolbarActionGroup.getChildren(ActionManager.getInstance()).toList()
+
+ override fun isModelUpdateInProgress(): Boolean = panel.changesView.isModelUpdateInProgress
+
+ override fun scheduleRefreshNow(callback: Runnable?) {
+ panel.scheduleRefreshNow(callback)
+ }
+
+ override fun scheduleDelayedRefresh() {
+ panel.scheduleRefresh()
+ }
+
+ override fun setGrouping(groupingKey: String) {
+ panel.setGrouping(groupingKey)
+ }
+
+ override fun resetViewImmediatelyAndRefreshLater() {
+ panel.resetViewImmediatelyAndRefreshLater()
+ }
+
+ override fun setShowCheckboxes(value: Boolean) {
+ panel.changesView.isShowCheckboxes = value
+ }
+
+ override fun getDisplayedChanges(): List = all(panel.changesView).userObjects(Change::class.java)
+
+ override fun getIncludedChanges(): List = included(panel.changesView).userObjects(Change::class.java)
+
+ override fun getDisplayedUnversionedFiles(): List = allUnderTag(panel.changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java)
+
+ override fun getIncludedUnversionedFiles(): List = includedUnderTag(panel.changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java)
+
+ override fun expand(item: Any) {
+ val node = panel.changesView.findNodeInTree(item)
+ node?.let { panel.changesView.expandSafe(it) }
+ }
+
+ override fun select(item: Any) {
+ val path = panel.changesView.findNodePathInTree(item)
+ path?.let { selectPath(panel.changesView, it, false) }
+ }
+
+ override fun selectFirst(items: Collection) {
+ if (items.isEmpty()) return
+ val path = treePathTraverser(panel.changesView).preOrderDfsTraversal().find { getLastUserObject(it) in items }
+ path?.let { selectPath(panel.changesView, it, false) }
+ }
+
+ override fun selectFile(vFile: VirtualFile?) {
+ panel.changesView.selectFile(vFile)
+ }
+
+ override fun selectChanges(changes: List) {
+ val paths: MutableList = ArrayList()
+ for (change in changes) {
+ panel.changesView.findNodePathInTree(change)?.let { paths.add(it) }
+ }
+ selectPaths(panel.changesView, paths)
+ }
+
+ override fun getTree(): ChangesListView = panel.changesView
+
+ override fun getPreferredFocusableComponent(): JComponent = panel.changesView.preferredFocusedComponent
+
+ private inner class ModelProvider : CommitChangesViewWithToolbarPanel.ModelProvider {
+ override fun getModelData(): CommitChangesViewWithToolbarPanel.ModelProvider.ModelData {
+ val project = panel.project
+ val changeListManager = ChangeListManagerImpl.getInstanceImpl(project)
+ val changeLists = changeListManager.changeLists
+ val unversionedFiles = changeListManager.unversionedFilesPaths
+
+ val ignoredFiles = if (ChangesViewSettings.getInstance(project).showIgnored) changeListManager.ignoredFilePaths else emptyList()
+ return CommitChangesViewWithToolbarPanel.ModelProvider.ModelData(
+ changeLists,
+ unversionedFiles,
+ ignoredFiles,
+ ) { commitWorkflowHandler?.isActive == true }
+ }
+
+ override fun synchronizeInclusion(changeLists: List, unversionedFiles: List) {
+ commitWorkflowHandler?.synchronizeInclusion(changeLists, unversionedFiles)
+ }
+ }
+}
diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt
new file mode 100644
index 000000000000..283eb59320a2
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt
@@ -0,0 +1,83 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.changes.viewModel
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vcs.changes.InclusionModel
+import com.intellij.openapi.vcs.changes.LocalChangesListView
+import com.intellij.openapi.vcs.changes.ui.ChangesListView
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import javax.swing.JComponent
+
+// TODO IJPL-173924 Propper RPC-based implementation
+internal class BackendRemoteCommitChangesViewModel(private val project: Project) : BackendCommitChangesViewModel {
+ private var horizontal: Boolean = true
+ private val treeView: ChangesListView by lazy { LocalChangesListView(project) }
+ override val inclusionChanged = MutableSharedFlow()
+
+ private val inclusionModel = MutableStateFlow(null)
+
+ override fun setInclusionModel(model: InclusionModel?) {
+ inclusionModel.value = model
+ }
+
+ override fun initPanel() {
+ }
+
+ override fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) {
+ }
+
+ override fun setToolbarHorizontal(horizontal: Boolean) {
+ this.horizontal = horizontal
+ }
+
+ override fun getActions(): List = emptyList()
+
+ override fun isModelUpdateInProgress(): Boolean = false
+
+ override fun scheduleRefreshNow(callback: Runnable?) {
+ }
+
+ override fun scheduleDelayedRefresh() {
+ }
+
+ override fun setGrouping(groupingKey: String) {
+ }
+
+ override fun resetViewImmediatelyAndRefreshLater() {
+ }
+
+ override fun setShowCheckboxes(value: Boolean) {}
+
+ override fun getDisplayedChanges(): List = emptyList()
+
+ override fun getIncludedChanges(): List = emptyList()
+
+ override fun getDisplayedUnversionedFiles(): List = emptyList()
+
+ override fun getIncludedUnversionedFiles(): List = emptyList()
+
+ override fun expand(item: Any) {
+ }
+
+ override fun select(item: Any) {
+ }
+
+ override fun selectFirst(items: Collection) {
+ }
+
+ override fun selectFile(vFile: VirtualFile?) {
+ }
+
+ override fun selectChanges(changes: List) {
+ }
+
+ override fun getTree(): ChangesListView = treeView
+
+ override fun getPreferredFocusableComponent(): JComponent = treeView.preferredFocusedComponent
+}
diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangeListViewCommitPanel.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangeListViewCommitPanel.kt
index f41cc07cb1d7..37c9050947dd 100644
--- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangeListViewCommitPanel.kt
+++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangeListViewCommitPanel.kt
@@ -16,9 +16,7 @@ import com.intellij.openapi.vcs.changes.ui.ChangesListView
import com.intellij.openapi.vcs.changes.ui.EditChangelistSupport
import com.intellij.openapi.vcs.changes.ui.VcsTreeModelData.*
import com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation
-import com.intellij.ui.EditorTextComponent
import com.intellij.util.application
-import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.tree.TreeUtil.*
import org.jetbrains.annotations.ApiStatus
@@ -29,7 +27,7 @@ abstract class ChangeListViewCommitPanel @ApiStatus.Internal constructor(
project: Project,
private val changesView: ChangesListView,
) : NonModalCommitPanel(project), ChangesViewCommitWorkflowUi {
- private val progressPanel = ChangeListViewCommitProgressPanel(project, this, commitMessage.editorField)
+ private val progressPanel = CommitProgressPanel(project, this, commitMessage.editorField)
private val commitActions = commitActionsPanel.createActions()
private var rootComponent: JComponent? = null
@@ -98,11 +96,9 @@ abstract class ChangeListViewCommitPanel @ApiStatus.Internal constructor(
final override fun getIncludedUnversionedFiles(): List =
includedUnderTag(changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java)
- final override var inclusionModel: InclusionModel?
- get() = changesView.inclusionModel
- set(value) {
- changesView.setInclusionModel(value)
- }
+ override fun setInclusionModel(model: InclusionModel?) {
+ changesView.setInclusionModel(model)
+ }
final override val commitProgressUi: CommitProgressUi get() = progressPanel
@@ -122,23 +118,3 @@ abstract class ChangeListViewCommitPanel @ApiStatus.Internal constructor(
changesView.setInclusionListener(null)
}
}
-
-private class ChangeListViewCommitProgressPanel(
- project: Project,
- private val commitWorkflowUi: ChangesViewCommitWorkflowUi,
- commitMessage: EditorTextComponent,
-) : CommitProgressPanel(project) {
-
- private var oldInclusion: Set = emptySet()
-
- init {
- setup(commitWorkflowUi, commitMessage, JBUI.Borders.empty())
- }
-
- override fun inclusionChanged() {
- val newInclusion = commitWorkflowUi.inclusionModel?.getInclusion().orEmpty()
-
- if (oldInclusion != newInclusion) super.inclusionChanged()
- oldInclusion = newInclusion
- }
-}
diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt
index 8b6e4e48360a..b8371a5cc490 100644
--- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt
+++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt
@@ -2,37 +2,135 @@
package com.intellij.vcs.commit
import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.writeIntentReadAction
+import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.vcs.FilePath
import com.intellij.openapi.vcs.VcsBundle.message
+import com.intellij.openapi.vcs.changes.Change
import com.intellij.openapi.vcs.changes.ChangesViewManager
-import com.intellij.openapi.vcs.changes.ui.ChangesListView
-import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager
+import com.intellij.openapi.vcs.changes.InclusionModel
+import com.intellij.openapi.vcs.changes.LocalChangeList
+import com.intellij.openapi.vcs.changes.ui.*
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager.Companion.LOCAL_CHANGES
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager.Companion.getToolWindowFor
-import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManagerListener
-import com.intellij.openapi.vcs.changes.ui.isCommitToolWindowShown
import com.intellij.openapi.wm.ToolWindow
+import com.intellij.platform.util.coroutines.childScope
+import com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation
+import com.intellij.util.application
+import com.intellij.util.ui.UIUtil
+import com.intellij.vcs.VcsDisposable
+import com.intellij.vcs.changes.viewModel.BackendCommitChangesViewModel
import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import org.jetbrains.annotations.ApiStatus
+import javax.swing.JComponent
+import kotlin.properties.Delegates.observable
-class ChangesViewCommitPanel @ApiStatus.Internal constructor(
+class ChangesViewCommitPanel internal constructor(
project: Project,
- private val changesView: ChangesListView,
-) : ChangeListViewCommitPanel(project, changesView) {
+ private val viewModel: BackendCommitChangesViewModel,
+) : NonModalCommitPanel(project), ChangesViewCommitWorkflowUi {
private var isHideToolWindowOnCommit = false
+ private val progressPanel = CommitProgressPanel(project, this, commitMessage.editorField)
+ private val commitActions = commitActionsPanel.createActions()
+ private var rootComponent: JComponent? = null
+
init {
+ val titleUpdater = ChangesViewCommitTabTitleUpdater(project)
+ titleUpdater.initSubscription(this)
+ titleUpdater.updateTitle()
+
+ Disposer.register(this, commitMessage)
+ setProgressComponent(progressPanel)
+
+ for (support in EditChangelistSupport.EP_NAME.getExtensionList(project)) {
+ support.installSearch(commitMessage.editorField, commitMessage.editorField)
+ }
+
+ val scope = VcsDisposable.getInstance(project).coroutineScope.childScope("ChangesViewCommitPanel")
+ Disposer.register(this) { scope.cancel() }
+ viewModel.inclusionChanged.onEach { writeIntentReadAction { fireInclusionChanged() } }.launchIn(scope)
+
+ commitActionsPanel.isCommitButtonDefault = {
+ !progressPanel.isDumbMode && UIUtil.isFocusAncestor(rootComponent ?: component)
+ }
+
ChangesViewCommitTabTitleUpdater(project).initSubscription(this)
}
override val isActive: Boolean get() = component.isVisible
+ @ApiStatus.Internal
+ fun registerRootComponent(newRootComponent: JComponent) {
+ logger().assertTrue(rootComponent == null)
+ rootComponent = newRootComponent
+ commitActions.forEach { it.registerCustomShortcutSet(newRootComponent, this) }
+ }
+
+ override var editedCommit: EditedCommitPresentation? by observable(null) { _, _, newValue ->
+ ChangesViewManager.getInstanceEx(project).scheduleRefresh {
+ application.invokeLater { newValue?.let { expand(it) } }
+ }
+ }
+
+ override fun expand(item: Any) {
+ viewModel.expand(item)
+ }
+
+ override fun select(item: Any) {
+ viewModel.select(item)
+ }
+
+ override fun selectFirst(items: Collection) {
+ viewModel.selectFirst(items)
+ }
+
+ override fun setCompletionContext(changeLists: List) {
+ commitMessage.setChangesSupplier(ChangeListChangesSupplier(changeLists))
+ }
+
+ override fun getDisplayedChanges(): List = viewModel.getDisplayedChanges()
+ override fun getIncludedChanges(): List = viewModel.getIncludedChanges()
+
+ override fun getDisplayedUnversionedFiles(): List =
+ viewModel.getDisplayedUnversionedFiles()
+
+ override fun getIncludedUnversionedFiles(): List =
+ viewModel.getIncludedUnversionedFiles()
+
+ override fun setInclusionModel(model: InclusionModel?) {
+ viewModel.setInclusionModel(model)
+ }
+
+ override val commitProgressUi: CommitProgressUi get() = progressPanel
+
+ override fun endExecution(): Unit = closeEditorPreviewIfEmpty()
+
+ private fun closeEditorPreviewIfEmpty() {
+ val changesViewManager = ChangesViewManager.getInstance(project) as? ChangesViewManager ?: return
+ ChangesViewManager.getInstanceEx(project).scheduleRefresh {
+ application.invokeLater {
+ changesViewManager.closeEditorPreview(true)
+ }
+ }
+ }
+
+ override fun dispose() {
+ super.dispose()
+ viewModel.setShowCheckboxes(false)
+ }
+
override fun activate(): Boolean {
val toolWindow = getVcsToolWindow() ?: return false
val contentManager = ChangesViewContentManager.getInstance(project)
saveToolWindowState()
- changesView.isShowCheckboxes = true
+ viewModel.setShowCheckboxes(true)
component.isVisible = true
commitActionsPanel.isActive = true
@@ -49,7 +147,7 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor(
}
clearToolWindowState()
- changesView.isShowCheckboxes = false
+ viewModel.setShowCheckboxes(false)
component.isVisible = false
commitActionsPanel.isActive = false
@@ -81,9 +179,18 @@ private class ChangesViewCommitTabTitleUpdater(private val project: Project) : C
}
override fun toolWindowMappingChanged() {
+ updateTitle()
+ }
+
+ fun updateTitle() {
val tabContent = ChangesViewContentManager.getInstance(project).findContent(LOCAL_CHANGES)
if (tabContent != null) {
- tabContent.displayName = if (project.isCommitToolWindowShown) message("tab.title.commit") else message("local.changes.tab")
- }
+ val contentsCount = getToolWindowFor(project, LOCAL_CHANGES)?.contentManager?.contentCount ?: 0
+
+ tabContent.displayName = when {
+ contentsCount == 1 -> null
+ project.isCommitToolWindowShown -> message("tab.title.commit")
+ else ->message("local.changes.tab")
+ }}
}
}
diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt
index 749470195150..505844f4911b 100644
--- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt
+++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt
@@ -66,8 +66,8 @@ class ChangesViewCommitWorkflowHandler(
ui.addExecutorListener(this, this)
ui.addDataProvider(EdtNoGetDataProvider { sink -> uiDataSnapshot(sink) })
ui.addInclusionListener(this, this)
- ui.inclusionModel = inclusionModel
- Disposer.register(inclusionModel, Disposable { ui.inclusionModel = null })
+ ui.setInclusionModel(inclusionModel)
+ Disposer.register(inclusionModel, Disposable { ui.setInclusionModel(null) })
ui.setCompletionContext(changeListManager.changeLists)
setupDumbModeTracking()
diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowUi.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowUi.kt
index eb432cc8da88..b83bc3b31ee6 100644
--- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowUi.kt
+++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowUi.kt
@@ -12,7 +12,7 @@ interface ChangesViewCommitWorkflowUi : NonModalCommitWorkflowUi {
suspend fun refreshChangesViewBeforeCommit()
- var inclusionModel: InclusionModel?
+ fun setInclusionModel(model: InclusionModel?)
fun expand(item: Any)
fun select(item: Any)
diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/CommitProgressPanel.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/CommitProgressPanel.kt
index 983e36a642d1..eed7e9de4fe0 100644
--- a/platform/vcs-impl/src/com/intellij/vcs/commit/CommitProgressPanel.kt
+++ b/platform/vcs-impl/src/com/intellij/vcs/commit/CommitProgressPanel.kt
@@ -81,6 +81,10 @@ private fun JBLabel.setWarning(@NlsContexts.Label warningText: String) {
open class CommitProgressPanel(project: Project) : CommitProgressUi, InclusionListener, DocumentListener, Disposable {
private val scope = VcsDisposable.getInstance(project).coroutineScope.childScope("CommitProgressPanel", Dispatchers.EDT)
+ constructor(project: Project, commitWorkflowUi: CommitWorkflowUi, commitMessage: EditorTextComponent) : this(project) {
+ setup(commitWorkflowUi, commitMessage, empty())
+ }
+
private val taskInfo = CommitChecksTaskInfo()
private val progressFlow = MutableStateFlow(null)
private var progress: InlineCommitChecksProgressIndicator? by progressFlow::value
diff --git a/platform/vcs-impl/testSrc/com/intellij/vcsUtil/SingleTaskRunnerTest.kt b/platform/vcs-impl/testSrc/com/intellij/vcsUtil/SingleTaskRunnerTest.kt
index 4ccd0c69c7d0..543b38099827 100644
--- a/platform/vcs-impl/testSrc/com/intellij/vcsUtil/SingleTaskRunnerTest.kt
+++ b/platform/vcs-impl/testSrc/com/intellij/vcsUtil/SingleTaskRunnerTest.kt
@@ -5,18 +5,13 @@ import com.intellij.platform.util.coroutines.childScope
import com.intellij.platform.vcs.impl.shared.SingleTaskRunner
import com.intellij.testFramework.LoggedErrorProcessor
import com.intellij.testFramework.common.timeoutRunBlocking
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.TimeoutCancellationException
-import kotlinx.coroutines.cancel
+import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.withTimeout
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion
internal class SingleTaskRunnerTest {
@Test
@@ -43,41 +38,13 @@ internal class SingleTaskRunnerTest {
}
}
- @Test
- fun `test instant execution`() = timeoutRunBlocking {
- var counter = 0
- withRunner({ counter++ }, delay = Duration.INFINITE) {
- start()
- request()
- requestNow()
- awaitNotBusy()
- assertEquals(1, counter)
- }
- }
-
- @Test
- fun `test delayed execution after instant`() = timeoutRunBlocking {
- var counter = 0
- withRunner({ counter++ }, delay = Duration.INFINITE) {
- start()
- request()
- requestNow()
- awaitNotBusy()
- request()
- assertThrows {
- withTimeout(100) {
- awaitNotBusy()
- }
- }
- assertEquals(1, counter)
- }
- }
-
@Test
fun `test execution debounced`() = timeoutRunBlocking {
var counter = 0
+ val taskStarted = CompletableDeferred()
val runAllowed = MutableStateFlow(false)
withRunner({
+ taskStarted.complete(Unit)
runAllowed.first { it }
counter++
}) {
@@ -85,11 +52,13 @@ internal class SingleTaskRunnerTest {
repeat(10) {
request()
+ taskStarted.await()
}
runAllowed.value = true
awaitNotBusy()
- assertEquals(1, counter)
+ assertEquals(2, counter)
+ counter = 0
runAllowed.value = false
request()
assertThrows {
@@ -97,10 +66,10 @@ internal class SingleTaskRunnerTest {
awaitNotBusy()
}
}
- assertEquals(1, counter)
+ assertEquals(0, counter)
runAllowed.value = true
awaitNotBusy()
- assertEquals(2, counter)
+ assertEquals(1, counter)
}
}
@@ -131,11 +100,10 @@ internal class SingleTaskRunnerTest {
private inline fun CoroutineScope.withRunner(
noinline task: suspend () -> Unit,
- delay: Duration = Companion.ZERO,
consumer: SingleTaskRunner.() -> Unit,
) {
val cs = childScope("BG runner")
- SingleTaskRunner(cs, delay, task).also(consumer)
+ SingleTaskRunner(cs, task).also(consumer)
cs.cancel()
}
}
\ No newline at end of file
diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/mixedmode/XMixedModeCombinedDebugProcess.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/mixedmode/XMixedModeCombinedDebugProcess.kt
index b1b262aa1195..2580c0974fdc 100644
--- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/mixedmode/XMixedModeCombinedDebugProcess.kt
+++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/mixedmode/XMixedModeCombinedDebugProcess.kt
@@ -115,6 +115,11 @@ class XMixedModeCombinedDebugProcess(
}
override fun startPausing() {
+ if (!session.isMixedModeHighProcessReady) {
+ low.startPausing()
+ return
+ }
+
stateMachine.set(PauseRequested)
}
diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java
index e30820563c8b..ef410177f0a6 100644
--- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java
+++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java
@@ -7,6 +7,7 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.impl.InternalUICustomization;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
@@ -63,6 +64,10 @@ public class XDebuggerExpressionComboBox extends XDebuggerEditorBase {
initEditor(showEditor, languageInside);
fillComboBox();
myComponent = JBUI.Panels.simplePanel().addToTop(myComboBox);
+ var customization = InternalUICustomization.getInstance();
+ if (customization != null) {
+ customization.installEditorBackground(myComponent);
+ }
setExpression(myExpression);
}
@@ -204,7 +209,7 @@ public class XDebuggerExpressionComboBox extends XDebuggerEditorBase {
XDebuggerComboBoxEditor(boolean showMultiline, boolean languageInside) {
myDelegate = new EditorComboBoxEditor(getProject(), getEditorsProvider().getFileType()) {
@Override
- protected void onEditorCreate(EditorEx editor) {
+ protected void onEditorCreate(@NotNull EditorEx editor) {
editor.putUserData(DebuggerCopyPastePreprocessor.REMOVE_NEWLINES_ON_PASTE, true);
prepareEditor(editor);
if (showMultiline) {
diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt
index 23d7def4fe29..729394357d4c 100644
--- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt
+++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt
@@ -4,6 +4,7 @@ package com.jetbrains.builtInHelp
import com.intellij.ide.BrowserUtil
import com.intellij.ide.browsers.BrowserLauncher
import com.intellij.ide.browsers.WebBrowserManager
+import com.intellij.l10n.LocalizationStateService
import com.intellij.openapi.application.IdeUrlTrackingParametersProvider
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.help.HelpManager
@@ -17,7 +18,12 @@ import java.lang.String.valueOf
import java.net.*
import java.nio.charset.StandardCharsets
-private val LOG = Logger.getInstance(BuiltInHelpManager::class.java)
+val LOG: Logger = Logger.getInstance(BuiltInHelpManager::class.java)
+
+//Later we'll add more languages here, for now it's Chinese only
+val localesToUrls: Map = mapOf(
+ "zh-cn" to "zh-cn",
+)
class BuiltInHelpManager : HelpManager() {
@@ -64,9 +70,18 @@ class BuiltInHelpManager : HelpManager() {
}
else {
- val activeKeymapParam = if (activeKeymap == null) "" else "&keymap=${URLEncoder.encode(activeKeymap.presentableName, StandardCharsets.UTF_8)}"
+ val activeKeymapParam = if (activeKeymap == null) ""
+ else
+ "&keymap=${URLEncoder.encode(activeKeymap.presentableName, StandardCharsets.UTF_8)}"
- "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}/help/?${
+ //Determine if we need to try loading localized docs first
+ val selectedLocale = LocalizationStateService.getInstance()?.selectedLocale?.lowercase()
+ val langPart = if (localesToUrls.containsKey(selectedLocale)) {
+ "/" + localesToUrls[selectedLocale]
+ }
+ else ""
+
+ "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}${langPart}/help/?${
helpIdToUse
}$activeKeymapParam"
}
diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpContentRequestHandler.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpContentRequestHandler.kt
index f5fd103defcd..41ca294bceb9 100644
--- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpContentRequestHandler.kt
+++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpContentRequestHandler.kt
@@ -28,6 +28,8 @@ class HelpContentRequestHandler : HelpRequestHandlerBase() {
request: FullHttpRequest,
context: ChannelHandlerContext,
): Boolean {
+ val locale = getRequestLocalePath(request)
+
for (name: String in urlDecoder.parameters().keys) {
val param = urlDecoder.parameters()[name]
if (param != null && (param.isEmpty() || StringUtil.isEmpty(param[0]))) {
@@ -43,13 +45,14 @@ class HelpContentRequestHandler : HelpRequestHandlerBase() {
val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.PERMANENT_REDIRECT)
- var location = "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}/help/${
+ var location = "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}${if (locale.isNotEmpty()) "/$locale" else ""}/help/${
map.getUrlForId(
name
)
}"
- if (urlDecoder.parameters().containsKey("keymap")) location += "?keymap=${URLEncoder.encode(urlDecoder.parameters()["keymap"]!![0], StandardCharsets.UTF_8)}"
+ if (urlDecoder.parameters().containsKey("keymap"))
+ location += "?keymap=${URLEncoder.encode(urlDecoder.parameters()["keymap"]!![0], StandardCharsets.UTF_8)}"
response.headers().add(
"Location",
@@ -63,14 +66,12 @@ class HelpContentRequestHandler : HelpRequestHandlerBase() {
when (val resourceName: String = urlDecoder.path().substringAfterLast("/")) {
"config.json" -> {
- val configStream = ResourceUtil.getResourceAsStream(
- HelpContentRequestHandler::class.java.classLoader,
- "topics", "config.json"
- )
+
+ val configContent = Utils.getResourceWithFallback("topics", resourceName, locale)
@Suppress("UNCHECKED_CAST")
val configJson: LinkedHashMap = jacksonObjectMapper().readValue(
- configStream,
+ configContent,
LinkedHashMap::class.java
) as LinkedHashMap
diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt
index c4cf0cef2424..ca7d39f1e42e 100644
--- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt
+++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt
@@ -1,7 +1,6 @@
// 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 com.jetbrains.builtInHelp
-import com.intellij.util.ResourceUtil
import io.netty.channel.Channel
import io.netty.handler.codec.http.FullHttpRequest
import io.netty.handler.codec.http.HttpHeaders
@@ -18,11 +17,17 @@ import kotlin.io.path.nameWithoutExtension
abstract class HelpRequestHandlerBase : HttpRequestHandler() {
@NonNls
open val prefix: String = "/help/"
+ val supportedLocales: Set = setOf("zh-cn")
override fun isAccessible(request: HttpRequest): Boolean {
return super.isAccessible(request) && request.uri().contains(prefix)
}
+ fun getRequestLocalePath(request: HttpRequest): String {
+ val requestLocale = request.uri().substringBefore(prefix).substringAfter("/")
+ return if (supportedLocales.contains(requestLocale)) requestLocale else ""
+ }
+
protected fun sendResource(
resourceName: String,
resourceLocation: String,
@@ -32,20 +37,22 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() {
): Boolean {
val isImage = resourceLocation.contains("/img/")
+
+ //We don't ship dark images because of storage concerns, yet frontend might still request them, and to avoid 404 we provide fallback
val retrieveName = when (isImage) {
true -> {
val base = Path(resourceName)
val baseName = base.nameWithoutExtension
"""${baseName.substringBeforeLast("_dark")}.${base.extension}"""
}
+ //For non-images it's just a base name
else -> resourceName
}
- val resStream = ResourceUtil.getResourceAsStream(
- HelpRequestHandlerBase::class.java.classLoader,
- if (isImage) "images" else "topics", retrieveName
- )
+
+ val content = Utils.getResourceWithFallback(if (isImage) "images" else "topics",
+ retrieveName, getRequestLocalePath(request))
return sendData(
- resStream?.readAllBytes() ?: throw Exception("$resourceName not found in $resourceLocation via ${request.uri()}"), resourceName,
+ content ?: throw Exception("$resourceName not found in $resourceLocation via ${request.uri()}"), resourceName,
request, channel,
extraHeaders
)
diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt
index 3a8266ae2a16..c004b885ba80 100644
--- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt
+++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt
@@ -11,16 +11,24 @@ import org.jetbrains.annotations.NonNls
class HelpSearchRequestHandler : HelpRequestHandlerBase() {
@NonNls
override val prefix: String = "/search/"
- override fun process(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): Boolean {
+
+ override fun process(
+ urlDecoder: QueryStringDecoder,
+ request: FullHttpRequest,
+ context: ChannelHandlerContext,
+ ): Boolean {
val query = urlDecoder.parameters()["query"]?.get(0)
val maxHitsParam = urlDecoder.parameters()["maxHits"]?.get(0)
- sendData(HelpSearch.search(query, if (maxHitsParam != null) Integer.parseInt(maxHitsParam) else 100).encodeToByteArray(),
- "data.json",
- request,
- context.channel(),
- request.headers())
+ sendData(
+ HelpSearch.search(query,
+ if (maxHitsParam != null) Integer.parseInt(maxHitsParam) else 100,
+ getRequestLocalePath(request)).encodeToByteArray(),
+ "data.json",
+ request,
+ context.channel(),
+ request.headers())
return true
}
diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt
index 01693f6e0cab..87cbe9284945 100644
--- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt
+++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt
@@ -5,6 +5,7 @@ import com.intellij.credentialStore.CredentialAttributes
import com.intellij.credentialStore.Credentials
import com.intellij.ide.passwordSafe.PasswordSafe
import com.intellij.ide.util.PropertiesComponent
+import com.intellij.util.ResourceUtil
import com.jetbrains.builtInHelp.settings.SettingsPage
import org.jetbrains.annotations.NonNls
@@ -12,11 +13,43 @@ class Utils {
companion object {
@NonNls
private const val EMPTY_STRING = ""
+
@NonNls
- const val BASE_HELP_URL = "https://www.jetbrains.com/"
+ const val BASE_HELP_URL: String = "https://www.jetbrains.com/"
private val secureKey = CredentialAttributes("Web Help Bundle")
+ fun getResourceWithFallback(resPath: String, resName: String, localePath: String): ByteArray? {
+
+ val resStream = ResourceUtil.getResourceAsStream(
+ HelpRequestHandlerBase::class.java.classLoader,
+ "${if (localePath.isNotEmpty()) "$localePath/" else ""}$resPath", resName
+ )
+
+ return resStream.use { res ->
+ try {
+ res.readAllBytes()
+ }
+ catch (e: Exception) {
+ LOG.warn("$localePath resource failed", e)
+ //So we couldn't find any localized resource, try the default English one
+ ResourceUtil.getResourceAsStream(
+ HelpRequestHandlerBase::class.java.classLoader,
+ resPath, resName
+ ).use { res ->
+ try {
+ res.readAllBytes()
+ }
+ catch (e: Exception) {
+ LOG.warn("Fallback resource failed", e)
+ null
+ }
+ }
+ }
+ }
+ }
+
+
@JvmStatic
@NonNls
fun getStoredValue(key: SettingsPage.SettingKey, default: String): String {
@@ -30,14 +63,14 @@ class Utils {
}
@JvmStatic
- fun setStoredValue(key: SettingsPage.SettingKey, value: String?): Boolean {
+ fun setStoredValue(key: SettingsPage.SettingKey, value: String?) {
if (!key.second) {
PropertiesComponent.getInstance().setValue(key.first, value, EMPTY_STRING)
- return true
}
- val passwordSafe = PasswordSafe.instance
- passwordSafe.set(secureKey, Credentials(key.first, value))
- return true
+ else {
+ val passwordSafe = PasswordSafe.instance
+ passwordSafe.set(secureKey, Credentials(key.first, value))
+ }
}
}
}
\ No newline at end of file
diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/search/HelpSearch.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/search/HelpSearch.kt
index 05b6c89cabcc..9b970acabd71 100644
--- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/search/HelpSearch.kt
+++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/search/HelpSearch.kt
@@ -1,10 +1,12 @@
// Copyright 2000-2022 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.
+@file:OptIn(ExperimentalPathApi::class)
+
package com.jetbrains.builtInHelp.search
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.intellij.openapi.diagnostic.Logger
-import com.intellij.util.ResourceUtil
import com.intellij.util.io.safeOutputStream
+import com.jetbrains.builtInHelp.Utils
import org.apache.lucene.analysis.standard.StandardAnalyzer
import org.apache.lucene.index.DirectoryReader
import org.apache.lucene.queryparser.simple.SimpleQueryParser
@@ -17,11 +19,14 @@ import org.apache.lucene.store.NIOFSDirectory
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.NotNull
import java.io.BufferedReader
+import java.io.ByteArrayInputStream
import java.io.InputStreamReader
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
+import kotlin.io.path.ExperimentalPathApi
+import kotlin.io.path.deleteRecursively
class HelpSearch {
@@ -32,10 +37,10 @@ class HelpSearch {
private val analyzer: StandardAnalyzer = StandardAnalyzer()
@NotNull
- fun search(query: String?, maxHits: Int): String {
+ fun search(query: String?, maxHits: Int, locale: String): String {
if (query != null) {
- val indexDir: Path? = Files.createTempDirectory("search-index")
+ val indexDir: Path? = Files.createTempDirectory("${locale}-search-index")
var indexDirectory: NIOFSDirectory? = null
var reader: DirectoryReader? = null
@@ -44,22 +49,23 @@ class HelpSearch {
val indexDirPath = indexDir.toAbsolutePath().toString()
Files.createDirectories(indexDir)
- //Read required names from rlist and then load resources based off of them
- ResourceUtil.getResourceAsStream(HelpSearch::class.java.classLoader, "search", "rlist")
- .use { resourceList ->
- BufferedReader(InputStreamReader(resourceList)).useLines { lines ->
- lines.forEach { line ->
- val path = Paths.get(indexDirPath, line)
- ResourceUtil.getResourceAsStream(HelpSearch::class.java.classLoader,
- "search", line)
- ?.use { resourceStream ->
- path.safeOutputStream().use { resourceOutput ->
- resourceOutput.write(resourceStream.readAllBytes())
- }
- }
- }
+ //Read required names from the rlist file and then load resources based off of them
+ val searchList = Utils.getResourceWithFallback("search", "rlist", locale)
+ ?: return NOT_FOUND
+
+ BufferedReader(InputStreamReader(ByteArrayInputStream(searchList))).useLines { lines ->
+ lines.forEach { line ->
+ val path = Paths.get(indexDirPath, line)
+
+ val dataToDeploy = Utils.getResourceWithFallback("search", line, locale)
+ ?: return@forEach
+
+ path.safeOutputStream().use { resourceOutput ->
+ resourceOutput.write(dataToDeploy)
}
+
}
+ }
indexDirectory = NIOFSDirectory(indexDir)
reader = DirectoryReader.open(indexDirectory)
@@ -111,7 +117,7 @@ class HelpSearch {
finally {
indexDirectory?.close()
reader?.close()
- indexDir.toFile().deleteRecursively()
+ indexDir.deleteRecursively()
}
}
return NOT_FOUND
diff --git a/plugins/devkit/devkit-core/resources/inspectionDescriptions/PluginKeysAreProhibitedInPlatformRegistry.html b/plugins/devkit/devkit-core/resources/inspectionDescriptions/PluginKeysAreProhibitedInPlatformRegistry.html
new file mode 100644
index 000000000000..78dcc4d4e356
--- /dev/null
+++ b/plugins/devkit/devkit-core/resources/inspectionDescriptions/PluginKeysAreProhibitedInPlatformRegistry.html
@@ -0,0 +1,5 @@
+
+
+Highlights keys in `registry.properties` that are not allowed in the platform registry and must be moved to plugins.
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
index b55e00b76652..f72d620f78ab 100644
--- a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
+++ b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
@@ -551,7 +551,8 @@
key="inspection.content.module.namespace.name"/>
-
+
() {
return findDescriptorInModuleLibraries(currentModule, descriptorFileName)
}
+ override fun getPsiElement(resolvedValue: IdeaPlugin?): PsiElement? {
+ return resolvedValue?.xmlElement?.containingFile
+ }
+
+ override fun handleElementRename(genericValue: GenericDomValue?, context: ConvertContext?, newElementName: String) {
+ super.handleElementRename(genericValue, context, newElementName.removeSuffix(".xml"))
+ }
+
override fun toString(plugin: IdeaPlugin?, context: ConvertContext): String? {
if (plugin == null) return null
return getDisplayName(plugin)
diff --git a/plugins/devkit/devkit-core/src/inspections/RegistryPropertiesAnnotator.kt b/plugins/devkit/devkit-core/src/inspections/RegistryPropertiesAnnotator.kt
index 29434df24d81..ba42373d2f55 100644
--- a/plugins/devkit/devkit-core/src/inspections/RegistryPropertiesAnnotator.kt
+++ b/plugins/devkit/devkit-core/src/inspections/RegistryPropertiesAnnotator.kt
@@ -2,36 +2,27 @@
package org.jetbrains.idea.devkit.inspections
-import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils
-import com.intellij.lang.annotation.AnnotationHolder
-import com.intellij.lang.annotation.Annotator
-import com.intellij.lang.annotation.HighlightSeverity
-import com.intellij.lang.properties.psi.PropertiesFile
+import com.intellij.codeInspection.*
+import com.intellij.lang.properties.PropertiesImplUtil
import com.intellij.lang.properties.psi.impl.PropertyImpl
import com.intellij.lang.properties.psi.impl.PropertyKeyImpl
-import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.IntelliJProjectUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiFile
-import com.intellij.util.IncorrectOperationException
import com.intellij.util.PsiNavigateUtil
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import org.jetbrains.idea.devkit.DevKitBundle
import java.util.*
-@NonNls
internal const val REGISTRY_PROPERTIES_FILENAME = "registry.properties"
-
-@NonNls
internal const val DESCRIPTION_SUFFIX = ".description"
-
-@NonNls
internal const val RESTART_REQUIRED_SUFFIX = ".restartRequired"
internal fun isImplicitUsageKey(keyName: String): Boolean {
@@ -39,120 +30,120 @@ internal fun isImplicitUsageKey(keyName: String): Boolean {
keyName.endsWith(RESTART_REQUIRED_SUFFIX)
}
+private val PLUGIN_GROUP_NAMES = setOf(
+ "appcode", "cidr", "clion",
+ "cvs", "git", "github", "svn", "hg4idea", "tfs",
+ "dart", "markdown",
+ "java", "javac", "uast", "junit4", "dsm",
+ "js", "javascript", "typescript", "nodejs", "eslint", "jest",
+ "ruby", "rubymine",
+ "groovy", "grails", "python", "php",
+ "kotlin", "spring", "jupyter", "dataspell", "javafx",
+ "maven", "gradle", "android", "eclipse",
+ "rider", "compiler"
+)
+
internal fun isRegistryPropertiesFile(psiFile: PsiFile): Boolean {
return IntelliJProjectUtil.isIntelliJPlatformProject(psiFile.project) && psiFile.name == REGISTRY_PROPERTIES_FILENAME
}
-/**
- * Highlights key in `registry.properties` without matching `key.description` entry + corresponding quickfix.
- */
-private class RegistryPropertiesAnnotator : Annotator, DumbAware {
- @NonNls
- private val PLUGIN_GROUP_NAMES = setOf(
- "appcode", "cidr", "clion",
- "cvs", "git", "github", "svn", "hg4idea", "tfs",
- "dart", "markdown",
- "java", "javac", "uast", "junit4", "dsm",
- "js", "javascript", "typescript", "nodejs", "eslint", "jest",
- "ruby", "rubymine",
- "groovy", "grails", "python", "php",
- "kotlin", "spring", "jupyter", "dataspell", "javafx",
- "maven", "gradle", "android", "eclipse"
- )
+internal class RegistryPropertiesInspection : LocalInspectionTool() {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
+ if (!isRegistryPropertiesFile(holder.file)) return PsiElementVisitor.EMPTY_VISITOR
- override fun annotate(element: PsiElement, holder: AnnotationHolder) {
- if (element !is PropertyKeyImpl) return
+ val propertiesFile = PropertiesImplUtil.getPropertiesFile(holder.file)
- val file = holder.currentAnnotationSession.file
- if (!isRegistryPropertiesFile(file)) {
- return
- }
+ return object : PsiElementVisitor() {
+ override fun visitElement(element: PsiElement) {
+ if (element !is PropertyKeyImpl) return
- val propertyName = element.text
- if (isImplicitUsageKey(propertyName)) {
- return
- }
+ val propertyName = element.text
+ if (isImplicitUsageKey(propertyName)) {
+ return
+ }
- val groupName = propertyName.substringBefore('.').lowercase(Locale.getDefault())
+ val groupName = propertyName.substringBefore('.').lowercase(Locale.getDefault())
- if (PLUGIN_GROUP_NAMES.contains(groupName) ||
- propertyName.startsWith("editor.config.") ||
- propertyName.startsWith("debugger.kotlin.") ||
- propertyName.startsWith("debugger.enable.kotlin.") ||
- propertyName.startsWith("ide.java.") ||
- propertyName.startsWith("execution.java.") ||
- propertyName.startsWith("debugger.log.jdi.")) {
+ if (PLUGIN_GROUP_NAMES.contains(groupName) ||
+ propertyName.startsWith("editor.config.") ||
+ propertyName.startsWith("debugger.kotlin.") ||
+ propertyName.startsWith("debugger.enable.kotlin.") ||
+ propertyName.startsWith("ide.java.") ||
+ propertyName.startsWith("ide.jvm.") ||
+ propertyName.startsWith("idea.profiler.") ||
+ propertyName.startsWith("execution.java.") ||
+ propertyName.startsWith("debugger.log.jdi.")) {
- holder.newAnnotation(HighlightSeverity.ERROR, DevKitBundle.message("registry.properties.annotator.plugin.keys.use.ep"))
- .withFix(ShowEPDeclarationIntention(propertyName)).create()
- }
+ holder.registerProblem(
+ element,
+ DevKitBundle.message("registry.properties.annotator.plugin.keys.use.ep"),
+ ShowEPDeclarationIntention(propertyName, element)
+ )
+ }
- val propertiesFile = file as PropertiesFile
- val descriptionProperty = propertiesFile.findPropertyByKey(propertyName + DESCRIPTION_SUFFIX)
- if (descriptionProperty == null) {
- holder.newAnnotation(HighlightSeverity.WARNING,
- DevKitBundle.message("registry.properties.annotator.key.no.description.key", propertyName))
- .withFix(AddDescriptionKeyIntention(propertyName)).create()
+ val descriptionProperty = propertiesFile?.findPropertyByKey(propertyName + DESCRIPTION_SUFFIX)
+ if (descriptionProperty == null) {
+ holder.registerProblem(
+ element,
+ DevKitBundle.message("registry.properties.annotator.key.no.description.key", propertyName),
+ ProblemHighlightType.WARNING,
+ AddDescriptionKeyIntention(propertyName, element)
+ )
+ }
+ }
}
}
+}
- private class ShowEPDeclarationIntention(private val propertyName: String) : IntentionAction, DumbAware {
- override fun startInWriteAction(): Boolean = false
+private class ShowEPDeclarationIntention(private val propertyName: String, element: PsiElement) : LocalQuickFixOnPsiElement(element), DumbAware {
+ override fun startInWriteAction(): Boolean = false
+ override fun getFamilyName(): String = DevKitBundle.message("registry.properties.annotator.show.ep.family.name")
+ override fun getText(): String = DevKitBundle.message("registry.properties.annotator.show.ep.name", propertyName)
- override fun generatePreview(project: Project, editor: Editor, psiFile: PsiFile): IntentionPreviewInfo {
- return IntentionPreviewInfo.EMPTY
+ override fun invoke(project: Project, psiFile: PsiFile, startElement: PsiElement, endElement: PsiElement) {
+ val propertiesFile = psiFile.let { PropertiesImplUtil.getPropertiesFile(it) } ?: return
+ val defaultValue = propertiesFile.findPropertyByKey(propertyName)?.value
+ val description = propertiesFile.findPropertyByKey(propertyName + DESCRIPTION_SUFFIX)?.value
+ @NonNls var restartRequiredText = ""
+ if (propertiesFile.findPropertyByKey(propertyName + RESTART_REQUIRED_SUFFIX) != null) {
+ restartRequiredText = "restartRequired=\"true\""
}
- override fun getFamilyName(): String = DevKitBundle.message("registry.properties.annotator.show.ep.family.name")
-
- override fun isAvailable(project: Project, editor: Editor?, psiFile: PsiFile?): Boolean = true
-
- override fun getText(): String = DevKitBundle.message("registry.properties.annotator.show.ep.name", propertyName)
-
- override fun invoke(project: Project, editor: Editor?, psiFile: PsiFile?) {
- val propertiesFile = psiFile as PropertiesFile
- val defaultValue = propertiesFile.findPropertyByKey(propertyName)!!.value
- val description = propertiesFile.findPropertyByKey(propertyName + DESCRIPTION_SUFFIX)?.value
- @NonNls var restartRequiredText = ""
- if (propertiesFile.findPropertyByKey(propertyName + RESTART_REQUIRED_SUFFIX) != null) {
- restartRequiredText = "restartRequired=\"true\""
- }
-
- val epText = """
+ val epText = """
""".trimIndent()
- Messages.showMultilineInputDialog(project,
- DevKitBundle.message("registry.properties.annotator.show.ep.message"),
- DevKitBundle.message("registry.properties.annotator.show.ep.title"),
- epText, null, null)
- }
+ Messages.showMultilineInputDialog(project,
+ DevKitBundle.message("registry.properties.annotator.show.ep.message"),
+ DevKitBundle.message("registry.properties.annotator.show.ep.title"),
+ epText, null, null)
}
- private class AddDescriptionKeyIntention(private val myPropertyName: String) : IntentionAction, DumbAware {
+ override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY
+}
- @Nls
- override fun getText(): String = DevKitBundle.message("registry.properties.annotator.add.description.text", myPropertyName)
+private class AddDescriptionKeyIntention(private val myPropertyName: String, element: PsiElement) : LocalQuickFixOnPsiElement(element), DumbAware {
+ override fun startInWriteAction(): Boolean = true
+ override fun getText(): @Nls String = DevKitBundle.message("registry.properties.annotator.add.description.text", myPropertyName)
+ override fun getFamilyName(): @Nls String = DevKitBundle.message("registry.properties.annotator.add.description.family.name")
- @Nls
- override fun getFamilyName(): String = DevKitBundle.message("registry.properties.annotator.add.description.family.name")
+ override fun invoke(project: Project, psiFile: PsiFile, startElement: PsiElement, endElement: PsiElement) {
+ val propertiesFile = psiFile.let { PropertiesImplUtil.getPropertiesFile(it) } ?: return
- override fun isAvailable(project: Project, editor: Editor, psiFile: PsiFile): Boolean = true
+ val originalProperty = propertiesFile.findPropertyByKey(myPropertyName)
+ val descriptionProperty = propertiesFile.addPropertyAfter(myPropertyName + DESCRIPTION_SUFFIX, "Description", originalProperty)
- @Throws(IncorrectOperationException::class)
- override fun invoke(project: Project, editor: Editor, psiFile: PsiFile) {
- val propertiesFile = psiFile as PropertiesFile
-
- val originalProperty = propertiesFile.findPropertyByKey(myPropertyName) as PropertyImpl?
- val descriptionProperty = propertiesFile.addPropertyAfter(myPropertyName + DESCRIPTION_SUFFIX, "Description", originalProperty)
-
- val valueNode = (descriptionProperty.psiElement as PropertyImpl).valueNode!!
- if (!IntentionPreviewUtils.isPreviewElement(valueNode.psi)) {
- PsiNavigateUtil.navigate(valueNode.psi)
+ val psi = descriptionProperty.psiElement
+ if (psi is PropertyImpl) {
+ val valuePsi = psi.valueNode?.psi ?: psi
+ if (!IntentionPreviewUtils.isPreviewElement(valuePsi)) {
+ PsiNavigateUtil.navigate(valuePsi)
}
}
-
- override fun startInWriteAction(): Boolean = true
+ else if (!IntentionPreviewUtils.isPreviewElement(psi)) {
+ PsiNavigateUtil.navigate(psi)
+ }
}
+ override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY
}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-core/src/inspections/eel/UseOptimizedEelFunctions.kt b/plugins/devkit/devkit-core/src/inspections/eel/UseOptimizedEelFunctions.kt
index c637650b915a..28cc83d6efb6 100644
--- a/plugins/devkit/devkit-core/src/inspections/eel/UseOptimizedEelFunctions.kt
+++ b/plugins/devkit/devkit-core/src/inspections/eel/UseOptimizedEelFunctions.kt
@@ -14,12 +14,16 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.uast.UastVisitorAdapter
+import org.jetbrains.annotations.ApiStatus
+import org.jetbrains.annotations.VisibleForTesting
+import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.uast.*
import org.jetbrains.uast.generate.getUastElementFactory
import org.jetbrains.uast.generate.replace
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
-@Suppress("HardCodedStringLiteral")
+@ApiStatus.Internal
+@VisibleForTesting
class UseOptimizedEelFunctions : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
UastVisitorAdapter(object : AbstractUastNonRecursiveVisitor() {
@@ -72,13 +76,13 @@ class UseOptimizedEelFunctions : LocalInspectionTool() {
val methodPsi = node.methodIdentifier?.sourcePsi ?: return
if (fqn == "java.nio.file.Files.readAllBytes") {
holder.registerProblem(
- methodPsi, "Works ineffectively with remote Eel", ProblemHighlightType.WARNING,
+ methodPsi, DevKitBundle.message("inspection.message.works.ineffectively.with.remote.eel"), ProblemHighlightType.WARNING,
ReplaceWithEelFunction(holder.project, "com.intellij.platform.eel.fs.EelFiles", "readAllBytes"),
)
}
if (fqn == "java.nio.file.Files.readString") {
holder.registerProblem(
- methodPsi, "Works ineffectively with remote Eel", ProblemHighlightType.WARNING,
+ methodPsi, DevKitBundle.message("inspection.message.works.ineffectively.with.remote.eel"), ProblemHighlightType.WARNING,
ReplaceWithEelFunction(holder.project, "com.intellij.platform.eel.fs.EelFiles", "readString"),
)
}
@@ -90,7 +94,7 @@ class UseOptimizedEelFunctions : LocalInspectionTool() {
node.valueArgumentCount == 1
) {
holder.registerProblem(
- methodPsi, "Works ineffectively with remote Eel", ProblemHighlightType.WARNING,
+ methodPsi, DevKitBundle.message("inspection.message.works.ineffectively.with.remote.eel"), ProblemHighlightType.WARNING,
ReplaceWithEelFunction(holder.project, "com.intellij.platform.eel.fs.EelFileUtils", "deleteRecursively"),
)
}
@@ -108,7 +112,7 @@ class UseOptimizedEelFunctions : LocalInspectionTool() {
?.name
override fun getFamilyName(): @IntentionFamilyName String =
- "Replace it with Eel API"
+ DevKitBundle.message("intention.family.name.replace.it.with.eel.api")
override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo {
doTheJob(project, previewDescriptor, preview = true)
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/findUsages/PluginModuleFindUsagesTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/findUsages/PluginModuleFindUsagesTest.kt
index 666a3085a231..3da3fe36d01a 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/findUsages/PluginModuleFindUsagesTest.kt
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/findUsages/PluginModuleFindUsagesTest.kt
@@ -13,8 +13,8 @@ class PluginModuleFindUsagesTest : JavaCodeInsightFixtureTestCase() {
fun `test find modules`() {
addModule("intellij.test.module1")
- myFixture.addXmlFile("intellij.test.module1/intellij.test.module1.xml", """
-
+ val moduleFile = myFixture.addXmlFile("intellij.test.module1/intellij.test.module1.xml", """
+
""".trimIndent()
)
@@ -37,7 +37,7 @@ class PluginModuleFindUsagesTest : JavaCodeInsightFixtureTestCase() {
""".trimIndent()
)
- val usages = myFixture.testFindUsages("intellij.test.module1/intellij.test.module1.xml")
+ val usages = myFixture.findUsages(moduleFile)
assertThat(usages)
.hasSize(2)
}
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/DevkitInspectionsRegistrationCheckTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/DevkitInspectionsRegistrationCheckTest.java
index e3c61f10b721..e65a8e631b5f 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/DevkitInspectionsRegistrationCheckTest.java
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/DevkitInspectionsRegistrationCheckTest.java
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase {
- private static final int EXPECTED_INSPECTIONS_NUMBER = 87;
+ private static final int EXPECTED_INSPECTIONS_NUMBER = 88;
/**
* Inspections that are finished and intentionally disabled.
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/PluginConfigReferenceTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/PluginConfigReferenceTest.java
index efc49df5a69b..6b1d933dec1d 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/PluginConfigReferenceTest.java
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/PluginConfigReferenceTest.java
@@ -71,7 +71,7 @@ public class PluginConfigReferenceTest extends JavaCodeInsightFixtureTestCase {
assertEquals(AllIcons.Nodes.PluginRestart, plugin.getIcon());
final LookupElementPresentation properties = getLookupElementPresentation("vcs.showConsole");
- assertEquals(" Show 'Console' tab in VCS toolwindow that logs all write-commands performed by IDE.", properties.getTailText());
+ assertEquals(" Show the 'Console' tab in the VCS toolwindow that logs all write-commands performed by IDE.", properties.getTailText());
assertEquals("true", properties.getTypeText());
assertEquals(AllIcons.Nodes.Plugin, properties.getIcon());
}
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/rename/PluginModuleRenameTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/rename/PluginModuleRenameTest.kt
new file mode 100644
index 000000000000..76f4bb9001bb
--- /dev/null
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/rename/PluginModuleRenameTest.kt
@@ -0,0 +1,73 @@
+// 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.idea.devkit.rename
+
+import com.intellij.psi.PsiFile
+import com.intellij.testFramework.PsiTestUtil
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture
+import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
+import org.assertj.core.api.Assertions.assertThat
+import org.intellij.lang.annotations.Language
+import org.jetbrains.idea.devkit.module.PluginModuleType
+
+class PluginModuleRenameTest : JavaCodeInsightFixtureTestCase() {
+
+ fun `test rename module file`() {
+ addModule("intellij.test.module1")
+ val moduleFile = myFixture.addXmlFile("intellij.test.module1/intellij.test.module1.xml", """
+
+
+ """.trimIndent()
+ )
+
+ addModule("intellij.test.plugin1")
+ myFixture.addXmlFile("intellij.test.plugin1/plugin.xml", """
+
+
+
+
+
+ """.trimIndent()
+ )
+
+ addModule("intellij.test.plugin2")
+ myFixture.addXmlFile("intellij.test.plugin2/plugin.xml", """
+
+
+
+
+
+ """.trimIndent()
+ )
+
+ myFixture.renameElement(moduleFile, "intellij.test.module.renamed")
+ myFixture.checkResult("intellij.test.plugin1/plugin.xml", """
+
+
+
+
+
+ """.trimIndent(), true)
+ myFixture.checkResult("intellij.test.plugin2/plugin.xml", """
+
+
+
+
+
+ """.trimIndent(), true
+ )
+ }
+
+ private fun addModule(moduleName: String) {
+ PsiTestUtil.addModule(
+ project,
+ PluginModuleType.getInstance(),
+ moduleName,
+ myFixture.tempDirFixture.findOrCreateDir(moduleName)
+ )
+ }
+
+ private fun CodeInsightTestFixture.addXmlFile(relativePath: String, @Language("XML") fileText: String): PsiFile {
+ return this.addFileToProject(relativePath, fileText)
+ }
+
+}
diff --git a/plugins/eclipse/resources/META-INF/plugin.xml b/plugins/eclipse/resources/META-INF/plugin.xml
index 420f2b9439ed..3e6d2a9d03e0 100644
--- a/plugins/eclipse/resources/META-INF/plugin.xml
+++ b/plugins/eclipse/resources/META-INF/plugin.xml
@@ -6,8 +6,8 @@
- Open Eclipse project in IntelliJ IDEA
- Import Eclipse project into IntelliJ IDEA
+ Open an Eclipse project in IntelliJ IDEA
+ Import an Eclipse project into IntelliJ IDEA
Export IntelliJ IDEA project into Eclipse
Convert IntelliJ IDEA module to Eclipse-compatible format
Synchronize Eclipse project files
@@ -48,6 +48,7 @@
+