From 3673990fab9f5e0bdfe75b122f529c4613927ff2 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 20 Oct 2025 00:43:15 +0200 Subject: [PATCH 001/105] workaround for a missing fleet.rpc.server module GitOrigin-RevId: da6cb74013fc3bedb219cc28252737734ecd2c99 --- .../jetbrains/intellij/build/impl/PlatformModules.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt index 58d998924f76..57f50d3a70ce 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt @@ -288,7 +288,7 @@ internal suspend fun createPlatformLayout(projectLibrariesUsedByPlugins: SortedS for (item in productPluginContentModules) { val iterator = filteredExplicit.iterator() while (iterator.hasNext()) { - if (item.moduleName == iterator.next().moduleName) { + if (item.moduleName == iterator.next().moduleName && !PRODUCT_MODULE_IMPL_COMPOSITION.values.any { it.contains(item.moduleName) }) { // todo - error instead of warn Span.current().addEvent("product module MUST NOT BE explicitly specified: ${item.moduleName}") iterator.remove() @@ -710,10 +710,12 @@ private suspend fun collectAndEmbedProductModules(root: Element, xIncludePathRes moduleSet = moduleSet, ) ) - PRODUCT_MODULE_IMPL_COMPOSITION.get(moduleName)?.let { - it.mapTo(result) { subModuleName -> - ModuleItem(moduleName = subModuleName, relativeOutputFile = relativeOutFile, reason = ModuleIncludeReasons.PRODUCT_MODULES, moduleSet = moduleSet) - } + PRODUCT_MODULE_IMPL_COMPOSITION.get(moduleName)?.let { list -> + list + .filter { !context.productProperties.productLayout.productImplementationModules.contains(it) } + .mapTo(result) { subModuleName -> + ModuleItem(moduleName = subModuleName, relativeOutputFile = relativeOutFile, reason = ModuleIncludeReasons.PRODUCT_MODULES, moduleSet = moduleSet) + } } check(moduleElement.content.isEmpty()) From 63d041ec57e28b7ee54c7595923550eeb9d08f99 Mon Sep 17 00:00:00 2001 From: Leonid Shalupov Date: Mon, 20 Oct 2025 12:36:39 +0200 Subject: [PATCH 002/105] .gitignore for expects compiler plugin GitOrigin-RevId: 64038df1078c3bd7fc2c73e558f2e1f548760499 --- fleet/compiler-plugins/expects/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 fleet/compiler-plugins/expects/.gitignore diff --git a/fleet/compiler-plugins/expects/.gitignore b/fleet/compiler-plugins/expects/.gitignore new file mode 100644 index 000000000000..01d8fb504537 --- /dev/null +++ b/fleet/compiler-plugins/expects/.gitignore @@ -0,0 +1,4 @@ +out/ +build/ +.idea/ +.gradle/ \ No newline at end of file From 74372f58fc9e0daba73adf516953e73d4fa0326a Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 20 Oct 2025 09:59:21 +0300 Subject: [PATCH 003/105] [terminal] IJPL-211122 Add executedCommand field to TerminalCommandBlock It can be more accurate to use the command reported from the shell integration than to get it by offsets from the output model. But getting it by offsets is still useful, because it can be used before command is started. GitOrigin-RevId: 8fbecfb87e4e175357b033bbbcd33dede8f3879b --- .../backend/StateAwareTerminalSession.kt | 2 +- .../TerminalShellIntegrationEventsHandler.kt | 2 +- .../session/dto/TerminalCommandBlockDto.kt | 3 +++ .../view/shellIntegration/TerminalBlocks.kt | 15 ++++++++---- .../impl/TerminalBlocksModelImpl.kt | 1 + .../impl/TerminalCommandBlockImpl.kt | 1 + .../impl/TerminalShellIntegrationImpl.kt | 4 ++-- .../tests/reworked/TerminalBlocksModelTest.kt | 24 ++++++++++++------- 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt index eda9057fddc2..ba4c79fb9872 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt @@ -192,7 +192,7 @@ internal class StateAwareTerminalSession( } is TerminalCommandStartedEvent -> { blocksModel.updateActiveCommandBlock { block -> - block.copy(outputStartOffset = outputModel.cursorOffset) + block.copy(outputStartOffset = outputModel.cursorOffset, executedCommand = event.command) } } is TerminalCommandFinishedEvent -> { diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt index c8d171b849e8..34c9e7a962a5 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt @@ -61,7 +61,7 @@ internal class TerminalShellIntegrationEventsHandler( is TerminalCommandStartedEvent -> { withContext(edtContext) { outputModelController.applyPendingUpdates() - getIntegrationOrThrow().onCommandStarted(outputModelController.model.cursorOffset) + getIntegrationOrThrow().onCommandStarted(outputModelController.model.cursorOffset, event.command) } } is TerminalCommandFinishedEvent -> { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt index 1d67f03e7221..46d66576d886 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt @@ -16,6 +16,7 @@ data class TerminalCommandBlockDto( val endOffset: Long, val commandStartOffset: Long?, val outputStartOffset: Long?, + val executedCommand: String?, val exitCode: Int?, ) @@ -27,6 +28,7 @@ fun TerminalCommandBlock.toDto(): TerminalCommandBlockDto { endOffset = endOffset.toAbsolute(), commandStartOffset = commandStartOffset?.toAbsolute(), outputStartOffset = outputStartOffset?.toAbsolute(), + executedCommand = executedCommand, exitCode = exitCode, ) } @@ -39,6 +41,7 @@ fun TerminalCommandBlockDto.toCommandBlock(): TerminalCommandBlock { endOffset = TerminalOffset.of(endOffset), commandStartOffset = commandStartOffset?.let { TerminalOffset.of(it) }, outputStartOffset = outputStartOffset?.let { TerminalOffset.of(it) }, + executedCommand = executedCommand, exitCode = exitCode, ) } \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt index 28e6ae5b0875..c6b3689a5daa 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt @@ -19,6 +19,12 @@ interface TerminalBlockBase { interface TerminalCommandBlock : TerminalBlockBase { val commandStartOffset: TerminalOffset? val outputStartOffset: TerminalOffset? + + /** + * Should be non-null if the command was started to execute. + * It is the command text reported by the shell integration right before it is started. + */ + val executedCommand: String? val exitCode: Int? } @@ -26,13 +32,14 @@ interface TerminalCommandBlock : TerminalBlockBase { * @param model regular terminal output model (not alternative one) * @return the text without trailing whitespaces that was typed at the place of the command. * Can return null if this block doesn't contain the command - * or if the command became partially output of model bounds because of trimming. + * or if the command became partial out of model bounds because of trimming. * - * **Note**: the return text might be not exactly the command, because currently, - * we do not take the right part of the prompt and inline completion grey text (provided by the shell plugins) into account. + * **Note**: the returned text might be different from [TerminalCommandBlock.executedCommand], because currently, + * we do not detect the right part of the prompt and inline completion grey text (provided by the shell plugins). + * But this method can be called before command execution is started to get the currently typed command. */ @ApiStatus.Experimental -fun TerminalCommandBlock.getCommandText(model: TerminalOutputModel): String? { +fun TerminalCommandBlock.getTypedCommandText(model: TerminalOutputModel): String? { val start = commandStartOffset ?: return null val end = outputStartOffset ?: endOffset if (start < model.startOffset || start > model.endOffset diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt index bfe9400f1bef..1b4316a735e9 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt @@ -133,6 +133,7 @@ class TerminalBlocksModelImpl(private val outputModel: TerminalOutputModel, pare endOffset = outputModel.endOffset, commandStartOffset = null, outputStartOffset = null, + executedCommand = null, exitCode = null, ) } diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt index 176343bd0290..4fe940e58b41 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt @@ -13,5 +13,6 @@ data class TerminalCommandBlockImpl( override val endOffset: TerminalOffset, override val commandStartOffset: TerminalOffset?, override val outputStartOffset: TerminalOffset?, + override val executedCommand: String?, override val exitCode: Int?, ) : TerminalCommandBlock \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt index 4ff314933965..1b636de19bbb 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt @@ -42,9 +42,9 @@ class TerminalShellIntegrationImpl( mutableOutputStatus.value = TypingCommand } - fun onCommandStarted(offset: TerminalOffset) { + fun onCommandStarted(offset: TerminalOffset, command: String) { blocksModel.updateActiveCommandBlock { block -> - block.copy(outputStartOffset = offset) + block.copy(outputStartOffset = offset, executedCommand = command) } mutableOutputStatus.value = ExecutingCommand diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt index 4bd6081d2e29..29006e3ad4f9 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt @@ -36,7 +36,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(null, block.commandStartOffset) assertEquals(null, block.outputStartOffset) assertEquals(null, block.exitCode) - assertEquals(null, block.getCommandText(outputModel)) + assertEquals(null, block.getTypedCommandText(outputModel)) assertEquals(null, block.getOutputText(outputModel)) assertEquals(false, block.wasExecuted) } @@ -57,7 +57,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(null, block.commandStartOffset) assertEquals(null, block.outputStartOffset) assertEquals(null, block.exitCode) - assertEquals(null, block.getCommandText(outputModel)) + assertEquals(null, block.getTypedCommandText(outputModel)) assertEquals(null, block.getOutputText(outputModel)) assertEquals(false, block.wasExecuted) } @@ -129,7 +129,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(1, blocksModel.blocks.size) val block = blocksModel.activeBlock as TerminalCommandBlock assertEquals("myCommand\n", outputModel.getTextAsString(block.commandStartOffset!!, block.outputStartOffset!!)) - assertEquals("myCommand", block.getCommandText(outputModel)) + assertEquals("myCommand", block.getTypedCommandText(outputModel)) assertEquals("someOutput\n\n", outputModel.getTextAsString(block.outputStartOffset!!, block.endOffset)) assertEquals("someOutput", block.getOutputText(outputModel)) assertEquals(true, block.wasExecuted) @@ -152,7 +152,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(2, blocksModel.blocks.size) val firstBlock = blocksModel.blocks[0] as TerminalCommandBlock - assertEquals("abortedCommand", firstBlock.getCommandText(outputModel)) + assertEquals("abortedCommand", firstBlock.getTypedCommandText(outputModel)) assertEquals(null, firstBlock.getOutputText(outputModel)) assertEquals(false, firstBlock.wasExecuted) } @@ -174,7 +174,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(2, blocksModel.blocks.size) val firstBlock = blocksModel.blocks[0] as TerminalCommandBlock - assertEquals("myCommand", firstBlock.getCommandText(outputModel)) + assertEquals("myCommand", firstBlock.getTypedCommandText(outputModel)) assertEquals("", firstBlock.getOutputText(outputModel)) assertEquals(true, firstBlock.wasExecuted) } @@ -194,13 +194,13 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(1, blocksModel.blocks.size) val firstBlock = blocksModel.blocks[0] as TerminalCommandBlock - assertEquals("myCommand", firstBlock.getCommandText(outputModel)) + assertEquals("myCommand", firstBlock.getTypedCommandText(outputModel)) assertEquals("someOutput...", firstBlock.getOutputText(outputModel)) assertEquals(true, firstBlock.wasExecuted) } @Test - fun `getCommandText returns null if command start is trimmed`() = runBlocking(Dispatchers.EDT) { + fun `getTypedCommandText returns null if command start is trimmed`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel(30) val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) @@ -217,7 +217,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(2, blocksModel.blocks.size) val firstBlock = blocksModel.blocks[0] as TerminalCommandBlock - assertEquals(null, firstBlock.getCommandText(outputModel)) + assertEquals(null, firstBlock.getTypedCommandText(outputModel)) assertEquals("someOutput", firstBlock.getOutputText(outputModel)) assertEquals(true, firstBlock.wasExecuted) } @@ -240,7 +240,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { assertEquals(2, blocksModel.blocks.size) val firstBlock = blocksModel.blocks[0] as TerminalCommandBlock - assertEquals(null, firstBlock.getCommandText(outputModel)) + assertEquals(null, firstBlock.getTypedCommandText(outputModel)) assertEquals("6789-123456789", firstBlock.getOutputText(outputModel)) assertEquals(true, firstBlock.wasExecuted) } @@ -506,6 +506,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 10, outputStartOffset = outputModel.startOffset + 20, endOffset = outputModel.startOffset + 31, + executedCommand = null, exitCode = null ) assertEquals(expectedFirstBlock, state.blocks[0]) @@ -516,6 +517,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 46, outputStartOffset = null, endOffset = outputModel.startOffset + 47, + executedCommand = null, exitCode = null ) assertEquals(expectedSecondBlock, state.blocks[1]) @@ -532,6 +534,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 10, outputStartOffset = outputModel.startOffset + 20, endOffset = outputModel.startOffset + 31, + executedCommand = null, exitCode = null ) val secondBlock = TerminalCommandBlockImpl( @@ -540,6 +543,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 46, outputStartOffset = null, endOffset = outputModel.startOffset + 47, + executedCommand = null, exitCode = null ) val state = TerminalBlocksModelState( @@ -588,6 +592,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = newOutputModel.startOffset + 10, outputStartOffset = newOutputModel.startOffset + 20, endOffset = newOutputModel.startOffset + 31, + executedCommand = null, exitCode = null ) assertEquals(expectedFirstBlock, state.blocks[0]) @@ -598,6 +603,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = newOutputModel.startOffset + 46, outputStartOffset = null, endOffset = newOutputModel.startOffset + 47, + executedCommand = null, exitCode = null ) assertEquals(expectedSecondBlock, state.blocks[1]) From 7cbc4cadf58ebf9b84825ca4122dd8bafb23991a Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 20 Oct 2025 10:37:17 +0300 Subject: [PATCH 004/105] [terminal] IJPL-211122 Add workingDirectory field to TerminalCommandBlock GitOrigin-RevId: 6c98cdf10810eac20885251b624166224e370613 --- .../backend/StateAwareTerminalSession.kt | 2 +- .../TerminalShellIntegrationEventsHandler.kt | 3 + .../frontend/view/impl/TerminalViewImpl.kt | 4 +- .../session/dto/TerminalCommandBlockDto.kt | 3 + .../view/shellIntegration/TerminalBlocks.kt | 1 + .../impl/TerminalBlocksModelImpl.kt | 8 ++- .../impl/TerminalCommandBlockImpl.kt | 1 + .../impl/TerminalShellIntegrationImpl.kt | 4 +- .../tests/reworked/TerminalBlocksModelTest.kt | 59 +++++++++++-------- .../completion/TerminalCompletionFixture.kt | 2 +- 10 files changed, 59 insertions(+), 28 deletions(-) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt index ba4c79fb9872..52312c904818 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt @@ -80,7 +80,7 @@ internal class StateAwareTerminalSession( alternateBufferModel = MutableTerminalOutputModelImpl(alternateBufferDocument, maxOutputLength = 0) alternateBufferHyperlinkFacade = BackendTerminalHyperlinkFacade(project, hyperlinkScope, alternateBufferModel, isInAlternateBuffer = true) - blocksModel = TerminalBlocksModelImpl(outputModel, coroutineScope.asDisposable()) + blocksModel = TerminalBlocksModelImpl(outputModel, sessionModel, coroutineScope.asDisposable()) coroutineScope.launch(CoroutineName("StateAwareTerminalSession: models updating")) { merge( diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt index 34c9e7a962a5..e02240eeaa9b 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jetbrains.plugins.terminal.block.reworked.TerminalAliasesStorage +import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel import org.jetbrains.plugins.terminal.session.* import org.jetbrains.plugins.terminal.session.dto.toState import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration @@ -16,6 +17,7 @@ import java.util.concurrent.CompletableFuture internal class TerminalShellIntegrationEventsHandler( private val outputModelController: TerminalOutputModelController, + private val sessionModel: TerminalSessionModel, private val shellIntegrationFuture: CompletableFuture, private val aliasesStorage: TerminalAliasesStorage, private val coroutineScope: CoroutineScope, @@ -82,6 +84,7 @@ internal class TerminalShellIntegrationEventsHandler( private fun initShellIntegration() { val integration = TerminalShellIntegrationImpl( outputModelController.model, + sessionModel, coroutineScope.childScope("TerminalShellIntegration") ) shellIntegrationFuture.complete(integration) diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt index 626d842675d1..879278a63a50 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt @@ -92,7 +92,8 @@ class TerminalViewImpl( @VisibleForTesting val shellIntegrationFuture: CompletableFuture = CompletableFuture() - private val sessionModel: TerminalSessionModel + @VisibleForTesting + val sessionModel: TerminalSessionModel private val encodingManager: TerminalKeyEncodingManager private val controller: TerminalSessionController @@ -261,6 +262,7 @@ class TerminalViewImpl( ) val shellIntegrationEventsHandler = TerminalShellIntegrationEventsHandler( outputModelController, + sessionModel, shellIntegrationFuture, terminalAliasesStorage, coroutineScope.childScope("TerminalShellIntegrationEventsHandler"), diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt index 46d66576d886..2a8c496dd239 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt @@ -16,6 +16,7 @@ data class TerminalCommandBlockDto( val endOffset: Long, val commandStartOffset: Long?, val outputStartOffset: Long?, + val workingDirectory: String?, val executedCommand: String?, val exitCode: Int?, ) @@ -28,6 +29,7 @@ fun TerminalCommandBlock.toDto(): TerminalCommandBlockDto { endOffset = endOffset.toAbsolute(), commandStartOffset = commandStartOffset?.toAbsolute(), outputStartOffset = outputStartOffset?.toAbsolute(), + workingDirectory = workingDirectory, executedCommand = executedCommand, exitCode = exitCode, ) @@ -41,6 +43,7 @@ fun TerminalCommandBlockDto.toCommandBlock(): TerminalCommandBlock { endOffset = TerminalOffset.of(endOffset), commandStartOffset = commandStartOffset?.let { TerminalOffset.of(it) }, outputStartOffset = outputStartOffset?.let { TerminalOffset.of(it) }, + workingDirectory = workingDirectory, executedCommand = executedCommand, exitCode = exitCode, ) diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt index c6b3689a5daa..641f3b357f3f 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/TerminalBlocks.kt @@ -20,6 +20,7 @@ interface TerminalCommandBlock : TerminalBlockBase { val commandStartOffset: TerminalOffset? val outputStartOffset: TerminalOffset? + val workingDirectory: String? /** * Should be non-null if the command was started to execute. * It is the command text reported by the shell integration right before it is started. diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt index 1b4316a735e9..897af36cf2af 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt @@ -3,6 +3,7 @@ package org.jetbrains.plugins.terminal.view.shellIntegration.impl import com.intellij.openapi.Disposable import com.intellij.util.EventDispatcher +import com.intellij.util.text.nullize import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.plugins.terminal.block.reworked.* @@ -10,7 +11,11 @@ import org.jetbrains.plugins.terminal.session.TerminalBlocksModelState import org.jetbrains.plugins.terminal.view.shellIntegration.* @ApiStatus.Internal -class TerminalBlocksModelImpl(private val outputModel: TerminalOutputModel, parentDisposable: Disposable) : TerminalBlocksModel { +class TerminalBlocksModelImpl( + private val outputModel: TerminalOutputModel, + private val sessionModel: TerminalSessionModel, + parentDisposable: Disposable, +) : TerminalBlocksModel { @VisibleForTesting var blockIdCounter: Int = 0 @@ -133,6 +138,7 @@ class TerminalBlocksModelImpl(private val outputModel: TerminalOutputModel, pare endOffset = outputModel.endOffset, commandStartOffset = null, outputStartOffset = null, + workingDirectory = sessionModel.terminalState.value.currentDirectory.nullize(), // it can be empty string, so use nullize() executedCommand = null, exitCode = null, ) diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt index 4fe940e58b41..6da60a350c5a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalCommandBlockImpl.kt @@ -13,6 +13,7 @@ data class TerminalCommandBlockImpl( override val endOffset: TerminalOffset, override val commandStartOffset: TerminalOffset?, override val outputStartOffset: TerminalOffset?, + override val workingDirectory: String?, override val executedCommand: String?, override val exitCode: Int?, ) : TerminalCommandBlock \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt index 1b636de19bbb..a36a1386bbda 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalShellIntegrationImpl.kt @@ -11,15 +11,17 @@ import kotlinx.coroutines.flow.asStateFlow import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel +import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel import org.jetbrains.plugins.terminal.view.shellIntegration.* import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalOutputStatus.* @ApiStatus.Internal class TerminalShellIntegrationImpl( private val outputModel: TerminalOutputModel, + sessionModel: TerminalSessionModel, coroutineScope: CoroutineScope, ) : TerminalShellIntegration { - override val blocksModel: TerminalBlocksModelImpl = TerminalBlocksModelImpl(outputModel, coroutineScope.asDisposable()) + override val blocksModel: TerminalBlocksModelImpl = TerminalBlocksModelImpl(outputModel, sessionModel, coroutineScope.asDisposable()) private val dispatcher = EventDispatcher.create(TerminalCommandExecutionListener::class.java) diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt index 29006e3ad4f9..cd63dc315b3a 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt @@ -9,6 +9,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import org.jetbrains.plugins.terminal.block.reworked.MutableTerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset +import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel +import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModelImpl import org.jetbrains.plugins.terminal.session.TerminalBlocksModelState import org.jetbrains.plugins.terminal.view.shellIntegration.* import org.jetbrains.plugins.terminal.view.shellIntegration.impl.TerminalBlocksModelImpl @@ -25,7 +27,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `initial block is created from empty output model`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) assertEquals(1, blocksModel.blocks.size) val block = blocksModel.activeBlock as TerminalCommandBlock @@ -46,7 +48,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { val outputModel = TerminalTestUtil.createOutputModel() outputModel.update(0, "some welcome text\n") - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) assertEquals(1, blocksModel.blocks.size) val block = blocksModel.activeBlock as TerminalCommandBlock @@ -65,7 +67,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `initial block is replaced with a new one`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") @@ -81,7 +83,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `command start offset is set correctly after prompt finish`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(outputModel.startOffset + 0L) @@ -100,7 +102,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `block end offset is updated on command typing`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(outputModel.startOffset + 0L) @@ -116,7 +118,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `output start offset is set correctly after command start`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(outputModel.startOffset + 0L) @@ -139,7 +141,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `aborted command block contains valid command and no output`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(TerminalOffset.ZERO) @@ -160,7 +162,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `getOutputText of command with no output is empty`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(TerminalOffset.ZERO) @@ -182,7 +184,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `getOutputText of running command returns current output`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(TerminalOffset.ZERO) @@ -202,7 +204,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `getTypedCommandText returns null if command start is trimmed`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel(30) - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(TerminalOffset.ZERO) @@ -225,7 +227,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `getOutputText returns partial output if output start is trimmed`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel(25) - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n") blocksModel.startNewBlock(TerminalOffset.ZERO) @@ -248,7 +250,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `new block is created after next prompt start`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(outputModel.startOffset + 0L) @@ -272,7 +274,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `initial block is left if there was some text`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") outputModel.update(0, "welcomeText\n\n\n") @@ -294,7 +296,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `blocks are preserved after full text replace`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) // Prepare outputModel.update(0, "\n\n\n") @@ -330,7 +332,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `single block is left after full replace`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) // Prepare outputModel.update(0, "\n\n\n") @@ -362,7 +364,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `single block is left after clear`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) // Prepare outputModel.update(0, "\n\n\n") @@ -390,7 +392,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `single block is left after clear (with trimming)`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel(100) - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) // Prepare outputModel.update(0, "\n\n\n") @@ -419,7 +421,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `block positions stay the same after output start trimmed`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel(maxLength = 30) - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") blocksModel.startNewBlock(TerminalOffset.ZERO) @@ -449,7 +451,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `block was removed after output start trimmed`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel(maxLength = 30) - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) outputModel.update(0, "\n\n\n") outputModel.update(0, "welcome12\n\n\n") @@ -480,7 +482,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `check state is dumped correctly`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) // Prepare outputModel.update(0, "\n\n\n") @@ -506,6 +508,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 10, outputStartOffset = outputModel.startOffset + 20, endOffset = outputModel.startOffset + 31, + workingDirectory = null, executedCommand = null, exitCode = null ) @@ -517,6 +520,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 46, outputStartOffset = null, endOffset = outputModel.startOffset + 47, + workingDirectory = null, executedCommand = null, exitCode = null ) @@ -526,7 +530,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `check state is restored correctly`() = runBlocking(Dispatchers.EDT) { val outputModel = TerminalTestUtil.createOutputModel() - val blocksModel = TerminalBlocksModelImpl(outputModel, testRootDisposable) + val blocksModel = createBlocksModel(outputModel) val firstBlock = TerminalCommandBlockImpl( id = TerminalBlockIdImpl(1), @@ -534,6 +538,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 10, outputStartOffset = outputModel.startOffset + 20, endOffset = outputModel.startOffset + 31, + workingDirectory = null, executedCommand = null, exitCode = null ) @@ -543,6 +548,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = outputModel.startOffset + 46, outputStartOffset = null, endOffset = outputModel.startOffset + 47, + workingDirectory = null, executedCommand = null, exitCode = null ) @@ -563,7 +569,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { @Test fun `check state is restored correctly after applying dumped state`() = runBlocking(Dispatchers.EDT) { val sourceOutputModel = TerminalTestUtil.createOutputModel() - val sourceBlocksModel = TerminalBlocksModelImpl(sourceOutputModel, testRootDisposable) + val sourceBlocksModel = createBlocksModel(sourceOutputModel) // Prepare sourceOutputModel.update(0, "\n\n\n") @@ -580,7 +586,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { // Test val state = sourceBlocksModel.dumpState() val newOutputModel = TerminalTestUtil.createOutputModel() - val newBlocksModel = TerminalBlocksModelImpl(newOutputModel, testRootDisposable) + val newBlocksModel = createBlocksModel(newOutputModel) newBlocksModel.restoreFromState(state) assertEquals(3, state.blockIdCounter) @@ -592,6 +598,7 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = newOutputModel.startOffset + 10, outputStartOffset = newOutputModel.startOffset + 20, endOffset = newOutputModel.startOffset + 31, + workingDirectory = null, executedCommand = null, exitCode = null ) @@ -603,12 +610,18 @@ internal class TerminalBlocksModelTest : BasePlatformTestCase() { commandStartOffset = newOutputModel.startOffset + 46, outputStartOffset = null, endOffset = newOutputModel.startOffset + 47, + workingDirectory = null, executedCommand = null, exitCode = null ) assertEquals(expectedSecondBlock, state.blocks[1]) } + private fun createBlocksModel(outputModel: TerminalOutputModel): TerminalBlocksModelImpl { + val sessionModel = TerminalSessionModelImpl() + return TerminalBlocksModelImpl(outputModel, sessionModel, testRootDisposable) + } + private fun MutableTerminalOutputModel.getTextAsString(startOffset: TerminalOffset, endOffset: TerminalOffset): String { return getText(startOffset, endOffset).toString() } diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt index 34072ae0412a..ae935cc173e8 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt @@ -52,7 +52,7 @@ class TerminalCompletionFixture(val project: Project, val testRootDisposable: Di terminalScope.cancel() } view = TerminalViewImpl(project, JBTerminalSystemSettingsProvider(), null, terminalScope) - val shellIntegration = TerminalShellIntegrationImpl(outputModel, terminalScope.childScope("TerminalShellIntegration")) + val shellIntegration = TerminalShellIntegrationImpl(outputModel, view.sessionModel, terminalScope.childScope("TerminalShellIntegration")) view.shellIntegrationFuture.complete(shellIntegration) shellIntegration.onPromptFinished(TerminalOffset.ZERO) // To make TerminalOutputStatus = TypingCommand From 7e7747d3a912f14d5984571de0562e140628baa4 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Thu, 16 Oct 2025 11:59:30 +0300 Subject: [PATCH 005/105] [platform UI] IJPL-156745 Extract WindowMouseListenerSupport To avoid having a mess of isWaylandToolkit() ifs, let's extract the actual implementation into a separate class and make it two different implementations. This change is copy-paste refactoring only. The next step is to split the code into smaller pieces so each one of them could be overridden independently. GitOrigin-RevId: a439a4c0739fb2b055440936bb8744fcc039bc7a --- .../com/intellij/ui/WindowMouseListener.java | 157 +++++++---------- .../intellij/ui/WindowMouseListenerSupport.kt | 160 ++++++++++++++++++ .../com/intellij/ui/WindowResizeListener.java | 1 + 3 files changed, 219 insertions(+), 99 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java index 0307d009aa0c..6d8684377156 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; + private 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..30afd761a8b2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt @@ -0,0 +1,160 @@ +// 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 location: Point? = null + private var viewBounds: Rectangle? = null + private var mouseButton = 0 + private var wasDragged = false + + var leftMouseButtonOnly = false + + val isBusy: Boolean + get() = location != 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 (start) wasDragged = false // reset dragged state when mouse pressed + + if (location == null) { + val content = source.getContent(event) + val view = source.getView(content) + if (view != null) { + 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)) + if (start && cursorType != Cursor.CUSTOM_CURSOR) { + mouseButton = event.getButton() + location = event.locationOnScreen + viewBounds = view.bounds + event.consume() + } + } + } + } + + /** + * Processes moving/resizing and stops it if not `mouseMove`. + */ + fun process(event: MouseEvent, mouseMove: Boolean) { + if (event.isConsumed) return + if (mouseMove) wasDragged = true // set dragged state when mouse dragged + + val viewBounds = this.viewBounds + val location = this.location + if (location != null && viewBounds != null) { + val content = source.getContent(event) + val view = source.getView(content) + if (mouseMove && cursorType == Cursor.DEFAULT_CURSOR && jbrMoveSupported(view)) { + // Enter in move mode only after mouse move, so double click is supported + JBR.getWindowMove().startMovingTogetherWithMouse(view as Window, mouseButton) + this.location = null + this.viewBounds = null + return + } + + if (view != null) { + val bounds = Rectangle(viewBounds) + var dx: Int = event.xOnScreen - location.x + var dy: Int = event.yOnScreen - location.y + if (cursorType == Cursor.DEFAULT_CURSOR && view is Frame) { + val state = view.extendedState + if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_HORIZ, state)) dx = 0 + if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_VERT, state)) dy = 0 + } + source.updateBounds(bounds, view, dx, dy) + 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) { + view.reshape(bounds.x, bounds.y, bounds.width, bounds.height) + view.invalidate() + view.validate() + view.repaint() + if (moved) source.notifyMoved() + if (resized) source.notifyResized() + } + } + } + if (!mouseMove) { + source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) + this.location = null + if (wasDragged) { + this.viewBounds = null // no mouse clicked when mouse released after mouse dragged + } + } + event.consume() + } + else if (!mouseMove && viewBounds != null) { + this.viewBounds = null // consume mouse clicked for consumed mouse released if no mouse dragged + event.consume() + } + } +} + +internal class RegularWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) { + override fun moveAfterMouseRelease(): Boolean = false + + override fun jbrMoveSupported(component: Component?): Boolean { + return (component is Frame || component is Dialog) && JBR.isWindowMoveSupported() + } +} + +internal class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) { + override fun moveAfterMouseRelease(): Boolean = true + + 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..720652822edf 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java +++ b/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java @@ -130,6 +130,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); } From 310e25ff1ac6057148af20285e13e65e11e195cf Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Thu, 16 Oct 2025 12:17:18 +0300 Subject: [PATCH 006/105] [platform UI] IJPL-156745 Refactor WindowMouseListenerSupport Extract a few functions here and there to make it more manageable, and to make some of them overridable. GitOrigin-RevId: d7feb975dcca8ebd0e6c1400a071eba30adce981 --- .../intellij/ui/WindowMouseListenerSupport.kt | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt index 30afd761a8b2..8b53d7b6441a 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt @@ -45,7 +45,8 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse private var location: Point? = null private var viewBounds: Rectangle? = null private var mouseButton = 0 - private var wasDragged = false + private var expectMouseReleased = false + private var expectMouseClicked = false var leftMouseButtonOnly = false @@ -64,82 +65,127 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse return } - if (start) wasDragged = false // reset dragged state when mouse pressed + if (start) { + expectMouseReleased = true + expectMouseClicked = true + } if (location == null) { val content = source.getContent(event) val view = source.getView(content) if (view != null) { - 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)) + updateCursor(event, content, view) if (start && cursorType != Cursor.CUSTOM_CURSOR) { - mouseButton = event.getButton() - location = event.locationOnScreen - viewBounds = view.bounds + start(event, view) event.consume() } } } } + 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) { + mouseButton = event.getButton() + location = event.locationOnScreen + viewBounds = view.bounds + } + /** * Processes moving/resizing and stops it if not `mouseMove`. */ fun process(event: MouseEvent, mouseMove: Boolean) { if (event.isConsumed) return - if (mouseMove) wasDragged = true // set dragged state when mouse dragged + if (mouseMove) expectMouseClicked = false // after dragging starts, there will be only one "released" event val viewBounds = this.viewBounds val location = this.location if (location != null && viewBounds != null) { 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)) { - // Enter in move mode only after mouse move, so double click is supported - JBR.getWindowMove().startMovingTogetherWithMouse(view as Window, mouseButton) - this.location = null - this.viewBounds = null + enterJbrMoveMode(view) return } if (view != null) { val bounds = Rectangle(viewBounds) - var dx: Int = event.xOnScreen - location.x - var dy: Int = event.yOnScreen - location.y + val delta = computeOffsetFromInitialLocation(event) if (cursorType == Cursor.DEFAULT_CURSOR && view is Frame) { val state = view.extendedState - if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_HORIZ, state)) dx = 0 - if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_VERT, state)) dy = 0 + if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_HORIZ, state)) delta.x = 0 + if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_VERT, state)) delta.y = 0 } - source.updateBounds(bounds, view, dx, dy) + 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) { - view.reshape(bounds.x, bounds.y, bounds.width, bounds.height) - view.invalidate() - view.validate() - view.repaint() - if (moved) source.notifyMoved() - if (resized) source.notifyResized() + setViewBounds(view, bounds, moved, resized) } } } if (!mouseMove) { - source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) - this.location = null - if (wasDragged) { - this.viewBounds = null // no mouse clicked when mouse released after mouse dragged - } + resetCursor(content) } event.consume() } - else if (!mouseMove && viewBounds != null) { - this.viewBounds = null // consume mouse clicked for consumed mouse released if no mouse dragged + if (isBusy && !mouseMove) { + handleStopEvent(event) + } + } + + private fun computeOffsetFromInitialLocation(event: MouseEvent): Point { + val location = this.location + checkNotNull(location) { "Must not be called when inactive" } + 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 resetCursor(content: Component) { + source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) + } + + private fun handleStopEvent(event: MouseEvent) { + if (expectMouseReleased && event.id == MouseEvent.MOUSE_RELEASED) { + expectMouseReleased = false event.consume() } + if (expectMouseClicked && event.id == MouseEvent.MOUSE_CLICKED) { + expectMouseClicked = false + event.consume() + } + if (!expectMouseReleased && !expectMouseClicked) { + stop() + } + } + + private fun stop() { + this.location = null + this.viewBounds = null + expectMouseClicked = false + expectMouseReleased = false + } + + private fun enterJbrMoveMode(view: Component?) { + JBR.getWindowMove().startMovingTogetherWithMouse(view as Window, mouseButton) + stop() } } From 167cb34b458fa962d03b105768a03c7725c67122 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Thu, 16 Oct 2025 12:37:55 +0300 Subject: [PATCH 007/105] [platform UI] IJPL-156745 Introduce the wayland.true.popups key There are pros and cons to using true popups, so we need a key to experiment with them. For now location restoring is disabled (even though it's one of the main benefits of true popups) because we first need to ensure location correctness. Otherwise, an attempt to restore the location may cause the popup to immediately disappear, making it permanently unusable. GitOrigin-RevId: 7597f770f339fc8f7dfbb14d4cf467f0d8e09035 --- .../src/com/intellij/ui/popup/AbstractPopup.java | 10 +++++++++- .../src/META-INF/PlatformExtensions.xml | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) 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..0249f8985d27 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; @@ -2833,6 +2836,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 +2913,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-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 1a389f96a3aa..e13412ced01c 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -1804,6 +1804,7 @@ provider="com.intellij.ide.minimap.settings.MinimapConfigurableProvider" key="settings.name" bundle="messages.MinimapBundle"/> + From 7eac367fdc050fa90f470de0fe4b640c07816368 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Thu, 16 Oct 2025 16:53:51 +0300 Subject: [PATCH 008/105] [platform UI] IJPL-156745 Make true popups resizable on Wayland Now that we have extracted the implementation from the listener, it isn't that hard. We just need to use the new pointer API to track the movement. Also, there's a special handling for the mouse released event because it arrives on Wayland no matter what and closes the popup immediately after resizing, so we have to consume it to avoid this. GitOrigin-RevId: 6e93b976d570d106e8482ee7b6fa258597e820cd --- .../intellij/ui/WindowMouseListenerSupport.kt | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt index 8b53d7b6441a..de32c3f72e02 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt @@ -45,9 +45,9 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse private var location: Point? = null private var viewBounds: Rectangle? = null private var mouseButton = 0 - private var expectMouseReleased = false - private var expectMouseClicked = false - + protected var expectMouseReleased = false + protected var expectMouseClicked = false + var leftMouseButtonOnly = false val isBusy: Boolean @@ -92,14 +92,19 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse mouseButton = event.getButton() location = event.locationOnScreen viewBounds = view.bounds + 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) { if (event.isConsumed) return - if (mouseMove) expectMouseClicked = false // after dragging starts, there will be only one "released" event + if (mouseMove) { + onDraggingStarted() + } val viewBounds = this.viewBounds val location = this.location @@ -140,8 +145,10 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse handleStopEvent(event) } } - - private fun computeOffsetFromInitialLocation(event: MouseEvent): Point { + + protected abstract fun onDraggingStarted() + + protected open fun computeOffsetFromInitialLocation(event: MouseEvent): Point { val location = this.location checkNotNull(location) { "Must not be called when inactive" } val dx: Int = event.xOnScreen - location.x @@ -189,16 +196,55 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse } } -internal class RegularWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) { +private class RegularWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) { override fun moveAfterMouseRelease(): Boolean = false + override fun onDraggingStarted() { + 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() } } -internal class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) { - override fun moveAfterMouseRelease(): Boolean = true +private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) { + private var isTruePopup = false + private var dx = 0 + private var dy = 0 + private var expectReleasedEvent = false + private var expectClickedEvent = false + + override fun onStarted(event: MouseEvent, view: Component) { + isTruePopup = view is Window && view.type == Window.Type.POPUP + dx = 0 + dy = 0 + if (isRelativeMovementMode()) { + @Suppress("UsePropertyAccessSyntax") + JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset() + expectClickedEvent = true + expectReleasedEvent = true + } + } + + override fun onDraggingStarted() { } // on Wayland, whether dragging has started or not, both "released" and "clicked" events will arrive + + override fun computeOffsetFromInitialLocation(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(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 From db9b9bc46ac9d7d9f1b89d2620c41b6362dc683d Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Fri, 17 Oct 2025 10:52:51 +0300 Subject: [PATCH 009/105] [platform UI] IJPL-156745 Introduce WindowMouseListenerSupport.getResizeCursor Because it's Wayland-dependent, it belongs to the new support class. We could've moved the rest there too, but it's mostly environment-agnostic, except for a tiny part of coordinate calculation. GitOrigin-RevId: a44c77b8f09f742b8c3af91d97234479d7226b32 --- .../com/intellij/ui/WindowMouseListener.java | 2 +- .../intellij/ui/WindowMouseListenerSupport.kt | 42 +++++++++++++++++++ .../com/intellij/ui/WindowResizeListener.java | 27 ++---------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java index 6d8684377156..a1bf14a7c673 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java @@ -19,7 +19,7 @@ import static com.intellij.ui.WindowMouseListenerSupportKt.createWindowMouseList @ApiStatus.Internal public abstract class WindowMouseListener extends MouseAdapter implements MouseInputListener { protected final Component myContent; - private final WindowMouseListenerSupport support = createWindowMouseListenerSupport(new SourceAdapter()); + protected final WindowMouseListenerSupport support = createWindowMouseListenerSupport(new SourceAdapter()); private class SourceAdapter implements WindowMouseListenerSource { @Override diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt index de32c3f72e02..13181b3df260 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt @@ -9,6 +9,7 @@ import java.awt.* import java.awt.event.MouseEvent import javax.swing.SwingUtilities + @ApiStatus.Internal internal interface WindowMouseListenerSource { fun getContent(event: MouseEvent): Component @@ -146,6 +147,33 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse } } + @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 + } + protected abstract fun onDraggingStarted() protected open fun computeOffsetFromInitialLocation(event: MouseEvent): Point { @@ -229,6 +257,20 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc override fun onDraggingStarted() { } // 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 { + // 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(event: MouseEvent): Point { if (isRelativeMovementMode()) { @Suppress("UsePropertyAccessSyntax") diff --git a/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java b/platform/platform-impl/src/com/intellij/ui/WindowResizeListener.java index 720652822edf..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; } From 5806102399b5839e395f2f216aee27911128692b Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Fri, 17 Oct 2025 11:02:24 +0300 Subject: [PATCH 010/105] [platform UI] IJPL-156745 Allow resizing on all sides for true Wayland popups GitOrigin-RevId: a0237fe541b2e67acde516ddb40f2a7a2b99d28b --- .../src/com/intellij/ui/WindowMouseListenerSupport.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt index 13181b3df260..1b6ab92f5837 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt @@ -66,6 +66,7 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse return } + if (start) { expectMouseReleased = true expectMouseClicked = true @@ -75,6 +76,7 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse 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) @@ -84,6 +86,8 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse } } + 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)) @@ -243,8 +247,11 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc private var expectReleasedEvent = false private var expectClickedEvent = false - override fun onStarted(event: MouseEvent, view: Component) { + 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()) { @@ -258,6 +265,7 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc override fun onDraggingStarted() { } // 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) { From 469f83403fd82b8b81624d54023c03051924041a Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Fri, 17 Oct 2025 14:39:42 +0300 Subject: [PATCH 011/105] [platform UI] IJPL-155826 Fit popup menus into the window on Wayland As Wayland doesn't let us position popups outside the main window, try to fit them into the window in such a way that they don't intersect with their parent popups. GitOrigin-RevId: 5c5d14205b2853514bdf3abd6171a3f860a17cb8 --- .../openapi/actionSystem/impl/ActionMenu.kt | 23 ++++- .../src/com/intellij/util/ui/WaylandUtil.kt | 91 +++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 platform/util/ui/src/com/intellij/util/ui/WaylandUtil.kt 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/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() From f38f3bf8465fa81af4286f918335027d9ad43fe5 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Fri, 17 Oct 2025 15:07:38 +0300 Subject: [PATCH 012/105] [platform UI] IJPL-155826 Fix popup fit-to-parent on Wayland The direct parent (which can be another popup) can be small, and we usually don't want one popup obscuring another. Therefore, first fit the popup into the top-level bounds, then convert to the direct parent bounds. GitOrigin-RevId: 2d46cbb4b9f24f95fa176374eb25cb70149e5c87 --- .../com/intellij/ui/popup/AbstractPopup.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) 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 0249f8985d27..f9273dff3834 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -1529,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(); } From 3184bed34bd48a65a1d1549493c31e4637f23c82 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Fri, 17 Oct 2025 15:08:20 +0300 Subject: [PATCH 013/105] [platform UI] IJPL-155826 Fix child action popup positioning Reuse the same fix as for popup menus. We just need a bit back-and-forth converting here because popups use screen coordinates. GitOrigin-RevId: 64a6eb843306c6e9966da51707eaa8648e5f0fd9 --- .../com/intellij/ui/popup/list/ListPopupImpl.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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(); From 0810b21fa184660ecfbe296ecb1189407e4d3edd Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Mon, 20 Oct 2025 12:30:13 +0300 Subject: [PATCH 014/105] [platform UI] IJPL-156745 Fix WindowMouseListenerSupport event consuming The recent refactoring introduced a regression: sometimes the listener would fail to finish the session properly, which would cause weird bugs like sudden window resize on a single click. Fix by refactoring the support a bit, introducing the drag session. This session has a clear starting point: on the first click, and it always stops on receiving the expected released / clicked events regardless of even if they're consumed. Once the session is over, the move / drag handlers do nothing. GitOrigin-RevId: e3710d753ec9c499bdc59b72624086284a45c42a --- .../intellij/ui/WindowMouseListenerSupport.kt | 169 +++++++++--------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt index 1b6ab92f5837..56a24866b1f5 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListenerSupport.kt @@ -43,16 +43,12 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse @get:JdkConstants.CursorType var cursorType: Int = 0 private set - private var location: Point? = null - private var viewBounds: Rectangle? = null - private var mouseButton = 0 - protected var expectMouseReleased = false - protected var expectMouseClicked = false + private var currentSession: WindowListenerSession? = null var leftMouseButtonOnly = false val isBusy: Boolean - get() = location != null + get() = currentSession != null protected abstract fun moveAfterMouseRelease(): Boolean @@ -66,13 +62,7 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse return } - - if (start) { - expectMouseReleased = true - expectMouseClicked = true - } - - if (location == null) { + if (!isBusy) { val content = source.getContent(event) val view = source.getView(content) if (view != null) { @@ -94,9 +84,11 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse } private fun start(event: MouseEvent, view: Component) { - mouseButton = event.getButton() - location = event.locationOnScreen - viewBounds = view.bounds + currentSession = WindowListenerSession( + event.locationOnScreen, + view.bounds, + event.getButton(), + ) onStarted(event, view) } @@ -106,47 +98,13 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse * Processes moving/resizing and stops it if not `mouseMove`. */ fun process(event: MouseEvent, mouseMove: Boolean) { - if (event.isConsumed) return - if (mouseMove) { - onDraggingStarted() + // 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) } - - val viewBounds = this.viewBounds - val location = this.location - if (location != null && viewBounds != null) { - 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(view) - return - } - - if (view != null) { - val bounds = Rectangle(viewBounds) - val delta = computeOffsetFromInitialLocation(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) - } - } - } - if (!mouseMove) { - resetCursor(content) - } - event.consume() - } - if (isBusy && !mouseMove) { + if (!mouseMove) { handleStopEvent(event) } } @@ -178,11 +136,46 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse return Cursor.CUSTOM_CURSOR } - protected abstract fun onDraggingStarted() + private fun handleDragEvent(event: MouseEvent, mouseMove: Boolean) { + val session = currentSession ?: return + if (mouseMove) { + onDraggingStarted(session) + } - protected open fun computeOffsetFromInitialLocation(event: MouseEvent): Point { - val location = this.location - checkNotNull(location) { "Must not be called when inactive" } + 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) @@ -197,42 +190,52 @@ internal sealed class WindowMouseListenerSupport(private val source: WindowMouse if (resized) source.notifyResized() } - private fun resetCursor(content: Component) { - source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) - } - private fun handleStopEvent(event: MouseEvent) { - if (expectMouseReleased && event.id == MouseEvent.MOUSE_RELEASED) { - expectMouseReleased = false + val session = currentSession ?: return + + resetCursor(source.getContent(event)) + + if (session.expectMouseReleased && event.id == MouseEvent.MOUSE_RELEASED) { + session.expectMouseReleased = false event.consume() } - if (expectMouseClicked && event.id == MouseEvent.MOUSE_CLICKED) { - expectMouseClicked = false + if (session.expectMouseClicked && event.id == MouseEvent.MOUSE_CLICKED) { + session.expectMouseClicked = false event.consume() } - if (!expectMouseReleased && !expectMouseClicked) { + + if (!session.expectMouseReleased && !session.expectMouseClicked) { stop() } } - private fun stop() { - this.location = null - this.viewBounds = null - expectMouseClicked = false - expectMouseReleased = false + private fun resetCursor(content: Component) { + source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) } - private fun enterJbrMoveMode(view: Component?) { - JBR.getWindowMove().startMovingTogetherWithMouse(view as Window, mouseButton) + 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() { - expectMouseClicked = false // after dragging starts, there will be only one "released" event + override fun onDraggingStarted(session: WindowListenerSession) { + session.expectMouseClicked = false // after dragging starts, there will be only one "released" event } override fun jbrMoveSupported(component: Component?): Boolean { @@ -244,8 +247,6 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc private var isTruePopup = false private var dx = 0 private var dy = 0 - private var expectReleasedEvent = false - private var expectClickedEvent = false override fun beforeUpdate(event: MouseEvent, view: Component) { isTruePopup = view is Window && view.type == Window.Type.POPUP @@ -257,12 +258,10 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc if (isRelativeMovementMode()) { @Suppress("UsePropertyAccessSyntax") JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset() - expectClickedEvent = true - expectReleasedEvent = true } } - override fun onDraggingStarted() { } // on Wayland, whether dragging has started or not, both "released" and "clicked" events will arrive + 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) @@ -279,7 +278,7 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc return Cursor.CUSTOM_CURSOR } - override fun computeOffsetFromInitialLocation(event: MouseEvent): Point { + override fun computeOffsetFromInitialLocation(session: WindowListenerSession, event: MouseEvent): Point { if (isRelativeMovementMode()) { @Suppress("UsePropertyAccessSyntax") val delta = JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset() @@ -288,7 +287,7 @@ private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSourc return Point(dx, dy) } else { - return super.computeOffsetFromInitialLocation(event) + return super.computeOffsetFromInitialLocation(session, event) } } From 2e6e32d8193de8438d29d348304ba1fa9fd170c6 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 20 Oct 2025 09:42:18 +0200 Subject: [PATCH 015/105] [devkit] IJPL-182738: search for module usages starting from module xml - also support rename of file, which would update all references in dependencies/content GitOrigin-RevId: 4a7b8db0bd57458a46bdfffffabb978f6ab56736 --- .../dom/impl/ModuleDescriptorNameConverter.kt | 11 +++ .../findUsages/PluginModuleFindUsagesTest.kt | 6 +- .../devkit/rename/PluginModuleRenameTest.kt | 73 +++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/rename/PluginModuleRenameTest.kt diff --git a/plugins/devkit/devkit-core/src/dom/impl/ModuleDescriptorNameConverter.kt b/plugins/devkit/devkit-core/src/dom/impl/ModuleDescriptorNameConverter.kt index d28282a7c9f4..7e0e18bf1984 100644 --- a/plugins/devkit/devkit-core/src/dom/impl/ModuleDescriptorNameConverter.kt +++ b/plugins/devkit/devkit-core/src/dom/impl/ModuleDescriptorNameConverter.kt @@ -12,7 +12,9 @@ import com.intellij.openapi.roots.LibraryOrderEntry import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.OrderRootType import com.intellij.openapi.util.Key +import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiElement import com.intellij.psi.PsiManager import com.intellij.psi.search.GlobalSearchScopes import com.intellij.psi.xml.XmlFile @@ -20,6 +22,7 @@ import com.intellij.util.Processor import com.intellij.util.xml.ConvertContext import com.intellij.util.xml.DomUtil import com.intellij.util.xml.ElementPresentationManager +import com.intellij.util.xml.GenericDomValue import com.intellij.util.xml.ResolvingConverter import org.jetbrains.idea.devkit.DevKitBundle import org.jetbrains.idea.devkit.dom.ContentDescriptor @@ -74,6 +77,14 @@ class ModuleDescriptorNameConverter : ResolvingConverter() { 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-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/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) + } + +} From 346eb052ad61f511729fe343e283db749cf20caa Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Oct 2025 11:39:37 +0200 Subject: [PATCH 016/105] [JPS] Make index registration in Dependency Graph pluggable GitOrigin-RevId: 809bf668f943cc178488767d4f58fa1bf6bf25f5 --- .../jps/dependency/impl/DeltaImpl.java | 8 +-- .../dependency/impl/DependencyGraphImpl.java | 31 ++++----- .../jps/dependency/impl/GraphImpl.java | 63 +++++++++++++++++-- .../impl/graph/LibraryGraphLoader.java | 3 +- 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DeltaImpl.java b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DeltaImpl.java index a9abb217c465..a7cddeae8499 100644 --- a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DeltaImpl.java +++ b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DeltaImpl.java @@ -3,8 +3,6 @@ package org.jetbrains.jps.dependency.impl; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.dependency.*; -import org.jetbrains.jps.dependency.java.SubclassesIndex; -import org.jetbrains.jps.dependency.kotlin.TypealiasesIndex; import org.jetbrains.jps.util.Iterators; import java.util.Collections; @@ -26,10 +24,8 @@ public final class DeltaImpl extends GraphImpl implements Delta { private final Set myBaseSources; private final Set myDeletedSources; - public DeltaImpl(Iterable baseSources, Iterable deletedSources) { - super(Containers.MEMORY_CONTAINER_FACTORY); - addIndex(new SubclassesIndex(Containers.MEMORY_CONTAINER_FACTORY)); - addIndex(new TypealiasesIndex(Containers.MEMORY_CONTAINER_FACTORY)); // todo: make registration 'pluggable', e.g. via DifferentiateStrategy + public DeltaImpl(Iterable baseSources, Iterable deletedSources, IndexFactory indexFactory) { + super(Containers.MEMORY_CONTAINER_FACTORY, indexFactory); myBaseSources = Collections.unmodifiableSet(baseSources instanceof Set? (Set)baseSources : Iterators.collect(baseSources, new HashSet<>())); myDeletedSources = Collections.unmodifiableSet(deletedSources instanceof Set? (Set)deletedSources : Iterators.collect(deletedSources, new HashSet<>())); } diff --git a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DependencyGraphImpl.java b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DependencyGraphImpl.java index e53b07563a5c..cb8554d60d0a 100644 --- a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DependencyGraphImpl.java +++ b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/DependencyGraphImpl.java @@ -6,9 +6,7 @@ import org.jetbrains.jps.dependency.*; import org.jetbrains.jps.dependency.diff.DiffCapable; import org.jetbrains.jps.dependency.diff.Difference; import org.jetbrains.jps.dependency.java.GeneralJvmDifferentiateStrategy; -import org.jetbrains.jps.dependency.java.SubclassesIndex; import org.jetbrains.jps.dependency.kotlin.KotlinSourceOnlyDifferentiateStrategy; -import org.jetbrains.jps.dependency.kotlin.TypealiasesIndex; import java.util.*; import java.util.function.Function; @@ -19,31 +17,24 @@ import static org.jetbrains.jps.util.Iterators.*; public final class DependencyGraphImpl extends GraphImpl implements DependencyGraph { private static final List ourDifferentiateStrategies = List.of(new KotlinSourceOnlyDifferentiateStrategy(), new GeneralJvmDifferentiateStrategy()); - private final Set myRegisteredIndices; public DependencyGraphImpl(MapletFactory containerFactory) { - super(containerFactory); - try { - addIndex(new SubclassesIndex(containerFactory)); - addIndex(new TypealiasesIndex(containerFactory)); // todo: make registration 'pluggable', e.g. via DifferentiateStrategy - myRegisteredIndices = Collections.unmodifiableSet(collect(map(getIndices(), index -> index.getName()), new HashSet<>())); - } - catch (RuntimeException e) { - closeIgnoreErrors(); - throw e; - } + this(containerFactory, IndexFactory.mandatoryIndices()); + } + + /** + * Note that IndexFactory must register all mandatory indices that {@link IndexFactory#mandatoryIndices()} provides + */ + public DependencyGraphImpl(MapletFactory containerFactory, IndexFactory idxFactory) { + super(containerFactory, idxFactory); } @Override public Delta createDelta(Iterable compiledSources, Iterable deletedSources, boolean isSourceOnly) { - Delta delta = isSourceOnly? new SourceOnlyDelta(myRegisteredIndices, compiledSources, deletedSources) : new DeltaImpl(compiledSources, deletedSources); - - Set deltaIndices = collect(map(delta.getIndices(), index -> index.getName()), new HashSet<>()); - if (!myRegisteredIndices.equals(deltaIndices)) { - throw new RuntimeException("Graph delta should contain the same set of indices as the base graph\n\tCurrent graph indices: " + myRegisteredIndices + "\n\tCurrent Delta indices: " + deltaIndices); + if (isSourceOnly) { + return new SourceOnlyDelta(map(getIndices(), BackDependencyIndex::getName), compiledSources, deletedSources); } - - return delta; + return new DeltaImpl(compiledSources, deletedSources, getIndexFactory()); } @Override diff --git a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/GraphImpl.java b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/GraphImpl.java index 57c9e628cedc..21c78f7c51de 100644 --- a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/GraphImpl.java +++ b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/impl/GraphImpl.java @@ -6,12 +6,17 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.dependency.*; import org.jetbrains.jps.dependency.java.JvmNodeReferenceID; +import org.jetbrains.jps.dependency.java.SubclassesIndex; +import org.jetbrains.jps.dependency.kotlin.TypealiasesIndex; +import org.jetbrains.jps.util.Iterators; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.function.Function; + +import static org.jetbrains.jps.util.Iterators.*; // this is a base implementation for shared functionality in both DependencyGraph and Delta @ApiStatus.Internal @@ -21,11 +26,57 @@ public abstract class GraphImpl implements Graph { protected final MultiMaplet myNodeToSourcesMap; protected final MultiMaplet> mySourceToNodesMap; private final MapletFactory myContainerFactory; + private final IndexFactory myIndexFactory; + private static final Set ourMandatoryIndexNames = Set.of( + NodeDependenciesIndex.NAME, SubclassesIndex.NAME, TypealiasesIndex.NAME + ); - protected GraphImpl(@NotNull MapletFactory cFactory) { + public interface IndexFactory { + Iterable createIndices(@NotNull MapletFactory cFactory); + + + /** + * An index factory creating set of mandatory indices that must be present in all graph instances + */ + static IndexFactory mandatoryIndices() { + return containerFactory -> List.of( + new NodeDependenciesIndex(containerFactory), + new SubclassesIndex(containerFactory), + new TypealiasesIndex(containerFactory) + ); + } + + /** + * + * @param extension indices + * @return a combined index factory that creates all mandatory indices plus those specified + */ + static IndexFactory create(Function... extIndices) { + IndexFactory mandatory = mandatoryIndices(); + return containerFactory -> collect( + map(Arrays.asList(extIndices), extIndex -> extIndex.apply(containerFactory)), + collect(mandatory.createIndices(containerFactory), new ArrayList<>()) + ); + } + + } + + protected GraphImpl(@NotNull MapletFactory cFactory, IndexFactory indexFactory) { myContainerFactory = cFactory; + myIndexFactory = indexFactory; try { - addIndex(myDependencyIndex = new NodeDependenciesIndex(cFactory)); + + Set registered = new HashSet<>(); + for (BackDependencyIndex index : indexFactory.createIndices(cFactory)) { + myIndices.add(index); + registered.add(index.getName()); + } + + if (!registered.containsAll(ourMandatoryIndexNames)) { + throw new RuntimeException("Dependency Graph must contain following mandatory indices: " + ourMandatoryIndexNames + "\n\tCurrent registered indices: " + registered); + } + + myDependencyIndex = find(myIndices, idx -> NodeDependenciesIndex.NAME.equals(idx.getName())); // important: if multiple implementations of NodeSource are available, change to generic graph element externalizer Externalizer srcExternalizer = Externalizer.forGraphElement(PathSource::new, NodeSource[]::new); @@ -38,8 +89,8 @@ public abstract class GraphImpl implements Graph { } } - protected final void addIndex(BackDependencyIndex index) { - myIndices.add(index); + protected final IndexFactory getIndexFactory() { + return myIndexFactory; } @Override diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/graph/LibraryGraphLoader.java b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/graph/LibraryGraphLoader.java index 4c82ef0676b7..ad271c4a2b10 100644 --- a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/graph/LibraryGraphLoader.java +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/graph/LibraryGraphLoader.java @@ -12,6 +12,7 @@ import org.jetbrains.jps.dependency.Delta; import org.jetbrains.jps.dependency.Graph; import org.jetbrains.jps.dependency.NodeSource; import org.jetbrains.jps.dependency.impl.DeltaImpl; +import org.jetbrains.jps.dependency.impl.GraphImpl; import org.jetbrains.jps.dependency.impl.PathSource; import org.jetbrains.jps.dependency.java.JvmClassNodeBuilder; import org.jetbrains.jps.util.Pair; @@ -41,7 +42,7 @@ public final class LibraryGraphLoader { private static Pair loadReadonlyLibraryGraph(Path jarPath) throws IOException { try (var is = new BufferedInputStream(Files.newInputStream(jarPath))) { - return loadReadonlyLibraryGraph(new DeltaImpl(Set.of(), Set.of()) /*depGraph.createDelta(Set.of(), Set.of(), false)*/, ClassDataZipEntry.fromSteam(is)); + return loadReadonlyLibraryGraph(new DeltaImpl(Set.of(), Set.of(), GraphImpl.IndexFactory.mandatoryIndices()) /*depGraph.createDelta(Set.of(), Set.of(), false)*/, ClassDataZipEntry.fromSteam(is)); } } From a181c0032e067fb2eef4ebeb0ece17bc579b89ff Mon Sep 17 00:00:00 2001 From: "Aleksei.Cherepanov" Date: Fri, 10 Oct 2025 16:52:07 +0200 Subject: [PATCH 017/105] KTIJ-35867 [Bazel] Provide Kotlin CRI Path GitOrigin-RevId: ee70c9a5050aeb2843aa327c60a0e8f27604bad2 --- .../tools/build/bazel/jvmIncBuilder/BuildContext.java | 3 +++ .../tools/build/bazel/jvmIncBuilder/DataPaths.java | 1 + .../build/bazel/jvmIncBuilder/impl/BuildContextImpl.java | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/BuildContext.java b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/BuildContext.java index cd5fc2177379..6c33a82a96e9 100644 --- a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/BuildContext.java +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/BuildContext.java @@ -36,6 +36,9 @@ public interface BuildContext extends DiagnosticSink { @Nullable Path getAbiOutputZip(); + @Nullable + Path getKotlinCriStoragePath(); + NodeSourceSnapshot getSources(); NodeSourceSnapshot getBinaryDependencies(); diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/DataPaths.java b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/DataPaths.java index 8e14fce15df7..aa70a4f6ec2c 100644 --- a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/DataPaths.java +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/DataPaths.java @@ -13,6 +13,7 @@ public interface DataPaths { String OLD_DEPS_DIR_NAME = "past-deps"; String ABI_JAR_SUFFIX = ".abi.jar"; String DATA_DIR_NAME_SUFFIX = "-ic"; + String KOTLIN_CRI_STORAGE_SUFFIX = "-kotlinCriStorage"; String KOTLIN_MODULE_EXTENSION = ".kotlin_module"; diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/BuildContextImpl.java b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/BuildContextImpl.java index fcdeb67c9bd2..c4c6ad73610d 100644 --- a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/BuildContextImpl.java +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/BuildContextImpl.java @@ -29,6 +29,7 @@ public class BuildContextImpl implements BuildContext { private final Appendable myMessageSink; private final @NotNull Path myOutJar; private final @Nullable Path myAbiJar; + private final @Nullable Path myKotlinCriStoragePath; private final Path myDataDir; private final @NotNull NodeSourceSnapshot mySources; @@ -60,6 +61,8 @@ public class BuildContextImpl implements BuildContext { String abiPath = CLFlags.ABI_OUT.getOptionalScalarValue(flags); myAbiJar = abiPath != null? baseDir.resolve(abiPath).normalize() : null; + myKotlinCriStoragePath = myOutJar.resolveSibling(truncateExtension(myOutJar.getFileName().toString()) + DataPaths.KOTLIN_CRI_STORAGE_SUFFIX); + myDataDir = myOutJar.resolveSibling(truncateExtension(myOutJar.getFileName().toString()) + DataPaths.DATA_DIR_NAME_SUFFIX); myIsRebuild = CLFlags.NON_INCREMENTAL.isFlagSet(flags); @@ -320,6 +323,11 @@ public class BuildContextImpl implements BuildContext { return myAbiJar; } + @Override + public @Nullable Path getKotlinCriStoragePath() { + return myKotlinCriStoragePath; + } + @Override public @NotNull NodeSourceSnapshot getSources() { return mySources; From 3b74c3d0bc2884b784836e15c9cfc99884a02508 Mon Sep 17 00:00:00 2001 From: "Aleksei.Cherepanov" Date: Wed, 8 Oct 2025 15:40:36 +0200 Subject: [PATCH 018/105] KTIJ-35867 [JPS] Introduce Lookups index to the incremental graph Added storage from an FqName to a collection of files, where this FqName was encountered. Enabled as an optional index only for Bazel GitOrigin-RevId: 1316f57b0a2ee3389c4d535319d9c40ddb6be8da --- .../jps/dependency/kotlin/LookupsIndex.java | 35 +++++++++++++++++++ .../bazel/jvmIncBuilder/StorageManager.java | 30 ++++++++++++---- 2 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/kotlin/LookupsIndex.java diff --git a/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/kotlin/LookupsIndex.java b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/kotlin/LookupsIndex.java new file mode 100644 index 000000000000..ef14f2096dff --- /dev/null +++ b/build/jvm-rules/dependency-graph/src/org/jetbrains/jps/dependency/kotlin/LookupsIndex.java @@ -0,0 +1,35 @@ +// 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.jps.dependency.kotlin; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.dependency.MapletFactory; +import org.jetbrains.jps.dependency.Node; +import org.jetbrains.jps.dependency.ReferenceID; +import org.jetbrains.jps.dependency.impl.BackDependencyIndexImpl; +import org.jetbrains.jps.dependency.java.JvmNodeReferenceID; +import org.jetbrains.jps.dependency.java.LookupNameUsage; +import org.jetbrains.jps.util.Iterators; + + +public final class LookupsIndex extends BackDependencyIndexImpl { + public static final String NAME = "lookups"; + + public LookupsIndex(@NotNull MapletFactory cFactory) { + super(NAME, cFactory); + } + + @Override + public Iterable getIndexedDependencies(@NotNull Node node) { + return Iterators.map( + Iterators.filter(node.getUsages(), usage -> usage instanceof LookupNameUsage), + usage -> { + LookupNameUsage lookupNameUsage = (LookupNameUsage)usage; + String ownerName = lookupNameUsage.getElementOwner().getNodeName(); + String name = lookupNameUsage.getName(); + + return ownerName == null || ownerName.isEmpty() + ? new JvmNodeReferenceID(name) + : new JvmNodeReferenceID(ownerName + "/" + name); + }); + } +} \ No newline at end of file diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java index 930c3ab9e5ae..bf7ae0f41402 100644 --- a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java @@ -15,6 +15,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.dependency.DependencyGraph; import org.jetbrains.jps.dependency.GraphConfiguration; import org.jetbrains.jps.dependency.impl.DependencyGraphImpl; +import org.jetbrains.jps.dependency.kotlin.LookupsIndex; import java.io.*; import java.net.MalformedURLException; @@ -34,6 +35,7 @@ public class StorageManager implements CloseableExt { private CompositeZipOutputBuilder myComposite; private InstrumentationClassFinder myInstrumentationClassFinder; private FormBinding myFormBinding; + private boolean isKotlinCriDataGenerationEnabled; private final MVStore myDataSwapStore; @@ -45,6 +47,7 @@ public class StorageManager implements CloseableExt { .cacheSize(8) .open(); myDataSwapStore.setVersionsToKeep(0); + isKotlinCriDataGenerationEnabled = myContext.getKotlinCriStoragePath() != null; } public void cleanBuildState() throws IOException { @@ -106,14 +109,29 @@ public class StorageManager implements CloseableExt { @NotNull public GraphConfiguration getGraphConfiguration() throws IOException { - GraphConfiguration config = myGraphConfig; - if (config == null) { - DependencyGraphImpl graph = new DependencyGraphImpl( - new PersistentMVStoreMapletFactory(DataPaths.getDepGraphStoreFile(myContext).toString(), Math.min(8, Runtime.getRuntime().availableProcessors())) + if (myGraphConfig != null) { + return myGraphConfig; + } + + DependencyGraphImpl graph = createDependencyGraph(); + myGraphConfig = GraphConfiguration.create(graph, myContext.getPathMapper()); + return myGraphConfig; + } + + @NotNull + private DependencyGraphImpl createDependencyGraph() throws IOException { + var filePath = DataPaths.getDepGraphStoreFile(myContext).toString(); + int maxBuilderThreads = Math.min(8, Runtime.getRuntime().availableProcessors()); + var containerFactory = new PersistentMVStoreMapletFactory(filePath, maxBuilderThreads); + + if (isKotlinCriDataGenerationEnabled) { + return new DependencyGraphImpl( + containerFactory, + DependencyGraphImpl.IndexFactory.create(LookupsIndex::new) ); - myGraphConfig = config = GraphConfiguration.create(graph, myContext.getPathMapper()); + } else { + return new DependencyGraphImpl(containerFactory); } - return config; } @NotNull From 1ccb337a146e7f30dd6a4cf6ac533c9005bb43fb Mon Sep 17 00:00:00 2001 From: "Aleksei.Cherepanov" Date: Fri, 10 Oct 2025 16:54:49 +0200 Subject: [PATCH 019/105] KTIJ-35867 [Bazel] Add serialization logic for Kotlin CRI storage GitOrigin-RevId: a83d15db2b7cc04c45a3f951b7d35e849eea98e5 --- .../bazel/jvmIncBuilder/impl/KotlinCriUtil.kt | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/KotlinCriUtil.kt diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/KotlinCriUtil.kt b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/KotlinCriUtil.kt new file mode 100644 index 000000000000..fa159929826a --- /dev/null +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/impl/KotlinCriUtil.kt @@ -0,0 +1,100 @@ +// 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.tools.build.bazel.jvmIncBuilder.impl + +import java.io.ByteArrayOutputStream +import java.io.DataOutputStream +import org.jetbrains.jps.dependency.BackDependencyIndex +import org.jetbrains.jps.dependency.DependencyGraph +import org.jetbrains.jps.dependency.ReferenceID +import org.jetbrains.jps.dependency.java.JvmNodeReferenceID +import org.jetbrains.jps.dependency.java.SubclassesIndex +import org.jetbrains.jps.dependency.kotlin.LookupsIndex + +const val VERSION = 1 + +internal fun prepareSerializedData(graph: DependencyGraph): Map { + val serializedDataResult = mutableMapOf() + + // process subtypes + val subclassesIndex: BackDependencyIndex? = graph.getIndex(SubclassesIndex.NAME) + val subclassesCount = subclassesIndex?.keys?.count() ?: 0 + if (subclassesCount > 0) { + ByteArrayOutputStream().use { byteStream -> + DataOutputStream(byteStream).use { dataOut -> + dataOut.writeVersion() + dataOut.writeInt(subclassesCount) + subclassesIndex?.keys?.forEach { key -> + val dependencies = subclassesIndex.getDependencies(key).map { it.toFqName() } + dataOut.writeUTF(key.toFqName()) + dataOut.writeInt(dependencies.size) + dependencies.forEach { dataOut.writeUTF(it) } + } + } + byteStream.toByteArray() + }.also { serializedDataResult["subtypes"] = it } + } + + // process lookups + val lookupsIndex: BackDependencyIndex? = graph.getIndex(LookupsIndex.NAME) + val lookupsCount = lookupsIndex?.keys?.count() ?: 0 + val lookupsAccumulator = mutableMapOf() + if (lookupsCount > 0) { + ByteArrayOutputStream().use { byteStream -> + DataOutputStream(byteStream).use { dataOut -> + dataOut.writeVersion() + dataOut.writeInt(lookupsCount) + lookupsIndex?.keys?.forEach { key -> + val dependencies = lookupsIndex.getDependencies(key) + .mapNotNull { (it as? JvmNodeReferenceID)?.nodeName } + dataOut.writeUTF(key.toFqName()) + dataOut.writeInt(dependencies.count()) + dependencies.forEach { + dataOut.writeInt(it.addFilePathIfNeeded(lookupsAccumulator)) } + } + } + byteStream.toByteArray() + }.also { serializedDataResult["lookups"] = it } + + if (lookupsAccumulator.isNotEmpty()) { + ByteArrayOutputStream().use { byteStream -> + DataOutputStream(byteStream).use { dataOut -> + dataOut.writeVersion() + dataOut.writeInt(lookupsAccumulator.size) + lookupsAccumulator.forEach { (path, fileId) -> + dataOut.writeInt(fileId) + dataOut.writeUTF(path) + } + } + byteStream.toByteArray() + }.also { serializedDataResult["fileIdToPath"] = it } + } + } + + return serializedDataResult +} + +/** + * Converts the current `ReferenceID` to its fully qualified name (FqName) representation. + * + * Example transformation: + * Input: `com/intellij/AppKt$foo$3$1` + * Output: `com.intellij.AppKt.foo` + * + * @return The fully qualified name as a `String`. + */ +private fun ReferenceID.toFqName(): String { + return (this as? JvmNodeReferenceID)?.nodeName + ?.replace('/', '.') + ?.replace(DOLLAR_DIGITS_SUFFIX_REGEX, "") + ?.replace('$', '.') + ?: this.toString() +} + +private val DOLLAR_DIGITS_SUFFIX_REGEX = Regex("(?:\\$\\d+)+$") + +private fun String.addFilePathIfNeeded(fileIdToPathEntryAccumulator: MutableMap): Int { + return fileIdToPathEntryAccumulator.computeIfAbsent(this) { fileIdToPathEntryAccumulator.size + 1 } +} + +private fun DataOutputStream.writeVersion() = writeInt(VERSION) \ No newline at end of file From 6bc84950a627089b88b38ce08eac8dd422438354 Mon Sep 17 00:00:00 2001 From: "Aleksei.Cherepanov" Date: Fri, 10 Oct 2025 16:56:35 +0200 Subject: [PATCH 020/105] KTIJ-35867 [Bazel] Implement Kotlin CRI data persistence GitOrigin-RevId: d5e7c625168fa52495623ba99dd8b229d5a3dbcd --- .../bazel/jvmIncBuilder/StorageManager.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java index bf7ae0f41402..0e8a79dfdc10 100644 --- a/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java +++ b/build/jvm-rules/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/StorageManager.java @@ -1,9 +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.tools.build.bazel.jvmIncBuilder; -import com.intellij.tools.build.bazel.jvmIncBuilder.impl.CompositeZipOutputBuilder; -import com.intellij.tools.build.bazel.jvmIncBuilder.impl.Utils; -import com.intellij.tools.build.bazel.jvmIncBuilder.impl.ZipOutputBuilderImpl; +import com.intellij.tools.build.bazel.jvmIncBuilder.impl.*; import com.intellij.tools.build.bazel.jvmIncBuilder.impl.forms.FormBinding; import com.intellij.tools.build.bazel.jvmIncBuilder.impl.graph.PersistentMVStoreMapletFactory; import com.intellij.tools.build.bazel.jvmIncBuilder.instrumentation.InstrumentationClassFinder; @@ -208,6 +206,7 @@ public class StorageManager implements CloseableExt { GraphConfiguration config = myGraphConfig; if (config != null) { myGraphConfig = null; + writeKotlinCriData(config.getGraph(), saveChanges); safeClose(config.getGraph(), saveChanges); } @@ -226,6 +225,31 @@ public class StorageManager implements CloseableExt { } } + private void writeKotlinCriData(DependencyGraph graph, Boolean saveChanges) { + if (!saveChanges || !isKotlinCriDataGenerationEnabled) return; + Path kotlinCriPath = myContext.getKotlinCriStoragePath(); + if (kotlinCriPath == null) return; + if (!Files.exists(kotlinCriPath)) { + try { Files.createDirectories(kotlinCriPath); } + catch (IOException e) { myContext.report(Message.create(null, e)); } + } + + KotlinCriUtilKt.prepareSerializedData(graph) + .forEach( + (name, content) -> { + try { + Files.write(kotlinCriPath.resolve(name), content, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING); + } + catch (IOException e) { + myContext.report(Message.create(null, e)); + } + } + ); + } + private void safeClose(Closeable cl, boolean saveChanges) { try { if (cl instanceof CloseableExt) { From 03132b3b7cc363a28220448b30eef65bc1e3c8db Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 15 Oct 2025 18:02:22 +0200 Subject: [PATCH 021/105] [vcs] IJPL-213060 Don't set commit tab title if it's a single tab GitOrigin-RevId: c80613a961ed6a62ca7aee4a2e516953501b90c0 --- .../vcs/commit/ChangesViewCommitPanel.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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..364e952ff184 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -22,7 +22,9 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor( private var isHideToolWindowOnCommit = false init { - ChangesViewCommitTabTitleUpdater(project).initSubscription(this) + val titleUpdater = ChangesViewCommitTabTitleUpdater(project) + titleUpdater.initSubscription(this) + titleUpdater.updateTitle() } override val isActive: Boolean get() = component.isVisible @@ -81,9 +83,19 @@ 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") } + } } } From e5879701e525f4fc9f520e5a3cb72c8deb691c17 Mon Sep 17 00:00:00 2001 From: Elena Shaverdova Date: Mon, 20 Oct 2025 12:01:42 +0200 Subject: [PATCH 022/105] [progresses, status bar] IJPL-213503 Status bar: unexpected white square in Islands theme GitOrigin-RevId: 4605c235955c7290f64759944d8cdf06b6c112ef --- .../com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.kt | 1 + 1 file changed, 1 insertion(+) 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..40ed8bd31fb8 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 @@ -1252,6 +1252,7 @@ private class CounterLabel : JPanel() { label = createLabel() layout = BorderLayout() add(label, BorderLayout.CENTER) + isOpaque = false setNumber(0, isProgressVisible =false, isPopupShowing = false) } From 659247781ed111b132c7254660c72aea19c7563a Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Thu, 9 Oct 2025 17:28:54 -0700 Subject: [PATCH 023/105] K2 UAST: FirKotlinUastLibraryPsiProviderService#provide with context ...so that the lib PSI provider can distinguish ambiguous candidates ^KTIJ-35935 fixed closes https://github.com/JetBrains/intellij-community/pull/3269 GitOrigin-RevId: 185f7eda6e12680809125061c0c2aacbb6c930a1 --- .../FirKotlinUastLibraryPsiProviderService.kt | 15 +++++++++++++-- .../kotlin/internal/firKotlinInternalUastUtils.kt | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/FirKotlinUastLibraryPsiProviderService.kt b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/FirKotlinUastLibraryPsiProviderService.kt index c701fe69caf7..86238bc50a0f 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/FirKotlinUastLibraryPsiProviderService.kt +++ b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/FirKotlinUastLibraryPsiProviderService.kt @@ -6,15 +6,26 @@ import com.intellij.openapi.components.service import com.intellij.psi.PsiElement import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.psi.KtElement /** * This service is required to add an ability to provide correct [PsiElement] for libraries in UAST/Lint CLI mode */ interface FirKotlinUastLibraryPsiProviderService { - fun KaSession.provide(symbol: KaSymbol): PsiElement? + /** + * Provide [PsiElement] from libraries that corresponds to the given [symbol]. + * + * If multiple candidates are available, an extra [context] will be used. + * For example, for the property symbol and its access, with a context like + * LHS or RHS of an assignment, either accessor would be appropriate, not both. + */ + fun KaSession.provide( + symbol: KaSymbol, + context: KtElement? = null, + ): PsiElement? class Default : FirKotlinUastLibraryPsiProviderService { - override fun KaSession.provide(symbol: KaSymbol): PsiElement? = symbol.psi + override fun KaSession.provide(symbol: KaSymbol, context: KtElement?): PsiElement? = symbol.psi } companion object { diff --git a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt index 3a0f0a9b70ac..cab5f2799db7 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt +++ b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt @@ -502,7 +502,7 @@ internal tailrec fun psiForUast( } val psiProvider = FirKotlinUastLibraryPsiProviderService.getInstance() - return with(psiProvider) { session.provide(symbol) } + return with(psiProvider) { session.provide(symbol, context) } } if (symbol is KaConstructorSymbol) { From b52dd6a931596a5af2d8813280f2bb50b975cb36 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Fri, 10 Oct 2025 16:07:13 -0700 Subject: [PATCH 024/105] K2 UAST: allow non-JVM platforms for type conversion ^KTIJ-35938 fixed closes https://github.com/JetBrains/intellij-community/pull/3271 GitOrigin-RevId: b9b28e419a474e9459031afe344863b216e52981 --- .../uast/kotlin/internal/firKotlinInternalUastUtils.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt index cab5f2799db7..de8f2bf7a292 100644 --- a/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt +++ b/plugins/kotlin/uast/uast-kotlin-fir/src/org/jetbrains/uast/kotlin/internal/firKotlinInternalUastUtils.kt @@ -416,7 +416,8 @@ internal fun toPsiType( psiTypeParent, allowErrorTypes = false, config.typeMappingMode, - isAnnotationMethod = false + isAnnotationMethod = false, + allowNonJvmPlatforms = true, ) ?: UastErrorType } From 4d1071ccc240c36f329fb7dfbe168c4de8d6e108 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 10 Oct 2025 14:54:04 +0200 Subject: [PATCH 025/105] cleanup GitOrigin-RevId: fe30fec0b870561084a76d0eb750ab4d38c1160c --- .../intellij/java/index/ConcurrentIndexTest.java | 13 ++++++------- .../cache/loader/JpsCompilationOutputLoader.java | 5 ++--- .../dependencies/DependencyResolvingBuilder.java | 3 ++- .../vfs/newvfs/impl/PersistentFS_FindFilesTest.java | 5 ++--- .../namecache/FileNameCacheMicroBenchmark.java | 5 ++--- .../intellij/util/lang/test/UrlClassLoaderTest.java | 5 ++--- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/java/index/ConcurrentIndexTest.java b/java/java-tests/testSrc/com/intellij/java/index/ConcurrentIndexTest.java index 99ee53127a82..ee6baef97746 100644 --- a/java/java-tests/testSrc/com/intellij/java/index/ConcurrentIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/java/index/ConcurrentIndexTest.java @@ -24,6 +24,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.testFramework.BombedProgressIndicator; import com.intellij.testFramework.SkipSlowTestLocally; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import com.intellij.util.ConcurrencyUtil; import com.intellij.util.ref.GCWatcher; import org.jetbrains.annotations.NotNull; @@ -82,7 +83,7 @@ public class ConcurrentIndexTest extends JavaCodeInsightFixtureTestCase { })); } - for (Future future : futuresToWait) future.get(); + ConcurrencyUtil.getAll(futuresToWait); } } @@ -149,7 +150,7 @@ public class ConcurrentIndexTest extends JavaCodeInsightFixtureTestCase { } - for (Future future : futuresToWait) future.get(); + ConcurrencyUtil.getAll(futuresToWait); } } @@ -209,7 +210,7 @@ public class ConcurrentIndexTest extends JavaCodeInsightFixtureTestCase { })); } - for (Future future : futuresToWait) future.get(); + ConcurrencyUtil.getAll(futuresToWait); } } @@ -253,12 +254,10 @@ public class ConcurrentIndexTest extends JavaCodeInsightFixtureTestCase { public void run(@NotNull ProgressIndicator indicator) { ReadAction.run(() -> { assertNotNull(JavaPsiFacade.getInstance(project).findClass("Foo", GlobalSearchScope.allScope(project))); - }); + }); } })); - for (Future future : futures) { - future.get(); - } + ConcurrencyUtil.getAll(futures); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/cache/loader/JpsCompilationOutputLoader.java b/jps/jps-builders/src/org/jetbrains/jps/cache/loader/JpsCompilationOutputLoader.java index 0b1da07254ca..4f2b6eb07d2b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cache/loader/JpsCompilationOutputLoader.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cache/loader/JpsCompilationOutputLoader.java @@ -7,6 +7,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.NioFiles; import com.intellij.util.ArrayUtil; +import com.intellij.util.ConcurrencyUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.ZipUtil; import org.jetbrains.annotations.ApiStatus; @@ -91,9 +92,7 @@ public final class JpsCompilationOutputLoader implements JpsOutputLoader> futureList = ContainerUtil.map(outputLoadResults, loadResult -> EXECUTOR_SERVICE.submit(new UnzipOutputTask(result, loadResult, myContext))); - for (Future future : futureList) { - future.get(); - } + ConcurrencyUtil.getAll(futureList); myTmpFolderToModuleName = result; LOG.info("Unzip compilation output took: " + (System.currentTimeMillis() - start)); return LoaderStatus.COMPLETE; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java index c8ff5cf8869c..17a5773131e9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/dependencies/DependencyResolvingBuilder.java @@ -9,6 +9,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.platform.jps.model.resolver.JpsDependencyResolverConfiguration; import com.intellij.platform.jps.model.resolver.JpsDependencyResolverConfigurationService; +import com.intellij.util.ConcurrencyUtil; import com.intellij.util.SmartList; import com.intellij.util.SystemProperties; import com.intellij.util.containers.CollectionFactory; @@ -226,7 +227,7 @@ public final class DependencyResolvingBuilder extends ModuleLevelBuilder { ExecutorService executorService = Executors.newFixedThreadPool(parallelism); try { List> futures = ContainerUtil.map(libs, lib -> executorService.submit(() -> resolveAction.accept(lib))); - for (Future future : futures) future.get(); + ConcurrencyUtil.getAll(futures); } finally { executorService.shutdown(); 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> 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/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 { From e81b8a8de1ccf60b70254a1ca335985ea7c8f444 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 10 Oct 2025 16:23:40 +0200 Subject: [PATCH 026/105] IJPL-30066 Highlighting settings popup: make primary language first entry GitOrigin-RevId: 79ed2760d785c51a8fbdbdf13396208b119d0cfa --- .../codeInsight/daemon/impl/TrafficLightRenderer.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 { From 916ff9df6b3cb4ce6cd693e7de233677af0e9928 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 10 Oct 2025 16:24:25 +0200 Subject: [PATCH 027/105] notnull GitOrigin-RevId: 7fa3ec9314253d8b8253424c27df50be77c9042e --- .../lang-impl/src/com/intellij/ui/StringComboboxEditor.java | 2 +- .../platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java | 2 +- .../intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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/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/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java index e30820563c8b..a291de193690 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 @@ -204,7 +204,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) { From 073b80516d1eca8b52f9aefc2a0d2b4717343832 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 10 Oct 2025 17:52:40 +0200 Subject: [PATCH 028/105] cleanup: avoid calling very internal method GitOrigin-RevId: 5563e4fa832bbdd80ef9fef381e45afb6dd12556 --- .../intellij/util/concurrency/ThreadContextPropagationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) } } From 24c4e4b0240ff1ca9799fe80f84b2806efd785d9 Mon Sep 17 00:00:00 2001 From: Alexander Bubenchikov Date: Mon, 20 Oct 2025 11:28:20 +0200 Subject: [PATCH 029/105] [maven][IDEA-365902] fix IDEA freezing when the module tag is empty GitOrigin-RevId: 3d2564877d0aac7e261e400cf1ba2ff023649613 --- .../maven/project/MavenProjectResolver.kt | 10 +-- .../idea/maven/project/MavenProjectsTree.kt | 61 ++++++++++++------- .../importing/InvalidProjectImportingTest.kt | 33 +++++++++- 3 files changed, 77 insertions(+), 27 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt index 02eed85b3805..8206b1371d7e 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectResolver.kt @@ -212,10 +212,12 @@ class MavenProjectResolver(private val myProject: Project) { val virtualFile = VirtualFileManager.getInstance().findFileByNioPath(file) ?: return@forEach val aggregatorProject = tree.findProject(virtualFile) ?: return@forEach modules.forEach modules@{ modulePath -> - val moduleFile = file.parent.resolve(modulePath).resolve("pom.xml") - val moduleVirtualFile = VirtualFileManager.getInstance().findFileByNioPath(moduleFile) ?: return@modules - val moduleProject = tree.findProject(moduleVirtualFile) ?: return@modules - tree.reconnect(aggregatorProject, moduleProject) + if (modulePath.isNotBlank()) { + val moduleFile = file.parent.resolve(modulePath).resolve("pom.xml") + val moduleVirtualFile = VirtualFileManager.getInstance().findFileByNioPath(moduleFile) ?: return@modules + val moduleProject = tree.findProject(moduleVirtualFile) ?: return@modules + tree.reconnect(aggregatorProject, moduleProject) + } } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.kt index 5f0d364eac58..c8bfb7846603 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.kt @@ -348,7 +348,7 @@ class MavenProjectsTree(val project: Project) { force: Boolean, generalSettings: MavenGeneralSettings, mavenEmbedderWrappers: MavenEmbedderWrappers, - process: MavenProgressIndicator + process: MavenProgressIndicator, ) { runBlockingMaybeCancellable { updateAll(force, generalSettings, mavenEmbedderWrappers, process.indicator) } } @@ -358,7 +358,7 @@ class MavenProjectsTree(val project: Project) { force: Boolean, generalSettings: MavenGeneralSettings, mavenEmbedderWrappers: MavenEmbedderWrappers, - process: ProgressIndicator + process: ProgressIndicator, ): MavenProjectsTreeUpdateResult { return updateAll(force, generalSettings, mavenEmbedderWrappers, toRawProgressReporter(process)) } @@ -400,11 +400,13 @@ class MavenProjectsTree(val project: Project) { return update(files, false, force, projectReader, progressReporter) } - private suspend fun update(files: Collection, - updateModules: Boolean, - forceRead: Boolean, - projectReader: MavenProjectReader, - progressReporter: RawProgressReporter): MavenProjectsTreeUpdateResult { + private suspend fun update( + files: Collection, + updateModules: Boolean, + forceRead: Boolean, + projectReader: MavenProjectReader, + progressReporter: RawProgressReporter, + ): MavenProjectsTreeUpdateResult { val updateContext = MavenProjectsTreeUpdateContext(this) val updater = MavenProjectsTreeUpdater( @@ -419,7 +421,7 @@ class MavenProjectsTree(val project: Project) { if (null == findProject(file)) { filesToAddModules.add(file) } - tracer.spanBuilder("updateProjectFile").useWithScope { updater.updateProjects(listOf(UpdateSpec(file, forceRead))) } + tracer.spanBuilder("updateProjectFile").useWithScope { updater.updateProjects(listOf(UpdateSpec(file, forceRead))) } } for (aggregator in projects) { @@ -490,9 +492,11 @@ class MavenProjectsTree(val project: Project) { return delete(projectReader, files, progressReporter) } - private suspend fun delete(projectReader: MavenProjectReader, - files: Collection, - progressReporter: RawProgressReporter): MavenProjectsTreeUpdateResult { + private suspend fun delete( + projectReader: MavenProjectReader, + files: Collection, + progressReporter: RawProgressReporter, + ): MavenProjectsTreeUpdateResult { val updateContext = MavenProjectsTreeUpdateContext(this) val inheritorsToUpdate: MutableSet = HashSet() @@ -583,7 +587,7 @@ class MavenProjectsTree(val project: Project) { val prevAggregator = findAggregator(project) if (prevAggregator === newAggregator) return false - + if (newAggregator === project) return false withWriteLock { if (prevAggregator != null) { removeModule(prevAggregator, project) @@ -780,11 +784,16 @@ class MavenProjectsTree(val project: Project) { private fun doFindRootProject(project: MavenProject): MavenProject { var rootProject = project + val traversed = LinkedHashSet().also { it.add(project) } while (true) { val aggregator = myModuleToAggregatorMapping[rootProject] if (aggregator == null) { return rootProject } + if (!traversed.add(aggregator)) { + MavenLog.LOG.warn("Recursive aggregator definition: ${traversed.joinToString(" -> ") { it.mavenId.toString() }}") + return project + } rootProject = aggregator } } @@ -941,9 +950,11 @@ class MavenProjectsTree(val project: Project) { fun profilesChanged() { } - fun projectsIgnoredStateChanged(ignored: List, - unignored: List, - fromImport: Boolean) { + fun projectsIgnoredStateChanged( + ignored: List, + unignored: List, + fromImport: Boolean, + ) { } fun projectsUpdated(updated: List>, deleted: List) { @@ -951,8 +962,10 @@ class MavenProjectsTree(val project: Project) { @Suppress("DEPRECATION") @Deprecated("use projectResolved(Pair)") - fun projectResolved(projectWithChanges: Pair, - nativeMavenProject: NativeMavenProjectHolder?) { + fun projectResolved( + projectWithChanges: Pair, + nativeMavenProject: NativeMavenProjectHolder?, + ) { } @Suppress("DEPRECATION") @@ -1152,8 +1165,10 @@ class MavenProjectsTree(val project: Project) { } @Throws(IOException::class) - private fun readProjectsRecursively(inputStream: DataInputStream, - tree: MavenProjectsTree): MutableList { + private fun readProjectsRecursively( + inputStream: DataInputStream, + tree: MavenProjectsTree, + ): MutableList { var count = inputStream.readInt() val result: MutableList = ArrayList(count) while (count-- > 0) { @@ -1176,9 +1191,11 @@ class MavenProjectsTree(val project: Project) { return result } - private fun updateExplicitProfiles(explicitProfiles: MutableCollection, - temporarilyRemovedExplicitProfiles: MutableCollection, - available: Set) { + private fun updateExplicitProfiles( + explicitProfiles: MutableCollection, + temporarilyRemovedExplicitProfiles: MutableCollection, + available: Set, + ) { val removedProfiles = HashSet(explicitProfiles) removedProfiles.removeAll(available) temporarilyRemovedExplicitProfiles.addAll(removedProfiles) diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/InvalidProjectImportingTest.kt b/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/InvalidProjectImportingTest.kt index 76f0c45a2eb8..251f7d3033b7 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/InvalidProjectImportingTest.kt +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/InvalidProjectImportingTest.kt @@ -21,7 +21,6 @@ import com.intellij.build.events.BuildIssueEvent import com.intellij.build.events.MessageEvent import com.intellij.maven.testFramework.MavenMultiVersionImportingTestCase import com.intellij.openapi.application.edtWriteAction -import com.intellij.openapi.util.registry.Registry import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.replaceService import kotlinx.coroutines.runBlocking @@ -1009,6 +1008,38 @@ class InvalidProjectImportingTest : MavenMultiVersionImportingTestCase() { assertProblems(root, "'settings.xml' has syntax errors") } + @Test + fun testImportingWithEmptyPath() = runBlocking { + importProjectAsync(""" + test + project + 1 + pom + + + + """.trimIndent()) + assertModules("project") + val rootProject = projectsManager.findProject(projectPom) + assertNotNull("Project should be found", rootProject) + val rootOfRoot = projectsManager.findRootProject(rootProject!!) + assertNotNull("Root of root should be null", rootOfRoot) + } + + @Test + fun testImportingWithSelfInclusionInclusion() = runBlocking { + importProjectAsync(""" + test + project + 1 + pom + + ./pom.xml + + """.trimIndent()) + assertModules("project") + } + private val rootProjects: List get() = projectsTree.rootProjects From b42ac4832304d8ba5d9c6f4634c28e3d04f37bcd Mon Sep 17 00:00:00 2001 From: Yuriy Artamonov Date: Sat, 18 Oct 2025 16:16:35 +0200 Subject: [PATCH 030/105] [registry] IJPL-175398 Move plugin registry keys out of the platform registry.properties GitOrigin-RevId: 70ed1d4864da4788fd77a719a9063b560c5bb5d6 --- .../impl/resources/META-INF/java-debugger.xml | 3 + .../resources/META-INF/JavaAnalysisPlugin.xml | 3 + .../resources/META-INF/JavaPlugin.xml | 15 +- .../util/resources/misc/registry.properties | 523 +++++++++--------- ...inKeysAreProhibitedInPlatformRegistry.html | 5 + .../resources/intellij.devkit.core.xml | 8 +- .../messages/DevKitBundle.properties | 3 +- .../RegistryPropertiesAnnotator.kt | 187 +++---- ...evkitInspectionsRegistrationCheckTest.java | 2 +- .../PluginConfigReferenceTest.java | 2 +- plugins/eclipse/resources/META-INF/plugin.xml | 5 +- .../detect/EclipseProjectDetector.java | 5 +- .../gradle/java/resources/META-INF/plugin.xml | 4 + .../plugin-resources/META-INF/plugin.xml | 5 + plugins/javaFX/resources/intellij.javaFX.xml | 3 + .../resources/META-INF/jvm-debugger.xml | 24 +- .../src/main/resources/META-INF/plugin.xml | 3 + .../intellij.pycharm.community.ide.impl.xml | 3 + 18 files changed, 418 insertions(+), 385 deletions(-) create mode 100644 plugins/devkit/devkit-core/resources/inspectionDescriptions/PluginKeysAreProhibitedInPlatformRegistry.html diff --git a/java/debugger/impl/resources/META-INF/java-debugger.xml b/java/debugger/impl/resources/META-INF/java-debugger.xml index ccde48254eef..2aac46de309b 100644 --- a/java/debugger/impl/resources/META-INF/java-debugger.xml +++ b/java/debugger/impl/resources/META-INF/java-debugger.xml @@ -108,6 +108,7 @@ + @@ -219,6 +220,8 @@ description="The timeout between the hit run-to-cursor breakpoint in another thread/coroutine and pause the debugger"/> "/> + diff --git a/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml b/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml index 44c2aee521fa..9ec4a1693a12 100644 --- a/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml +++ b/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml @@ -365,5 +365,8 @@ + + diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml index 7c865a63cd7e..4a6fe53c3863 100644 --- a/java/java-impl/resources/META-INF/JavaPlugin.xml +++ b/java/java-impl/resources/META-INF/JavaPlugin.xml @@ -678,10 +678,6 @@ implementationClass="com.intellij.codeInsight.hint.NextParameterAfterCompletionHandler"/> - - - - @@ -811,6 +807,10 @@ + + + @@ -2785,12 +2785,19 @@ + + + + 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/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..4d326eaa151f 100644 --- a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml +++ b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml @@ -659,7 +659,13 @@ implementation="org.jetbrains.idea.devkit.references.MessageBundleReferenceContributor$ImplicitUsageProvider"/> - + """.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-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/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 @@ +
    diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/detect/EclipseProjectDetector.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/detect/EclipseProjectDetector.java index 004542433c7b..4adc89407743 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/detect/EclipseProjectDetector.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/detect/EclipseProjectDetector.java @@ -11,6 +11,7 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.impl.welcomeScreen.ProjectDetector; import com.intellij.util.ArrayUtil; @@ -53,7 +54,7 @@ public final class EclipseProjectDetector extends ProjectDetector { for (String appLocation : getStandardAppLocations()) { collectProjects(projects, Path.of(appLocation)); } - if (PropertiesComponent.getInstance().getBoolean("eclipse.scan.home.directory", true)) { + if (Registry.is("eclipse.scan.home.directory", true)) { visitFiles(new File(home), file1 -> scanForProjects(file1.getPath(), projects), 2); } } @@ -80,7 +81,7 @@ public final class EclipseProjectDetector extends ProjectDetector { ProjectGroup group = ContainerUtil.find(manager.getGroups(), g -> groupName.equals(g.getName())); String property = "eclipse.projects.detected"; if (group == null && PropertiesComponent.getInstance().isValueSet(property)) { - // the group was removed by user + // the user removed the group return; } diff --git a/plugins/gradle/java/resources/META-INF/plugin.xml b/plugins/gradle/java/resources/META-INF/plugin.xml index ee5bd398e926..88ec4e9ef917 100644 --- a/plugins/gradle/java/resources/META-INF/plugin.xml +++ b/plugins/gradle/java/resources/META-INF/plugin.xml @@ -129,6 +129,10 @@ description="Specifies port at which Gradle target server process will wait for debugger connections. -1 means disabled feature."/> + + + + + diff --git a/plugins/javaFX/resources/intellij.javaFX.xml b/plugins/javaFX/resources/intellij.javaFX.xml index 0a2baab33fa7..e9f2aca565b5 100644 --- a/plugins/javaFX/resources/intellij.javaFX.xml +++ b/plugins/javaFX/resources/intellij.javaFX.xml @@ -105,6 +105,9 @@ + + diff --git a/plugins/kotlin/plugin/common/resources/META-INF/jvm-debugger.xml b/plugins/kotlin/plugin/common/resources/META-INF/jvm-debugger.xml index 2120e525780b..da9a5818e4cf 100644 --- a/plugins/kotlin/plugin/common/resources/META-INF/jvm-debugger.xml +++ b/plugins/kotlin/plugin/common/resources/META-INF/jvm-debugger.xml @@ -28,6 +28,8 @@ + @@ -38,6 +40,8 @@ + @@ -47,7 +51,7 @@ - + @@ -64,6 +68,8 @@ + @@ -100,5 +106,21 @@ description="Use Analysis API-based implementation for file applicability checker" defaultValue="false" restartRequired="false"/> + + + + diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index 4afb144fc743..1e3b0e8a635e 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -79,6 +79,9 @@ + + diff --git a/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml b/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml index a21af3c53a65..1dd8037757f3 100644 --- a/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml +++ b/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml @@ -93,6 +93,9 @@ instance="com.intellij.pycharm.community.ide.impl.settings.MemoryUsageConfigurable" groupId="root"/> + + From be282cd7c64714eae5cb93f057747cd157d3e892 Mon Sep 17 00:00:00 2001 From: Nikolai Bogdanov Date: Mon, 20 Oct 2025 12:53:13 +0000 Subject: [PATCH 031/105] [aia] LME-609 Remove PSI magic from maven execution Merge-request: IJ-MR-178680 Merged-by: Nikolai Bogdanov GitOrigin-RevId: 33e6ab060c2be0bd2213dbc3ea07a54b0cf8baf9 --- .../intellij/cce/java/test/JavaTestRunner.kt | 3 +- .../cce/java/test/JavaTestRunnerForMaven.kt | 55 ++----------------- 2 files changed, 8 insertions(+), 50 deletions(-) diff --git a/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunner.kt b/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunner.kt index 36751b754ff1..6eab216690d2 100644 --- a/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunner.kt +++ b/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunner.kt @@ -35,7 +35,8 @@ class JavaTestRunner : TestRunner { } .groupBy { it.first } .map { - ModuleTests(it.key, it.value + val module = if(it.key == "src") null else it.key + ModuleTests(module, it.value .map { it.second } .filter { it != "gradle_test_execution" }) // just a command to run all the tests (no specific) } diff --git a/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunnerForMaven.kt b/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunnerForMaven.kt index 44c5cc5e1bd9..219ce9fe495d 100644 --- a/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunnerForMaven.kt +++ b/plugins/evaluation-plugin/languages/java/src/com/intellij/cce/java/test/JavaTestRunnerForMaven.kt @@ -1,19 +1,16 @@ package com.intellij.cce.java.test -import com.intellij.cce.test.TestNotFoundException import com.intellij.cce.test.TestRunResult -import com.intellij.openapi.application.smartReadAction -import com.intellij.openapi.module.ModuleUtil import com.intellij.openapi.project.Project import com.intellij.openapi.project.guessProjectDir -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.idea.maven.execution.MavenRunConfigurationType import org.jetbrains.idea.maven.execution.MavenRunnerParameters import org.jetbrains.idea.maven.execution.MavenRunnerSettings -import org.jetbrains.idea.maven.project.MavenProjectsManager +/** + * Just a maven runner. + */ internal object JavaTestRunnerForMaven { suspend fun run(project: Project, moduleTests: List): TestRunResult { @@ -28,10 +25,8 @@ internal object JavaTestRunnerForMaven { params.cmdOptions = "-am" - val resolvedModuleTests = resolveMavenProjects(project, moduleTests) - // in multi-module projects tests will be prefixed with module:test - resolvedModuleTests + moduleTests .mapNotNull { it.module } .also { if (it.isNotEmpty()) { @@ -46,7 +41,7 @@ internal object JavaTestRunnerForMaven { "surefire.failIfNoSpecifiedTests" to "false", "failIfNoTests" to "false", "maven.gitcommitid.skip" to "true", - "test" to resolvedModuleTests.flatMap { it.tests }.joinToString(separator = ",") + "test" to moduleTests.flatMap { it.tests }.joinToString(separator = ",") ) } @@ -62,7 +57,7 @@ internal object JavaTestRunnerForMaven { results.exitCode, passed, failed, - resolvedModuleTests.flatMap { it.tests }, + moduleTests.flatMap { it.tests }, compilationSuccessful, projectIsResolvable, output @@ -129,42 +124,4 @@ object MavenOutputParser { } return res } -} - -private suspend fun resolveMavenProjects(project: Project, moduleTests: List): List { - val result = mutableMapOf>() - - smartReadAction(project) { - for ((priorProject, tests) in moduleTests) { - for (test in tests) { - val (guessedProject, test) = tryGuessProject(project, priorProject, test) - if (!result.containsKey(guessedProject)) { - result[guessedProject] = mutableListOf() - } - result[guessedProject]!!.add(test) - } - } - } - - return result.map { ModuleTests(it.key, it.value.distinct()) } -} - -private fun tryGuessProject(project: Project, priorProject: String?, test: String): Pair { - if (priorProject != null) { - return Pair(priorProject, test) - } - - val facade = JavaPsiFacade.getInstance(project) - val scope = GlobalSearchScope.projectScope(project) - val psiClass = facade.findClass(test, scope) ?: facade.findClass(test.split('.').dropLast(1).joinToString("."), scope) - - if (psiClass == null) { - throw TestNotFoundException("Not found class for test: $priorProject:$test") - } - - val module = ModuleUtil.findModuleForFile(psiClass.containingFile) - val mavenProject = module?.let { MavenProjectsManager.getInstance(project).findProject(it) } - - val guessedProject = if (mavenProject == null) null else ":${mavenProject.mavenId.artifactId}" - return Pair(guessedProject, psiClass.qualifiedName!!) } \ No newline at end of file From 4e8184c30cbb653508a27a8cd35c74e9ead93107 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 20 Oct 2025 11:45:33 +0200 Subject: [PATCH 032/105] [java-analysis] IDEA-373908 Freeze at com.intellij.codeInspection.dataFlow.CommonDataflow.getDataflowResult Use ConcurrentFactoryMap instead of ConcurrentHashMap.computeIfAbsent The ConcurrentHashMap.computeIfAbsent was used to prevent concurrent computations, as DFA can be computationally intensive and there are high chances of concurrent computations on the same method, so we didn't want to load all the cores with the same computation. However, blocking call cause deadlocks in rare cases in current threading model. Since CommonDataflow was introduced, we improved performance significantly and separated CFG creation into a separate cached action, so probably the CPU utilization is probably better now. In any case, it's better than a deadlock. GitOrigin-RevId: 87951d83c9e51adddb34b49d0d8c75fa9201f79b --- .../dataFlow/CommonDataflow.java | 46 ++++--------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java index 1f06016f5873..a2e47c769708 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java @@ -26,6 +26,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.ThreeState; +import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.Contract; @@ -33,9 +34,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ConcurrentMap; import static com.intellij.codeInspection.dataFlow.DfaUtil.hasImplicitImpureSuperCall; @@ -272,41 +271,14 @@ public final class CommonDataflow { public static @Nullable DataflowResult getDataflowResult(@NotNull PsiElement context) { PsiElement body = DfaUtil.getDataflowContext(context); if (body == null) return null; - ConcurrentHashMap fileMap = + + ConcurrentMap fileMap = CachedValuesManager.getCachedValue(body.getContainingFile(), () -> - CachedValueProvider.Result.create(new ConcurrentHashMap<>(), PsiModificationTracker.MODIFICATION_COUNT)); - class ManagedCompute implements ForkJoinPool.ManagedBlocker { - DataflowResult myResult; - - @Override - public boolean block() { - myResult = fileMap.computeIfAbsent(body, CommonDataflow::runDFA); - return true; - } - - @Override - public boolean isReleasable() { - myResult = fileMap.get(body); - return myResult != null; - } - - DataflowResult getResult() { - return myResult == null || myResult.myResult != RunnerResult.OK ? null : myResult; - } - } - ManagedCompute managedCompute = new ManagedCompute(); - try { - ForkJoinPool.managedBlock(managedCompute); - } - catch (RejectedExecutionException ex) { - // Too many FJP threads: execute anyway in current thread - managedCompute.block(); - } - catch (InterruptedException ex) { - // Should not happen - throw new AssertionError(ex); - } - return managedCompute.getResult(); + CachedValueProvider.Result.create(ConcurrentFactoryMap.createMap(e -> { + DataflowResult result = runDFA(e); + return result.myResult != RunnerResult.OK ? null : result; + }), PsiModificationTracker.MODIFICATION_COUNT)); + return fileMap.get(body); } /** From 22eb431bc462b86df87a784beffe27a1920e7579 Mon Sep 17 00:00:00 2001 From: Kajus Kuncaitis Date: Fri, 3 Oct 2025 13:19:12 +0200 Subject: [PATCH 033/105] [gradle][IDEA-380904] Move DeprecatedGradleVersionIssue check to a more appropriate place. GitOrigin-RevId: 9770ba7cdf68b0c2d8c49ba1a7f05959103e57d9 --- .../execution/GradleExecutionHelper.java | 22 +++++++++++++++++++ .../project/GradleProjectResolver.java | 14 +++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java index fd09485c31f7..ff6475f99b79 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java @@ -1,6 +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 org.jetbrains.plugins.gradle.service.execution; +import com.intellij.build.events.MessageEvent; +import com.intellij.build.events.impl.BuildIssueEventImpl; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.ProcessOutputType; import com.intellij.openapi.application.Application; @@ -12,6 +14,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.ExternalSystemException; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener; +import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemBuildEvent; import com.intellij.openapi.externalSystem.service.execution.ExternalSystemExecutionAware; import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil; import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration; @@ -35,6 +38,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.VisibleForTesting; import org.jetbrains.plugins.gradle.connection.GradleConnectorService; +import org.jetbrains.plugins.gradle.issue.DeprecatedGradleVersionIssue; import org.jetbrains.plugins.gradle.jvmcompat.GradleJvmSupportMatrix; import org.jetbrains.plugins.gradle.properties.GradlePropertiesFile; import org.jetbrains.plugins.gradle.service.execution.cmd.GradleCommandLineOptionsProvider; @@ -560,6 +564,7 @@ public final class GradleExecutionHelper { } checkThatGradleBuildEnvironmentIsSupportedByIdea(buildEnvironment); + checkThatGradleBuildEnvironmentIsDeprecatedByIdea(context, buildEnvironment); return buildEnvironment; } @@ -576,6 +581,23 @@ public final class GradleExecutionHelper { } } + private static void checkThatGradleBuildEnvironmentIsDeprecatedByIdea( + @NotNull GradleExecutionContext context, + @NotNull BuildEnvironment buildEnvironment + ) { + final GradleVersion gradleVersion = GradleVersion.version(buildEnvironment.getGradle().getGradleVersion()); + if (GradleJvmSupportMatrix.isGradleDeprecatedByIdea(gradleVersion)) { + final String projectPath = context.getProjectPath(); + final var issue = new DeprecatedGradleVersionIssue(gradleVersion, projectPath); + context.getListener().onStatusChange( + new ExternalSystemBuildEvent( + context.getTaskId(), + new BuildIssueEventImpl(context.getTaskId(), issue, MessageEvent.Kind.WARNING) + ) + ); + } + } + private static void checkThatGradleBuildEnvironmentIsSupportedByIdea(@NotNull BuildEnvironment buildEnvironment) { var gradleVersion = GradleVersion.version(buildEnvironment.getGradle().getGradleVersion()); LOG.debug("Gradle version: " + gradleVersion); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 66f7810a0ee2..97e5e0a59144 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -1,7 +1,6 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.gradle.service.project; -import com.intellij.build.events.MessageEvent; import com.intellij.execution.configurations.ParametersList; import com.intellij.gradle.toolingExtension.impl.model.sourceSetDependencyModel.DefaultGradleSourceSetDependencyModel; import com.intellij.gradle.toolingExtension.impl.model.sourceSetModel.DefaultGradleSourceSetModel; @@ -48,15 +47,15 @@ import org.gradle.tooling.model.build.BuildEnvironment; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.tooling.model.idea.IdeaProject; import org.jetbrains.annotations.*; -import org.jetbrains.plugins.gradle.issue.DeprecatedGradleVersionIssue; -import org.jetbrains.plugins.gradle.jvmcompat.GradleJvmSupportMatrix; import org.jetbrains.plugins.gradle.model.*; import org.jetbrains.plugins.gradle.model.data.BuildParticipant; import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData; import org.jetbrains.plugins.gradle.model.data.CompositeBuildData; import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData; import org.jetbrains.plugins.gradle.remote.impl.GradleLibraryNamesMixer; -import org.jetbrains.plugins.gradle.service.execution.*; +import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper; +import org.jetbrains.plugins.gradle.service.execution.GradleInitScriptUtil; +import org.jetbrains.plugins.gradle.service.execution.GradleWrapperHelper; import org.jetbrains.plugins.gradle.service.modelAction.GradleIdeaModelHolder; import org.jetbrains.plugins.gradle.service.modelAction.GradleModelFetchActionRunner; import org.jetbrains.plugins.gradle.service.syncAction.impl.GradleSyncProjectConfigurator; @@ -285,13 +284,6 @@ public final class GradleProjectResolver implements ExternalSystemProjectResolve try (Scope ignore = gradleCallSpan.makeCurrent()) { var modelFetchActionResultHandler = GradleSyncProjectConfigurator.createModelFetchResultHandler(resolverContext); GradleModelFetchActionRunner.runAndTraceBuildAction(connection, resolverContext, buildAction, modelFetchActionResultHandler); - - var gradleVersion = resolverContext.getGradleVersion(); - if (GradleJvmSupportMatrix.isGradleDeprecatedByIdea(gradleVersion)) { - var projectPath = resolverContext.getProjectPath(); - var issue = new DeprecatedGradleVersionIssue(gradleVersion, projectPath); - resolverContext.report(MessageEvent.Kind.WARNING, issue); - } } catch (Throwable t) { gradleCallErrorsCount += 1; From a6b711dbf17c908d3c266213e047a3f0c1027eea Mon Sep 17 00:00:00 2001 From: Kajus Kuncaitis Date: Sat, 18 Oct 2025 12:33:39 +0200 Subject: [PATCH 034/105] [gradle][IDEA-380904] Add an assertion for 'Deprecated Gradle Version' in GradleDependencyNodeIndexTest GitOrigin-RevId: 25940f421704f9e8f146b6cb15f674ffcff99203 --- .../dependencyAnalyzer/GradleDependencyNodeIndexTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/gradle/java/testSources/dependencyAnalyzer/GradleDependencyNodeIndexTest.kt b/plugins/gradle/java/testSources/dependencyAnalyzer/GradleDependencyNodeIndexTest.kt index 32c6ea68d927..458c335ca07f 100644 --- a/plugins/gradle/java/testSources/dependencyAnalyzer/GradleDependencyNodeIndexTest.kt +++ b/plugins/gradle/java/testSources/dependencyAnalyzer/GradleDependencyNodeIndexTest.kt @@ -9,6 +9,7 @@ import org.gradle.util.GradleVersion import org.jetbrains.plugins.gradle.dependency.analyzer.GradleDependencyNodeIndex import org.jetbrains.plugins.gradle.frameworkSupport.GradleDsl import org.jetbrains.plugins.gradle.frameworkSupport.buildscript.GradleBuildScriptBuilder.Companion.buildScript +import org.jetbrains.plugins.gradle.importing.BuildViewMessagesImportingTestCase.Companion.assertNodeWithDeprecatedGradleWarning import org.jetbrains.plugins.gradle.testFramework.annotations.AllGradleVersionsSource import org.jetbrains.plugins.gradle.testFramework.util.assumeThatConfigurationCacheIsSupported import org.jetbrains.plugins.gradle.testFramework.util.assumeThatIsolatedProjectsIsSupported @@ -64,6 +65,7 @@ class GradleDependencyNodeIndexTest : GradleDependencyNodeIndexTestCase() { assertSyncViewTree { assertNode("successful") { + assertNodeWithDeprecatedGradleWarning(gradleVersion) assertNode(":ijCollectDependencies") } } From 2410a52d7bca1e6d9a16de4fcd03b8a4b3395b13 Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Mon, 20 Oct 2025 12:25:11 +0200 Subject: [PATCH 035/105] [kotlin] KTIJ-36027 Fix approximation calculation GitOrigin-RevId: 401cf8e73b79e1c32772314f1d160a03870c3af8 --- .../idea/k2/codeinsight/hints/printType.kt | 17 ++++++++--------- ...ReferenceTypeHintsProviderTestGenerated.java | 5 +++++ ...ReferenceTypeHintsProviderTestGenerated.java | 5 +++++ .../types/AnonymousAnyCollectionElement.k2.kt | 8 ++++++++ .../types/AnonymousAnyCollectionElement.kt | 8 ++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.k2.kt create mode 100644 plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.kt diff --git a/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/hints/printType.kt b/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/hints/printType.kt index 314e0e9aed90..ec7d1f9a54f1 100644 --- a/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/hints/printType.kt +++ b/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/hints/printType.kt @@ -8,6 +8,7 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.components.DefaultTypeClassIds +import org.jetbrains.kotlin.analysis.api.components.approximateToDenotableSupertypeOrSelf import org.jetbrains.kotlin.analysis.api.components.isMarkedNullable import org.jetbrains.kotlin.analysis.api.components.withNullability import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind @@ -122,18 +123,16 @@ internal fun PresentationTreeBuilder.printKtType(type: KaType) { if (markedNullable) text("?") } +@OptIn(KaExperimentalApi::class) context(_: KaSession) private fun PresentationTreeBuilder.printNonErrorClassType(type: KaClassType, anotherType: KaClassType? = null) { - val classType = - when (val symbol = type.symbol) { - is KaClassSymbol if symbol.classKind == KaClassKind.ANONYMOUS_OBJECT -> { - (symbol.superTypes.firstOrNull() as? KaClassType) ?: type - } + val classType = when (val symbol = type.symbol) { + is KaClassSymbol if symbol.classKind == KaClassKind.ANONYMOUS_OBJECT -> + type.approximateToDenotableSupertypeOrSelf(allowLocalDenotableTypes = false) as? KaClassType ?: type - else -> { - type - } - } + else -> type + + } val truncatedName = truncatedName(classType) if (truncatedName.isNotEmpty()) { if (classType.classId.isLocal) { diff --git a/plugins/kotlin/code-insight/kotlin.code-insight.k2/test/org/jetbrains/kotlin/idea/k2/hints/KtReferenceTypeHintsProviderTestGenerated.java b/plugins/kotlin/code-insight/kotlin.code-insight.k2/test/org/jetbrains/kotlin/idea/k2/hints/KtReferenceTypeHintsProviderTestGenerated.java index 3125b2ba8501..16f944bb9f62 100644 --- a/plugins/kotlin/code-insight/kotlin.code-insight.k2/test/org/jetbrains/kotlin/idea/k2/hints/KtReferenceTypeHintsProviderTestGenerated.java +++ b/plugins/kotlin/code-insight/kotlin.code-insight.k2/test/org/jetbrains/kotlin/idea/k2/hints/KtReferenceTypeHintsProviderTestGenerated.java @@ -30,6 +30,11 @@ public class KtReferenceTypeHintsProviderTestGenerated extends AbstractKtReferen KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("AnonymousAnyCollectionElement.kt") + public void testAnonymousAnyCollectionElement() throws Exception { + runTest("../../idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.kt"); + } + @TestMetadata("AnonymousCollectionElement.kt") public void testAnonymousCollectionElement() throws Exception { runTest("../../idea/tests/testData/codeInsight/hints/types/AnonymousCollectionElement.kt"); diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/codeInsight/hints/KotlinReferenceTypeHintsProviderTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/codeInsight/hints/KotlinReferenceTypeHintsProviderTestGenerated.java index ed3e808bee5b..77c2205edcfc 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/codeInsight/hints/KotlinReferenceTypeHintsProviderTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/codeInsight/hints/KotlinReferenceTypeHintsProviderTestGenerated.java @@ -30,6 +30,11 @@ public class KotlinReferenceTypeHintsProviderTestGenerated extends AbstractKotli KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("AnonymousAnyCollectionElement.kt") + public void testAnonymousAnyCollectionElement() throws Exception { + runTest("testData/codeInsight/hints/types/AnonymousAnyCollectionElement.kt"); + } + @TestMetadata("AnonymousCollectionElement.kt") public void testAnonymousCollectionElement() throws Exception { runTest("testData/codeInsight/hints/types/AnonymousCollectionElement.kt"); diff --git a/plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.k2.kt b/plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.k2.kt new file mode 100644 index 000000000000..33c22c7eed62 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.k2.kt @@ -0,0 +1,8 @@ +// MODE: all +fun x() { + val map/*<# : |[kotlin.collections.List:kotlin.fqn.class]List|<|[kotlin.Any:kotlin.fqn.class]Any|> #>*/ = listOf(1, 2, 3).map { + object { + override fun toString(): String = it.toString() + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.kt b/plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.kt new file mode 100644 index 000000000000..12e9ca5f1462 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/codeInsight/hints/types/AnonymousAnyCollectionElement.kt @@ -0,0 +1,8 @@ +// MODE: all +fun x() { + val map/*<# : |[kotlin.collections.List:kotlin.fqn.class]List|<|[x..:kotlin.fqn.class]``|> #>*/ = listOf(1, 2, 3).map { + object { + override fun toString(): String = it.toString() + } + } +} \ No newline at end of file From 9842a864b89dcc5285a1675c7a7d830e3bf330af Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Mon, 20 Oct 2025 13:31:55 +0200 Subject: [PATCH 036/105] [threading] IJPL-207730: Use a correct lock-wrapping dispatcher for running project opened listeners GitOrigin-RevId: 08a433af8194345d999813519604e32e2f123962 --- .../project/impl/ProjectManagerImpl.kt | 4 ++-- .../project/impl/ProjectOpeningTest.kt | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) 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-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() + } } From 84065b5ee032ef61a4ae9362a5ddd45fe304e93b Mon Sep 17 00:00:00 2001 From: Dmitry Drobotov Date: Fri, 17 Oct 2025 17:43:56 +0200 Subject: [PATCH 037/105] [fus][a11y] IJPL-207044 Implement collector for third-party accessibility tools and settings GitOrigin-RevId: cd05e6f401cb06eae66e4da7d57012b7df52b1b6 --- .../AccessibilityToolsStateCollector.kt | 120 ++++++++++++++++++ .../src/META-INF/PlatformExtensions.xml | 1 + 2 files changed, 121 insertions(+) create mode 100644 platform/platform-impl/src/com/intellij/accessibility/AccessibilityToolsStateCollector.kt 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-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index e13412ced01c..ed7e402d8699 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -1027,6 +1027,7 @@ + Date: Mon, 20 Oct 2025 14:27:26 +0200 Subject: [PATCH 038/105] [debugger] more logging for exceptions while class filters matching GitOrigin-RevId: c31f421e28df9f67cb7eab3851cc2892b688ed17 --- .../impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 43bb22015600..e5791749f403 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -190,6 +190,9 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { catch (PatternSyntaxException e) { LOG.debug(e); } + catch (Exception e) { + LOG.error("Exception while matching ClassFilter \"" + classFilter.getPattern() + "\" with the input \"" + qName + "\"", e); + } return false; } From df47c2f32558966771176ad052641a9861d6fc2f Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Mon, 20 Oct 2025 13:32:40 +0200 Subject: [PATCH 039/105] IJPL-213787 Islands: llm.riderNext.enabled key is defined in LLM plugin GitOrigin-RevId: d25d58d7ef7fd42b4b4e9ed01f3ec163ba3560ab --- .../src/com/intellij/ide/ui/laf/ThemeListProviderImpl.kt | 9 +-------- .../openapi/application/impl/islands/IslandsFeedback.kt | 4 +--- .../application/impl/islands/IslandsUICustomization.kt | 3 +-- 3 files changed, 3 insertions(+), 13 deletions(-) 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/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 From 6a86da59c46124134c6382997a81a3b9eea6016b Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Fri, 26 Sep 2025 12:42:29 +0200 Subject: [PATCH 040/105] Looking for help topics and search indexes in different locations based on current locale, with fallback to en-us (cherry picked from commit e28de438f9ac6bec5a7e16f4a668eb4f567bc6f3) IJ-MR-176799 GitOrigin-RevId: f7467209a3e04cdd257f2cd005e0a2d8ac3afb2f --- .../builtInHelp/BuiltInHelpManager.kt | 18 +++++++-- .../builtInHelp/HelpRequestHandlerBase.kt | 38 ++++++++++++++++++- .../builtInHelp/HelpSearchRequestHandler.kt | 4 +- .../builtInHelp/search/HelpSearch.kt | 14 ++++--- 4 files changed, 63 insertions(+), 11 deletions(-) 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..96ffb2b8744d 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,7 @@ import java.lang.String.valueOf import java.net.* import java.nio.charset.StandardCharsets -private val LOG = Logger.getInstance(BuiltInHelpManager::class.java) +val LOG = Logger.getInstance(BuiltInHelpManager::class.java) class BuiltInHelpManager : HelpManager() { @@ -42,6 +43,7 @@ class BuiltInHelpManager : HelpManager() { val helpIdToUse = URLEncoder.encode(helpId, StandardCharsets.UTF_8) ?: "top" logWillOpenHelpId(helpIdToUse) + val locale = LocalizationStateService.getInstance()?.selectedLocale var activeKeymap: Keymap? = KeymapManagerEx.getInstanceEx().getActiveKeymap() if (true == activeKeymap?.canModify()) @@ -64,9 +66,19 @@ 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/?${ + //Later we'll add more languages here, for now it's either Chinese or English default + val langToUrl = mapOf( + "zh" to "zh-cn", + ) + + val selectedLocale = LocalizationStateService.getInstance()?.selectedLocale + + val langPart = langToUrl[selectedLocale] ?: "en-us" + "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}/${langPart}/help/?${ helpIdToUse }$activeKeymapParam" } 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..49b46eff8dda 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt @@ -8,6 +8,7 @@ import io.netty.handler.codec.http.HttpHeaders import io.netty.handler.codec.http.HttpRequest import org.jetbrains.annotations.NonNls import org.jetbrains.ide.HttpRequestHandler +import java.io.IOException import kotlin.io.path.Path import kotlin.io.path.extension import kotlin.io.path.nameWithoutExtension @@ -23,6 +24,14 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() { return super.isAccessible(request) && request.uri().contains(prefix) } + fun getDefaultLocalePath(): String { + return "en-us" + } + + fun getRequestLocalePath(request: HttpRequest): String { + return request.uri().substringBefore(prefix).substringBeforeLast("/") + } + protected fun sendResource( resourceName: String, resourceLocation: String, @@ -32,20 +41,45 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() { ): Boolean { val isImage = resourceLocation.contains("/img/") + + //We don't ship dark images because of storage concerns, yet frontent 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 + if (isImage) "images" else "${getRequestLocalePath(request)}/topics", retrieveName ) + + //First try to deliver a topic from the selected locale and fallback to en-us when needed + val contentToSend = resStream.use { res -> + try { + res.readAllBytes() + } + catch (e: IOException) { + LOG.warn(e) + //Because of space concerns, images are only shipped for en-us anyway + if (!isImage) { + ResourceUtil.getResourceAsStream( + HelpRequestHandlerBase::class.java.classLoader, + "${getDefaultLocalePath()}/topics", retrieveName + ).use { res -> + res.readAllBytes() + } + } + else null + } + } + return sendData( - resStream?.readAllBytes() ?: throw Exception("$resourceName not found in $resourceLocation via ${request.uri()}"), resourceName, + contentToSend ?: 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..651317040e35 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt @@ -11,12 +11,14 @@ import org.jetbrains.annotations.NonNls class HelpSearchRequestHandler : HelpRequestHandlerBase() { @NonNls override val prefix: String = "/search/" + 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(), + sendData( + HelpSearch.search(query, if (maxHitsParam != null) Integer.parseInt(maxHitsParam) else 100).encodeToByteArray(), "data.json", request, context.channel(), 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..b97c2dd2412b 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,4 +1,6 @@ // 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 @@ -22,6 +24,8 @@ 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 +36,10 @@ class HelpSearch { private val analyzer: StandardAnalyzer = StandardAnalyzer() @NotNull - fun search(query: String?, maxHits: Int): String { + fun search(query: String?, maxHits: Int, locale: String = "en-us"): 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 @@ -45,13 +49,13 @@ class HelpSearch { Files.createDirectories(indexDir) //Read required names from rlist and then load resources based off of them - ResourceUtil.getResourceAsStream(HelpSearch::class.java.classLoader, "search", "rlist") + ResourceUtil.getResourceAsStream(HelpSearch::class.java.classLoader, "${locale}/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) + "${locale}/search", line) ?.use { resourceStream -> path.safeOutputStream().use { resourceOutput -> resourceOutput.write(resourceStream.readAllBytes()) @@ -111,7 +115,7 @@ class HelpSearch { finally { indexDirectory?.close() reader?.close() - indexDir.toFile().deleteRecursively() + indexDir.deleteRecursively() } } return NOT_FOUND From f83731f79daf5f759d0157188bac95ab82c7a783 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Wed, 1 Oct 2025 14:58:05 +0200 Subject: [PATCH 041/105] Use current base path for en-us and all other fallback content (cherry picked from commit d2a7b811615335aea7aa15631309477706b0f0c1) IJ-MR-176799 GitOrigin-RevId: 6614a0020c9d8693c0a5700049a6709825356f0a --- .../builtInHelp/HelpRequestHandlerBase.kt | 16 +++++++--------- .../builtInHelp/HelpSearchRequestHandler.kt | 18 ++++++++++++------ .../src/com/jetbrains/builtInHelp/Utils.kt | 2 +- .../jetbrains/builtInHelp/search/HelpSearch.kt | 4 ++-- 4 files changed, 22 insertions(+), 18 deletions(-) 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 49b46eff8dda..dc39b9305abb 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt @@ -19,17 +19,15 @@ 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 getDefaultLocalePath(): String { - return "en-us" - } - fun getRequestLocalePath(request: HttpRequest): String { - return request.uri().substringBefore(prefix).substringBeforeLast("/") + val requestLocale = request.uri().substringBefore(prefix).substringBeforeLast("/") + return if (supportedLocales.contains(requestLocale)) requestLocale else "" } protected fun sendResource( @@ -42,7 +40,7 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() { val isImage = resourceLocation.contains("/img/") - //We don't ship dark images because of storage concerns, yet frontent might still request them and to avoid 404 we provide fallback + //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) @@ -58,18 +56,18 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() { if (isImage) "images" else "${getRequestLocalePath(request)}/topics", retrieveName ) - //First try to deliver a topic from the selected locale and fallback to en-us when needed + //First, try to deliver a topic from the selected locale and fallback to en-us when needed val contentToSend = resStream.use { res -> try { res.readAllBytes() } catch (e: IOException) { LOG.warn(e) - //Because of space concerns, images are only shipped for en-us anyway + //Because of space concerns, images are only shipped for en-us anyway and stored under /images if (!isImage) { ResourceUtil.getResourceAsStream( HelpRequestHandlerBase::class.java.classLoader, - "${getDefaultLocalePath()}/topics", retrieveName + "topics", retrieveName ).use { res -> res.readAllBytes() } 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 651317040e35..c004b885ba80 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpSearchRequestHandler.kt @@ -12,17 +12,23 @@ 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()) + 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..c7cd96d68512 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt @@ -13,7 +13,7 @@ class Utils { @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") 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 b97c2dd2412b..9d377ad65887 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 @@ -36,7 +36,7 @@ class HelpSearch { private val analyzer: StandardAnalyzer = StandardAnalyzer() @NotNull - fun search(query: String?, maxHits: Int, locale: String = "en-us"): String { + fun search(query: String?, maxHits: Int, locale: String): String { if (query != null) { val indexDir: Path? = Files.createTempDirectory("${locale}-search-index") @@ -55,7 +55,7 @@ class HelpSearch { lines.forEach { line -> val path = Paths.get(indexDirPath, line) ResourceUtil.getResourceAsStream(HelpSearch::class.java.classLoader, - "${locale}/search", line) + "$locale/search", line) ?.use { resourceStream -> path.safeOutputStream().use { resourceOutput -> resourceOutput.write(resourceStream.readAllBytes()) From 4175c20d9f63c7e7205e77cb17ec73f841524a41 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Wed, 1 Oct 2025 17:09:45 +0200 Subject: [PATCH 042/105] Use current base path for en-us and all other fallback content, step 1 (cherry picked from commit b872efa164618a45376871ea484ca150d49d0fdc) IJ-MR-176799 GitOrigin-RevId: d17aa510c949ccfa6e55b1128ff4ab17e1142464 --- .../builtInHelp/BuiltInHelpManager.kt | 22 +++++++++++-------- .../builtInHelp/HelpRequestHandlerBase.kt | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) 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 96ffb2b8744d..fa979456e1b4 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt @@ -18,7 +18,12 @@ import java.lang.String.valueOf import java.net.* import java.nio.charset.StandardCharsets -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 = mapOf( + "zh-cn" to "zh-cn", +) class BuiltInHelpManager : HelpManager() { @@ -70,15 +75,14 @@ class BuiltInHelpManager : HelpManager() { else "&keymap=${URLEncoder.encode(activeKeymap.presentableName, StandardCharsets.UTF_8)}" - //Later we'll add more languages here, for now it's either Chinese or English default - val langToUrl = mapOf( - "zh" to "zh-cn", - ) + //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 "" - val selectedLocale = LocalizationStateService.getInstance()?.selectedLocale - - val langPart = langToUrl[selectedLocale] ?: "en-us" - "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}/${langPart}/help/?${ + "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}${langPart}/help/?${ helpIdToUse }$activeKeymapParam" } 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 dc39b9305abb..cb3ed09021e2 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpRequestHandlerBase.kt @@ -26,7 +26,7 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() { } fun getRequestLocalePath(request: HttpRequest): String { - val requestLocale = request.uri().substringBefore(prefix).substringBeforeLast("/") + val requestLocale = request.uri().substringBefore(prefix).substringAfter("/") return if (supportedLocales.contains(requestLocale)) requestLocale else "" } From 8cb696122778c9828de8e9807d5dbafa36fd27fb Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Thu, 2 Oct 2025 14:41:31 +0200 Subject: [PATCH 043/105] Move resource reading and fallback to the dedicated utility method (cherry picked from commit 02de810b56bb9792373f41c5773f19d5dd10a8b4) IJ-MR-176799 GitOrigin-RevId: 0e66f672b6bc2cf30a8002c101f14a9a65f40b6c --- .../builtInHelp/HelpContentRequestHandler.kt | 12 +++---- .../builtInHelp/HelpRequestHandlerBase.kt | 31 ++--------------- .../src/com/jetbrains/builtInHelp/Utils.kt | 33 +++++++++++++++++++ .../builtInHelp/search/HelpSearch.kt | 32 +++++++++--------- 4 files changed, 59 insertions(+), 49 deletions(-) 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..3b763e3bdf5f 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpContentRequestHandler.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/HelpContentRequestHandler.kt @@ -43,7 +43,7 @@ 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}${getRequestLocalePath(request)}/help/${ map.getUrlForId( name ) @@ -61,16 +61,16 @@ class HelpContentRequestHandler : HelpRequestHandlerBase() { } } + val locale = getRequestLocalePath(request) + 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 cb3ed09021e2..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,14 +1,12 @@ // 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 import io.netty.handler.codec.http.HttpRequest import org.jetbrains.annotations.NonNls import org.jetbrains.ide.HttpRequestHandler -import java.io.IOException import kotlin.io.path.Path import kotlin.io.path.extension import kotlin.io.path.nameWithoutExtension @@ -51,33 +49,10 @@ abstract class HelpRequestHandlerBase : HttpRequestHandler() { else -> resourceName } - val resStream = ResourceUtil.getResourceAsStream( - HelpRequestHandlerBase::class.java.classLoader, - if (isImage) "images" else "${getRequestLocalePath(request)}/topics", retrieveName - ) - - //First, try to deliver a topic from the selected locale and fallback to en-us when needed - val contentToSend = resStream.use { res -> - try { - res.readAllBytes() - } - catch (e: IOException) { - LOG.warn(e) - //Because of space concerns, images are only shipped for en-us anyway and stored under /images - if (!isImage) { - ResourceUtil.getResourceAsStream( - HelpRequestHandlerBase::class.java.classLoader, - "topics", retrieveName - ).use { res -> - res.readAllBytes() - } - } - else null - } - } - + val content = Utils.getResourceWithFallback(if (isImage) "images" else "topics", + retrieveName, getRequestLocalePath(request)) return sendData( - contentToSend ?: 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/Utils.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt index c7cd96d68512..6103ba188164 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt @@ -5,18 +5,51 @@ 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 +import java.io.IOException class Utils { companion object { @NonNls private const val EMPTY_STRING = "" + @NonNls 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: IOException) { + LOG.warn(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: IOException) { + null + } + } + } + } + } + + @JvmStatic @NonNls fun getStoredValue(key: SettingsPage.SettingKey, default: String): String { 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 9d377ad65887..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 @@ -5,8 +5,8 @@ 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 @@ -19,6 +19,7 @@ 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 @@ -48,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, "${locale}/search", "rlist") - .use { resourceList -> - BufferedReader(InputStreamReader(resourceList)).useLines { lines -> - lines.forEach { line -> - val path = Paths.get(indexDirPath, line) - ResourceUtil.getResourceAsStream(HelpSearch::class.java.classLoader, - "$locale/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) From 3391889aec393030d1acb6441df942df246e6620 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Thu, 2 Oct 2025 14:45:23 +0200 Subject: [PATCH 044/105] Make sure redirect also is locale-aware (cherry picked from commit 0e88bd35b23d18ef1bcd74e8b8b1df9e9d9205a3) IJ-MR-176799 GitOrigin-RevId: bf161f5ee26f09f6fe5d1100508fe9ea6ec6009b --- .../jetbrains/builtInHelp/HelpContentRequestHandler.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 3b763e3bdf5f..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}${getRequestLocalePath(request)}/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", @@ -61,8 +64,6 @@ class HelpContentRequestHandler : HelpRequestHandlerBase() { } } - val locale = getRequestLocalePath(request) - when (val resourceName: String = urlDecoder.path().substringAfterLast("/")) { "config.json" -> { From c750aeb7533bbf6681afe3cc6247e73ac6a5caa4 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Thu, 2 Oct 2025 16:46:29 +0200 Subject: [PATCH 045/105] Exception type adjusted (cherry picked from commit 58010bb5e51ab6e5e5d4a38d6dfa255eb2649f68) IJ-MR-176799 GitOrigin-RevId: f32da2308ae3b75b20b6557e0d01dd2afac45376 --- .../src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt | 3 +-- .../built-in-help/src/com/jetbrains/builtInHelp/Utils.kt | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) 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 fa979456e1b4..729394357d4c 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt @@ -21,7 +21,7 @@ import java.nio.charset.StandardCharsets val LOG: Logger = Logger.getInstance(BuiltInHelpManager::class.java) //Later we'll add more languages here, for now it's Chinese only -val localesToUrls = mapOf( +val localesToUrls: Map = mapOf( "zh-cn" to "zh-cn", ) @@ -48,7 +48,6 @@ class BuiltInHelpManager : HelpManager() { val helpIdToUse = URLEncoder.encode(helpId, StandardCharsets.UTF_8) ?: "top" logWillOpenHelpId(helpIdToUse) - val locale = LocalizationStateService.getInstance()?.selectedLocale var activeKeymap: Keymap? = KeymapManagerEx.getInstanceEx().getActiveKeymap() if (true == activeKeymap?.canModify()) 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 6103ba188164..e942606d6814 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt @@ -8,7 +8,6 @@ import com.intellij.ide.util.PropertiesComponent import com.intellij.util.ResourceUtil import com.jetbrains.builtInHelp.settings.SettingsPage import org.jetbrains.annotations.NonNls -import java.io.IOException class Utils { companion object { @@ -31,8 +30,8 @@ class Utils { try { res.readAllBytes() } - catch (e: IOException) { - LOG.warn(e) + 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, @@ -41,7 +40,8 @@ class Utils { try { res.readAllBytes() } - catch (e: IOException) { + catch (e: Exception) { + LOG.warn("Fallback resource failed", e) null } } From fc4f28218ae33768456c587b38244bc01619829b Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Thu, 2 Oct 2025 19:33:04 +0200 Subject: [PATCH 046/105] Removed unnecessary return value (cherry picked from commit dd4ccc527e1d4811815af4da7ddc9fc66ffef1b4) IJ-MR-176799 GitOrigin-RevId: ebf9ec3722d1f674eb9e86880e4e172b002189ff --- .../src/com/jetbrains/builtInHelp/Utils.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 e942606d6814..87cbe9284945 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/Utils.kt @@ -63,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 From 3eccfee2dd3aa3f8e94d70b7b4a6527e1eb6a630 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Wed, 8 Oct 2025 18:19:20 +0200 Subject: [PATCH 047/105] Build Lucene indexes for provided i18n content (cherry picked from commit 490b96ca12e002f5f6f4a95a3f5ddacf0fbea8b2) IJ-MR-176799 GitOrigin-RevId: 2ff1ad8248d97f43fd941f07752fdd7f7cbccb1a --- .../intellij/build/impl/BuiltInHelpPlugin.kt | 66 ++++++++++++++----- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 72f77cda6f3c..4795661f621e 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -14,6 +14,7 @@ import org.jetbrains.intellij.build.telemetry.TraceManager.spanBuilder import org.jetbrains.intellij.build.telemetry.use import java.nio.file.Files import java.nio.file.Path +import kotlin.io.path.exists internal const val BUILT_IN_HELP_MODULE_NAME = "intellij.builtInHelp" @@ -88,25 +89,60 @@ private fun pluginXml(buildContext: BuildContext, version: String): String { */ private val helpIndexerMutex = Mutex() + +/* Offline help plugins include a separate set of help topics for each of the supported languages. +This is a map of language code to descriptors that define resources associated with that language. + */ + +private data class LanguageResourcesDescriptor( + val isRequired: Boolean = false, + val resPath: String, + val resList: List = listOf("topics", "images", "search"), +) + +private val supportedLanguages = mapOf( + //Localized resources don't include images + Pair("zh-cn", LanguageResourcesDescriptor(resPath = "zh-cn/", resList = listOf("topics", "search"))), + Pair("en-us", LanguageResourcesDescriptor(isRequired = true, resPath = "")) +) + private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: List, assetJar: Path, context: CompilationContext) { spanBuilder("index help topics").use { helpIndexerMutex.withLock { - runJavaForIntellijModule( - context = context, mainClass = "com.jetbrains.builtInHelp.indexer.HelpIndexer", - args = listOf( - resourceRoot.resolve("search").toString(), - resourceRoot.resolve("topics").toString(), - ), - jvmArgs = emptyList(), - classPath = classPath, - ) + supportedLanguages.forEach { (lang, descriptor) -> + val topicPath = resourceRoot.resolve("${descriptor.resPath}topics") + + if (topicPath.exists()) + runJavaForIntellijModule( + context = context, mainClass = "com.jetbrains.builtInHelp.indexer.HelpIndexer", + args = listOf( + resourceRoot.resolve("${descriptor.resPath}search").toString(), + topicPath.toString(), + ), + jvmArgs = emptyList(), + classPath = classPath, + ) + else + Span.current().addEvent("skip \"${lang}\" for offline help plugin because it was not supplied. ") + } } - writeNewZipWithoutIndex(file = assetJar, compress = true) { zipCreator -> - val archiver = ZipArchiver() - archiver.setRootDir(resourceRoot) - archiveDir(startDir = resourceRoot.resolve("topics"), addFile = { archiver.addFile(it, zipCreator) }) - archiveDir(startDir = resourceRoot.resolve("images"), addFile = { archiver.addFile(it, zipCreator) }) - archiveDir(startDir = resourceRoot.resolve("search"), addFile = { archiver.addFile(it, zipCreator) }) + + supportedLanguages.forEach { (lang, descriptor) -> + writeNewZipWithoutIndex(file = assetJar, compress = true) { zipCreator -> + val rootDir = resourceRoot.resolve(descriptor.resPath) + if (rootDir.exists()) { + val archiver = ZipArchiver() + archiver.setRootDir(rootDir) + + descriptor.resList.forEach { resDir -> + archiveDir( + startDir = resourceRoot.resolve(resDir), + addFile = { archiver.addFile(it, zipCreator) }) + } + } + } } } } + + From 5bd35b8d0fe91d9280dea1c6a7788106f76c340f Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Mon, 13 Oct 2025 11:13:28 +0200 Subject: [PATCH 048/105] Changing the order of loop to include all resources in a single ZIP (cherry picked from commit f97c0305e319f630ef1d50040cfbd9cb782846f2) IJ-MR-176799 GitOrigin-RevId: 2c1a4df8be1a74489ca6cc8fbad670c1e96d31b6 --- .../org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 4795661f621e..5d86b27c9ce8 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -127,12 +127,13 @@ private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: L } } - supportedLanguages.forEach { (lang, descriptor) -> - writeNewZipWithoutIndex(file = assetJar, compress = true) { zipCreator -> + writeNewZipWithoutIndex(file = assetJar, compress = true) { zipCreator -> + supportedLanguages.forEach { (lang, descriptor) -> val rootDir = resourceRoot.resolve(descriptor.resPath) if (rootDir.exists()) { val archiver = ZipArchiver() archiver.setRootDir(rootDir) + Span.current().addEvent("adding \"${lang}\" to the resulting ZIP.") descriptor.resList.forEach { resDir -> archiveDir( @@ -140,6 +141,8 @@ private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: L addFile = { archiver.addFile(it, zipCreator) }) } } + else + Span.current().addEvent("skip adding \"${lang}\" to the resulting ZIP because it was not supplied.") } } } From b146dd21f32e664d257702ff05d2cf66700a665b Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Mon, 13 Oct 2025 17:29:34 +0200 Subject: [PATCH 049/105] Fix resolve of plugin resources in relation to its lang dir (cherry picked from commit 75e094965eab72b7cce708ace6dfab717eda05c1) IJ-MR-176799 GitOrigin-RevId: 8c28c47576a2b9e651be0cf0da08ca56b04740d8 --- .../src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 5d86b27c9ce8..67b3cc562f48 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -137,7 +137,7 @@ private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: L descriptor.resList.forEach { resDir -> archiveDir( - startDir = resourceRoot.resolve(resDir), + startDir = rootDir.resolve(resDir), addFile = { archiver.addFile(it, zipCreator) }) } } From d62a093c0bb1833845af1dd03c94a71f14426966 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Tue, 14 Oct 2025 12:17:12 +0200 Subject: [PATCH 050/105] Fix resolve of plugin resources in relation to its lang dir, step 2 (cherry picked from commit 7ba87817cccba823da1adf46e2c48262e8796b8e) IJ-MR-176799 GitOrigin-RevId: dd3c994565cc4743e9f0f37b9b43c767f5609a4b --- .../src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 67b3cc562f48..9f43c5ab47a4 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -137,7 +137,7 @@ private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: L descriptor.resList.forEach { resDir -> archiveDir( - startDir = rootDir.resolve(resDir), + startDir = rootDir, addFile = { archiver.addFile(it, zipCreator) }) } } From 5b59a9f1dc5e61a6c47e627fc74cde69c0dc043b Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Tue, 14 Oct 2025 15:46:34 +0200 Subject: [PATCH 051/105] Maybe if we don't reset the root, eventually it will pick the subfolders up (cherry picked from commit aae201038e8d2e677f5c947aa66e81198bc50899) IJ-MR-176799 GitOrigin-RevId: 8fc55b293ff5c2ff355e238fb6a542e7e4e68660 --- .../org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 9f43c5ab47a4..16800202086d 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -128,13 +128,13 @@ private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: L } writeNewZipWithoutIndex(file = assetJar, compress = true) { zipCreator -> + val archiver = ZipArchiver() + archiver.setRootDir(resourceRoot) + supportedLanguages.forEach { (lang, descriptor) -> val rootDir = resourceRoot.resolve(descriptor.resPath) if (rootDir.exists()) { - val archiver = ZipArchiver() - archiver.setRootDir(rootDir) Span.current().addEvent("adding \"${lang}\" to the resulting ZIP.") - descriptor.resList.forEach { resDir -> archiveDir( startDir = rootDir, From da5036e6b01b23efee153f88d92dca41da957e39 Mon Sep 17 00:00:00 2001 From: Egor Malyshev Date: Tue, 14 Oct 2025 16:14:25 +0200 Subject: [PATCH 052/105] Maybe if we don't reset the root, eventually it will pick the subfolders up (cherry picked from commit b34734e53219822fb2157b7bcb3f6e3b7bc84dc5) IJ-MR-176799 GitOrigin-RevId: 0d729fa855c3f75de1fa283275ed9792692a0535 --- .../org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 16800202086d..fa81b76b53b1 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -132,12 +132,12 @@ private suspend fun buildResourcesForHelpPlugin(resourceRoot: Path, classPath: L archiver.setRootDir(resourceRoot) supportedLanguages.forEach { (lang, descriptor) -> - val rootDir = resourceRoot.resolve(descriptor.resPath) - if (rootDir.exists()) { + val langRootDir = resourceRoot.resolve(descriptor.resPath) + if (langRootDir.exists()) { Span.current().addEvent("adding \"${lang}\" to the resulting ZIP.") descriptor.resList.forEach { resDir -> archiveDir( - startDir = rootDir, + startDir = langRootDir.resolve(resDir), addFile = { archiver.addFile(it, zipCreator) }) } } From 25c7d26b59ed0bf1ebac65adaf4fb9c7a9398657 Mon Sep 17 00:00:00 2001 From: "Egor.Skrypnikov" Date: Mon, 13 Oct 2025 17:44:51 +0200 Subject: [PATCH 053/105] [Rider] add a setting so that the doc comment calculation in Rider would be OFF by default GitOrigin-RevId: 855cb07f224b3dc16499338cd4b90df2817bb9de --- .../options/editor/EditorAppearanceConfigurable.kt | 4 +++- .../options/editor/EditorAppearanceConfigurableEP.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.kt b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.kt index 95f6b59f174e..8369c04f9bb6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.kt +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.kt @@ -22,6 +22,7 @@ import com.intellij.ui.components.JBCheckBox import com.intellij.ui.dsl.builder.* import com.intellij.ui.dsl.listCellRenderer.textListCellRenderer import com.intellij.util.PlatformUtils +import org.jetbrains.annotations.ApiStatus import javax.swing.DefaultComboBoxModel // @formatter:off @@ -49,7 +50,8 @@ private val myRenderedDocCheckBox get() = CheckboxDescriptor private val myUseEditorFontInInlays get() = CheckboxDescriptor(ApplicationBundle.message("use.editor.font.for.inlays"), model::isUseEditorFontInInlays, model::setUseEditorFontInInlays) // @formatter:on -internal class EditorAppearanceConfigurable : BoundCompositeSearchableConfigurable( +@ApiStatus.Internal +class EditorAppearanceConfigurable : BoundCompositeSearchableConfigurable( 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 { } From 211882f40d1242b5e9715d17c815a5485bb6e23a Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 14 Oct 2025 14:14:18 +0200 Subject: [PATCH 054/105] [Rider] RIDER-131203 fix zombies being delimited GitOrigin-RevId: cdff7fb977318898c3489f4523907f5e687af798 --- .../codeVision/ui/model/ZombieCodeVisionEntry.kt | 1 + .../ui/renderers/painters/CodeVisionListPainter.kt | 2 +- .../ui/renderers/painters/ICodeVisionEntryBasePainter.kt | 2 +- .../ui/renderers/providers/CodeVisionPainterProviders.kt | 6 +++++- .../codeInsight/daemon/impl/grave/CodeVisionZombie.kt | 9 +++++++-- 5 files changed, 15 insertions(+), 5 deletions(-) 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..17df107f4306 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 @@ -13,6 +13,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..9ab692c97ecc 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 @@ -50,7 +50,11 @@ private class ZombieCodeVisionEntryPainter : DefaultCodeVisionPainter { return INSTANCE.getPainter(this@painter) 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) } } From 100f6b3b3fe89e6bc88b1dc3de7591b6802193b7 Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 14 Oct 2025 14:35:51 +0200 Subject: [PATCH 055/105] [Rider] RIDER-131203 move zero width lens to the platform to pass tests GitOrigin-RevId: a20dead0721f0e994a60d3ffcf94e1d0118762be --- .../ui/model/ZeroWidthCodeVisionEntry.kt | 14 +++++++++ .../providers/CodeVisionPainterProviders.kt | 31 +++++++++++++++++++ .../src/META-INF/LangExtensions.xml | 2 ++ 3 files changed, 47 insertions(+) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZeroWidthCodeVisionEntry.kt 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..a68e3f62c7e8 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/codeVision/ui/model/ZeroWidthCodeVisionEntry.kt @@ -0,0 +1,14 @@ +// 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 + +class ZeroWidthCodeVisionEntry( + providerId: String +) : CodeVisionEntry( + providerId, + icon = null, + longPresentation = "", + tooltip = "", + extraActions = emptyList() +) 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 9ab692c97ecc..c6afd75a7e87 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() @@ -56,6 +63,30 @@ 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) } \ No newline at end of file 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"/> + From 70ac5c900211bb6e1a48aadd3b3206641ed75d2f Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 14 Oct 2025 14:46:55 +0200 Subject: [PATCH 056/105] [Rider] RIDER-131203 patch up the tests and api GitOrigin-RevId: 735db409df09a1b9268620fd364def220e9fb9ee --- platform/lang-impl/api-dump-unreviewed.txt | 2 -- platform/lang-impl/api-dump.txt | 4 ++++ .../codeVision/ui/model/ZeroWidthCodeVisionEntry.kt | 2 ++ .../codeInsight/codeVision/ui/model/ZombieCodeVisionEntry.kt | 1 + .../ui/renderers/providers/CodeVisionPainterProviders.kt | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/api-dump-unreviewed.txt b/platform/lang-impl/api-dump-unreviewed.txt index 8102b1d5025a..7a29d041fb45 100644 --- a/platform/lang-impl/api-dump-unreviewed.txt +++ b/platform/lang-impl/api-dump-unreviewed.txt @@ -1723,8 +1723,6 @@ c:com.intellij.codeInsight.codeVision.ui.model.TextCodeVisionEntry - f:getText():java.lang.String f:com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry - com.intellij.codeInsight.codeVision.CodeVisionEntry -- (java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,java.lang.String):V -- b:(java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,java.lang.String,I,kotlin.jvm.internal.DefaultConstructorMarker):V - f:getCount():java.lang.Integer - f:getText():java.lang.String f:com.intellij.codeInsight.codeVision.ui.model.richText.RichString diff --git a/platform/lang-impl/api-dump.txt b/platform/lang-impl/api-dump.txt index cb04f24e4cc3..7767ce391d00 100644 --- a/platform/lang-impl/api-dump.txt +++ b/platform/lang-impl/api-dump.txt @@ -114,6 +114,10 @@ com.intellij.codeInsight.codeVision.settings.CodeVisionGroupSettingProvider - getGroupName():java.lang.String com.intellij.codeInsight.codeVision.ui.model.CodeVisionPredefinedActionEntry - a:onClick(com.intellij.openapi.editor.Editor):V +f:com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry +- (java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,java.lang.String,Z):V +- b:(java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,java.lang.String,Z,I,kotlin.jvm.internal.DefaultConstructorMarker):V +- f:getShouldBeDelimited():Z com.intellij.codeInsight.codeVision.ui.renderers.painters.ICodeVisionEntryBasePainter - a:paint(com.intellij.openapi.editor.Editor,com.intellij.openapi.editor.markup.TextAttributes,java.awt.Graphics,java.lang.Object,java.awt.Point,com.intellij.codeInsight.codeVision.ui.model.RangeCodeVisionModel$InlayState,Z,com.intellij.codeInsight.codeVision.CodeVisionEntry):V - a:size(com.intellij.openapi.editor.Editor,com.intellij.codeInsight.codeVision.ui.model.RangeCodeVisionModel$InlayState,java.lang.Object):java.awt.Dimension 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 index a68e3f62c7e8..2b8f7e93b72f 100644 --- 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 @@ -2,7 +2,9 @@ package com.intellij.codeInsight.codeVision.ui.model import com.intellij.codeInsight.codeVision.CodeVisionEntry +import org.jetbrains.annotations.ApiStatus +@ApiStatus.Internal class ZeroWidthCodeVisionEntry( providerId: String ) : CodeVisionEntry( 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 17df107f4306..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 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 c6afd75a7e87..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 @@ -63,7 +63,7 @@ private class ZombieCodeVisionEntryPainter : DefaultCodeVisionPainter { +private class ZeroWidthCodeVisionEntryPainter : ICodeVisionEntryBasePainter { override fun paint( editor: Editor, From 68671e553d1835734db6ee9a8945cde97fd53769 Mon Sep 17 00:00:00 2001 From: Egor Date: Thu, 16 Oct 2025 11:25:34 +0200 Subject: [PATCH 057/105] [Rider] RIDER-131203 fix display of the zero width lenses in More... menu GitOrigin-RevId: f5ba9abbd9695d3f60694f5219a91fb5beedb57c --- .../codeVision/ui/model/ZeroWidthCodeVisionEntry.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 index 2b8f7e93b72f..c7b04333356f 100644 --- 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 @@ -10,7 +10,11 @@ class ZeroWidthCodeVisionEntry( ) : CodeVisionEntry( providerId, icon = null, - longPresentation = "", + longPresentation = "Show a Code Vision inlay inlay when no results are available", tooltip = "", extraActions = emptyList() -) +) { + init { + showInMorePopup = false + } +} From 6a730408d1b87fe6a82194ea6b03f0d0f5e86d1b Mon Sep 17 00:00:00 2001 From: Egor Date: Thu, 16 Oct 2025 14:59:07 +0200 Subject: [PATCH 058/105] [Rider] RIDER-131203 localize string... duh GitOrigin-RevId: 49e141a2be2ddd160f9a6ac7c07d2bf1794fd74c --- platform/lang-api/resources/messages/LangBundle.properties | 1 + .../codeVision/ui/model/ZeroWidthCodeVisionEntry.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/lang-api/resources/messages/LangBundle.properties b/platform/lang-api/resources/messages/LangBundle.properties index b4216b9e92a1..c8b8ed174df4 100644 --- a/platform/lang-api/resources/messages/LangBundle.properties +++ b/platform/lang-api/resources/messages/LangBundle.properties @@ -623,3 +623,4 @@ shared.target.presentation.label=[shared] notification.content.editor.autoreparse.delay.has.been.restored=Editor autoreparse delay has been restored to the default value {0}ms. 0 value is associated with potential UI freezes and not recommended. action.open.settings.text=Open Settings +show.a.code.vision.inlay.inlay.when.no.results.are.available=Show a Code Vision inlay inlay when no results are available 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 index c7b04333356f..4b02c0eb7c39 100644 --- 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 @@ -2,6 +2,7 @@ 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 @@ -10,7 +11,7 @@ class ZeroWidthCodeVisionEntry( ) : CodeVisionEntry( providerId, icon = null, - longPresentation = "Show a Code Vision inlay inlay when no results are available", + longPresentation = LangBundle.message("show.a.code.vision.inlay.inlay.when.no.results.are.available"), tooltip = "", extraActions = emptyList() ) { From a7236b108246af971e1633bf741e7ab029d73980 Mon Sep 17 00:00:00 2001 From: Alexander Kuznetsov Date: Mon, 20 Oct 2025 14:49:58 +0200 Subject: [PATCH 059/105] [compose] IJPL-192198 Markdown: enable HTML blocks support GitOrigin-RevId: a93d11a1aa43b286d58844621be995338c41b638 --- .../intellij/markdown/compose/preview/MarkdownComposePanel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt b/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt index e0c8f0ce1a22..def8ca63e645 100644 --- a/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt +++ b/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt @@ -108,6 +108,7 @@ internal class MarkdownComposePanel( AutolinkProcessorExtension, ), markdownMode, + parseEmbeddedHtml = true, ) } val tableRenderer = remember(markdownStyling) { From 68304ad77647690b6827c1fda431b0ba1cda8714 Mon Sep 17 00:00:00 2001 From: Timur Malanin Date: Mon, 20 Oct 2025 15:09:57 +0000 Subject: [PATCH 060/105] PY-84905 Introduce `run.with.py.tool` registry key to enable running with specific run tools. GitOrigin-RevId: d62efa40233864a49bf0c4c79395b9a726b0eca9 --- python/pluginResources/intellij.python.community.impl.xml | 1 + .../com/jetbrains/python/run/PythonCommandLineState.java | 3 ++- .../AbstractPythonConfigurationFragmentedEditor.kt | 7 ++++--- .../com/jetbrains/python/run/features/PyRunToolProvider.kt | 6 ++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index b5ad92b8701f..a16bd436c2ad 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -192,6 +192,7 @@ overrides="true"/> + diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index c9cf0f2246a4..113dcb4edad3 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -93,6 +93,7 @@ import java.util.function.Function; import static com.intellij.execution.util.EnvFilesUtilKt.configureEnvsFromFiles; import static com.jetbrains.python.run.PythonScriptCommandLineState.getExpandedWorkingDir; import static com.jetbrains.python.run.features.PyRunToolExtKt.useRunTool; +import static com.jetbrains.python.run.features.PyRunToolProviderKt.getEnableRunTool; /** * Since this state is async, any method could be called on any thread @@ -350,7 +351,7 @@ public abstract class PythonCommandLineState extends CommandLineState { .toList(); PyRunToolParameters runToolParameters = null; - if (sdk != null) { + if (sdk != null && getEnableRunTool()) { PyRunToolProvider runToolProvider = PyRunToolProvider.forSdk(sdk); if (runToolProvider != null && useRunTool(myConfig, sdk)) { runToolParameters = runToolProvider.getRunToolParameters(); diff --git a/python/src/com/jetbrains/python/run/configuration/AbstractPythonConfigurationFragmentedEditor.kt b/python/src/com/jetbrains/python/run/configuration/AbstractPythonConfigurationFragmentedEditor.kt index 75a266087659..57744e494a0f 100644 --- a/python/src/com/jetbrains/python/run/configuration/AbstractPythonConfigurationFragmentedEditor.kt +++ b/python/src/com/jetbrains/python/run/configuration/AbstractPythonConfigurationFragmentedEditor.kt @@ -12,6 +12,7 @@ import com.jetbrains.python.run.AbstractPythonRunConfiguration import com.jetbrains.python.run.PyCommonFragmentsBuilder import com.jetbrains.python.run.PythonRunConfigurationExtensionsManager import com.jetbrains.python.run.features.PyRunToolProvider +import com.jetbrains.python.run.features.enableRunTool import com.jetbrains.python.run.features.useRunTool import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent @@ -31,9 +32,9 @@ abstract class AbstractPythonConfigurationFragmentedEditor - createRunToolTag(sdk)?.let { fragments.add(it) } - } + runConfiguration.sdk?.takeIf { enableRunTool } + ?.let { createRunToolTag(it) } + ?.let { fragments.add(it) } customizeFragments(fragments) fragments.add(PyEditorExtensionFragment()) diff --git a/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt b/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt index b990d0c81b37..98e20b12bd92 100644 --- a/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt +++ b/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt @@ -3,10 +3,16 @@ package com.jetbrains.python.run.features import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.util.registry.Registry import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import org.jetbrains.annotations.NonNls +/** + * Feature flag for Python "Run with …" tool integration. + */ +internal val enableRunTool: Boolean get() = Registry.`is`("run.with.py.tool") + @JvmInline @ApiStatus.Internal value class PyRunToolId(@param:NonNls val value: String) From 91f955483268e3d97a0888bfce231e90be980ceb Mon Sep 17 00:00:00 2001 From: "Sergey.Masloboev" Date: Tue, 14 Oct 2025 19:59:49 +0200 Subject: [PATCH 061/105] fix DPA-3312 Unexpected component for dataContext: org.jetbrains.skiko.SkiaLayer$1 GitOrigin-RevId: 86c4c17aaa10a4e09dca74ac3e5a0d451e5cfec5 --- .../src/com/intellij/ui/popup/PopupFactoryImpl.java | 7 +++++++ 1 file changed, 7 insertions(+) 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..cda7e796307c 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 + JComponent jComponent = (JComponent)SwingUtilities.getAncestorOfClass(JComponent.class, component); + if (jComponent != null) { + return jComponent; + } else { String componentClass = component == null ? null : component.getClass().getName(); throw new AssertionError("Unexpected component for " + errorInfoSupplier.get() + ": " + componentClass); From 75b2316276f36cb6c4bbd5b67b9edfdaaaf3fc33 Mon Sep 17 00:00:00 2001 From: "Sergey.Masloboev" Date: Fri, 17 Oct 2025 12:45:08 +0200 Subject: [PATCH 062/105] Only check the parent for non-Swing components to prevent premature generalization GitOrigin-RevId: 8c126caf49530bb19e51e709b6ec528d20e21f12 --- .../src/com/intellij/ui/popup/PopupFactoryImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 cda7e796307c..3a9fa95d823f 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java @@ -549,9 +549,9 @@ public class PopupFactoryImpl extends JBPopupFactory { // 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 - JComponent jComponent = (JComponent)SwingUtilities.getAncestorOfClass(JComponent.class, component); - if (jComponent != null) { - return jComponent; + Container parent = component == null ? null : component.getParent(); + if (parent instanceof JComponent c) { + return c; } else { String componentClass = component == null ? null : component.getClass().getName(); From b2df893565a273f70dfa805a25b3278c9166d873 Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 20 Oct 2025 13:00:05 +0300 Subject: [PATCH 063/105] [terminal] IJPL-211122 Refactor: expose TerminalShellIntegration as Deferred It looks more flexible to use: it can be easily passed somewhere, and it is more friendly for Java. GitOrigin-RevId: f8ba29883f6a8b07106b8640cf370343acbec634 --- .../terminal/frontend/view/TerminalView.kt | 7 +++-- .../view/impl/TerminalEventsHandlerImpl.kt | 7 ++--- .../TerminalShellIntegrationEventsHandler.kt | 9 ++++--- .../TerminalTypeAheadOutputModelController.kt | 10 +++---- .../view/impl/TerminalVfsSynchronizer.kt | 11 ++++---- .../frontend/view/impl/TerminalViewImpl.kt | 27 ++++++------------- .../terminal/util/terminalCoroutine.kt | 8 ++++++ .../completion/TerminalCompletionFixture.kt | 2 +- 8 files changed, 39 insertions(+), 42 deletions(-) diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt index 3359ced8582c..1ee2c66ddc66 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt @@ -3,6 +3,7 @@ package com.intellij.terminal.frontend.view import com.intellij.openapi.actionSystem.DataKey import com.intellij.terminal.TerminalTitle import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.flow.StateFlow import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel @@ -29,6 +30,8 @@ interface TerminalView { val sessionState: StateFlow + val shellIntegrationDeferred: Deferred + suspend fun hasChildProcesses(): Boolean fun getCurrentDirectory(): String? @@ -37,10 +40,6 @@ interface TerminalView { fun createSendTextBuilder(): TerminalSendTextBuilder - fun getShellIntegration(): TerminalShellIntegration? - - suspend fun awaitShellIntegrationInitialized(): TerminalShellIntegration - // todo companion object { diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt index ec238a3123ac..ffe912b23dd2 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt @@ -14,10 +14,12 @@ import com.jediterm.terminal.emulator.mouse.MouseButtonCodes import com.jediterm.terminal.emulator.mouse.MouseButtonModifierFlags import com.jediterm.terminal.emulator.mouse.MouseFormat import com.jediterm.terminal.emulator.mouse.MouseMode +import kotlinx.coroutines.Deferred import org.jetbrains.plugins.terminal.TerminalOptionsProvider import org.jetbrains.plugins.terminal.block.reworked.* import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputModelEditor import org.jetbrains.plugins.terminal.session.TerminalState +import org.jetbrains.plugins.terminal.util.getNow import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalOutputStatus import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration import java.awt.Point @@ -26,7 +28,6 @@ import java.awt.event.KeyEvent import java.awt.event.MouseEvent import java.awt.event.MouseWheelEvent import java.nio.charset.Charset -import java.util.concurrent.CompletableFuture import javax.swing.SwingUtilities import kotlin.math.abs @@ -43,7 +44,7 @@ internal open class TerminalEventsHandlerImpl( private val settings: JBTerminalSystemSettingsProviderBase, private val scrollingModel: TerminalOutputScrollingModel?, private val outputModel: TerminalOutputModel, - private val shellIntegrationFuture: CompletableFuture?, + private val shellIntegrationDeferred: Deferred?, private val typeAhead: TerminalTypeAhead?, ) : TerminalEventsHandler { private var ignoreNextKeyTypedEvent: Boolean = false @@ -404,7 +405,7 @@ internal open class TerminalEventsHandlerImpl( private fun scheduleCompletionPopupIfNeeded(charTyped: Char) { val project = editor.project ?: return - val shellIntegration = shellIntegrationFuture?.getNow(null) ?: return + val shellIntegration = shellIntegrationDeferred?.getNow() ?: return if (editor.isOutputModelEditor && TerminalCommandCompletion.isEnabled() && TerminalOptionsProvider.instance.showCompletionPopupAutomatically diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt index e02240eeaa9b..c6cd20840730 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt @@ -4,6 +4,7 @@ import com.intellij.openapi.application.EDT import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.asContextElement import com.intellij.platform.util.coroutines.childScope +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -11,21 +12,21 @@ import org.jetbrains.plugins.terminal.block.reworked.TerminalAliasesStorage import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel import org.jetbrains.plugins.terminal.session.* import org.jetbrains.plugins.terminal.session.dto.toState +import org.jetbrains.plugins.terminal.util.getNow import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration import org.jetbrains.plugins.terminal.view.shellIntegration.impl.TerminalShellIntegrationImpl -import java.util.concurrent.CompletableFuture internal class TerminalShellIntegrationEventsHandler( private val outputModelController: TerminalOutputModelController, private val sessionModel: TerminalSessionModel, - private val shellIntegrationFuture: CompletableFuture, + private val shellIntegrationDeferred: CompletableDeferred, private val aliasesStorage: TerminalAliasesStorage, private val coroutineScope: CoroutineScope, ) : TerminalOutputEventsHandler { private val edtContext = Dispatchers.EDT + ModalityState.any().asContextElement() private val shellIntegration: TerminalShellIntegrationImpl? - get() = shellIntegrationFuture.getNow(null) as? TerminalShellIntegrationImpl + get() = shellIntegrationDeferred.getNow() as? TerminalShellIntegrationImpl private fun getIntegrationOrThrow(): TerminalShellIntegrationImpl { return shellIntegration ?: error("Shell integration is not initialized yet") @@ -87,6 +88,6 @@ internal class TerminalShellIntegrationEventsHandler( sessionModel, coroutineScope.childScope("TerminalShellIntegration") ) - shellIntegrationFuture.complete(integration) + shellIntegrationDeferred.complete(integration) } } \ No newline at end of file diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt index 319c1e594d57..fbb0b6408446 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt @@ -19,11 +19,11 @@ import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isRewo import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent import org.jetbrains.plugins.terminal.session.TerminalCursorPositionChangedEvent import org.jetbrains.plugins.terminal.session.TerminalOutputEvent +import org.jetbrains.plugins.terminal.util.getNow import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalCommandBlock import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalOutputStatus import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration import java.lang.Runnable -import java.util.concurrent.CompletableFuture /** * Implementation of the [TerminalOutputModelController] that supports type-ahead. @@ -33,11 +33,11 @@ import java.util.concurrent.CompletableFuture * This way, there will be no flickering when partial backend updates are applied on top of the predictions. * And if type-ahead predictions were incorrect, the user will see the actual state with a small delay. */ -@OptIn(FlowPreview::class) +@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) internal class TerminalTypeAheadOutputModelController( private val project: Project, private val outputModel: MutableTerminalOutputModel, - private val shellIntegrationFuture: CompletableFuture, + private val shellIntegrationDeferred: Deferred, coroutineScope: CoroutineScope, ) : TerminalOutputModelController, TerminalTypeAhead { override val model: MutableTerminalOutputModel = outputModel @@ -60,7 +60,7 @@ internal class TerminalTypeAheadOutputModelController( private fun isTypeAheadEnabled(): Boolean { val enabledInRegistry = Registry.`is`("terminal.type.ahead", false) - val outputStatus = shellIntegrationFuture.getNow(null)?.outputStatus?.value + val outputStatus = shellIntegrationDeferred.getNow()?.outputStatus?.value return enabledInRegistry && outputStatus == TerminalOutputStatus.TypingCommand } @@ -78,7 +78,7 @@ internal class TerminalTypeAheadOutputModelController( override fun backspace() { if (!isTypeAheadEnabled()) return - val shellIntegration = shellIntegrationFuture.getNow(null)!! // isTypeAheadEnabled should guarantee that it is available + val shellIntegration = shellIntegrationDeferred.getCompleted() // isTypeAheadEnabled should guarantee that it is available val commandBlock = shellIntegration.blocksModel.activeBlock as? TerminalCommandBlock val cursorOffset = outputModel.cursorOffset val commandStartOffset = commandBlock?.commandStartOffset diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalVfsSynchronizer.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalVfsSynchronizer.kt index cf7623780a62..a68d6dcfd240 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalVfsSynchronizer.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalVfsSynchronizer.kt @@ -11,9 +11,7 @@ import com.intellij.openapi.util.Key import com.intellij.platform.util.coroutines.childScope import com.intellij.util.asDisposable import com.intellij.util.concurrency.annotations.RequiresEdt -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancel -import kotlinx.coroutines.isActive +import kotlinx.coroutines.* import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalCommandExecutionListener import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalCommandFinishedEvent @@ -21,11 +19,10 @@ import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegra import java.awt.event.FocusEvent import java.awt.event.FocusListener import java.awt.event.KeyEvent -import java.util.concurrent.CompletableFuture import javax.swing.JComponent internal class TerminalVfsSynchronizer( - shellIntegrationFuture: CompletableFuture, + shellIntegrationDeferred: Deferred, outputModel: TerminalOutputModel, terminalComponent: JComponent, coroutineScope: CoroutineScope, @@ -45,7 +42,9 @@ internal class TerminalVfsSynchronizer( init { val disposable = coroutineScope.asDisposable() - shellIntegrationFuture.thenAccept { shellIntegration -> + coroutineScope.launch(CoroutineName("Shell integration awaiting")) { + val shellIntegration = shellIntegrationDeferred.await() + // If we have events from the shell integration, we no more need heuristic-based refresher. heuristicBasedRefresherScope.cancel() LOG.debug { "Shell integration initialized, cancel heuristic-based VFS refresher." } diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt index 879278a63a50..8baa44b6f414 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt @@ -40,7 +40,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.* -import kotlinx.coroutines.future.await import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.plugins.terminal.TerminalPanelMarker @@ -89,9 +88,6 @@ class TerminalViewImpl( ) : TerminalView { private val sessionFuture: CompletableFuture = CompletableFuture() - @VisibleForTesting - val shellIntegrationFuture: CompletableFuture = CompletableFuture() - @VisibleForTesting val sessionModel: TerminalSessionModel @@ -131,11 +127,12 @@ class TerminalViewImpl( private val mutableSessionState: MutableStateFlow = MutableStateFlow(TerminalViewSessionState.NotStarted) override val sessionState: StateFlow = mutableSessionState.asStateFlow() + override val shellIntegrationDeferred: CompletableDeferred = CompletableDeferred(coroutineScope.coroutineContext.job) + init { // Cancell the hanging callbacks that wait for future completion if the coroutine scope is cancelled. coroutineScope.coroutineContext.job.invokeOnCompletion { sessionFuture.cancel(true) - shellIntegrationFuture.cancel(true) } val hyperlinkScope = coroutineScope.childScope("TerminalViewImpl hyperlink facades") @@ -167,7 +164,7 @@ class TerminalViewImpl( settings, scrollingModel = null, alternateBufferModel, - shellIntegrationFuture = null, + shellIntegrationDeferred = null, typeAhead = null, ) configureOutputEditor( @@ -201,7 +198,7 @@ class TerminalViewImpl( val outputModelController = TerminalTypeAheadOutputModelController( project, outputModel, - shellIntegrationFuture, + shellIntegrationDeferred, coroutineScope.childScope("TerminalTypeAheadOutputModelController") ) outputEditor.putUserData(TerminalTypeAhead.KEY, outputModelController) @@ -214,7 +211,7 @@ class TerminalViewImpl( settings, scrollingModel, outputModel, - shellIntegrationFuture, + shellIntegrationDeferred, typeAhead = outputModelController ) @@ -263,7 +260,7 @@ class TerminalViewImpl( val shellIntegrationEventsHandler = TerminalShellIntegrationEventsHandler( outputModelController, sessionModel, - shellIntegrationFuture, + shellIntegrationDeferred, terminalAliasesStorage, coroutineScope.childScope("TerminalShellIntegrationEventsHandler"), ) @@ -280,7 +277,7 @@ class TerminalViewImpl( listenAlternateBufferSwitch() val synchronizer = TerminalVfsSynchronizer( - shellIntegrationFuture, + shellIntegrationDeferred, outputModel, terminalPanel, coroutineScope.childScope("TerminalVfsSynchronizer"), @@ -292,7 +289,7 @@ class TerminalViewImpl( ModalityState.any().asContextElement() + CoroutineName("Shell integration features init") ) { - val shellIntegration = awaitShellIntegrationInitialized() + val shellIntegration = shellIntegrationDeferred.await() outputEditor.putUserData(TerminalBlocksModel.KEY, shellIntegration.blocksModel) TerminalBlocksDecorator( @@ -347,14 +344,6 @@ class TerminalViewImpl( terminalInput.sendText(options) } - override fun getShellIntegration(): TerminalShellIntegration? { - return shellIntegrationFuture.getNow(null) - } - - override suspend fun awaitShellIntegrationInitialized(): TerminalShellIntegration { - return shellIntegrationFuture.await() - } - fun setTopComponent(component: JComponent, disposable: Disposable) { val resizeListener = object : ComponentAdapter() { override fun componentResized(e: ComponentEvent) { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/util/terminalCoroutine.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/util/terminalCoroutine.kt index 2147e1d1b35b..2ac4926a8c83 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/util/terminalCoroutine.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/util/terminalCoroutine.kt @@ -5,8 +5,16 @@ import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.ExperimentalCoroutinesApi import org.jetbrains.annotations.ApiStatus +@ApiStatus.Experimental +@OptIn(ExperimentalCoroutinesApi::class) +fun Deferred.getNow(): T? { + return if (isCompleted && !isCancelled) getCompleted() else null +} + @ApiStatus.Internal fun terminalProjectScope(project: Project): CoroutineScope { return project.service().coroutineScope diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt index ae935cc173e8..448b09aba8fb 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/completion/TerminalCompletionFixture.kt @@ -53,7 +53,7 @@ class TerminalCompletionFixture(val project: Project, val testRootDisposable: Di } view = TerminalViewImpl(project, JBTerminalSystemSettingsProvider(), null, terminalScope) val shellIntegration = TerminalShellIntegrationImpl(outputModel, view.sessionModel, terminalScope.childScope("TerminalShellIntegration")) - view.shellIntegrationFuture.complete(shellIntegration) + view.shellIntegrationDeferred.complete(shellIntegration) shellIntegration.onPromptFinished(TerminalOffset.ZERO) // To make TerminalOutputStatus = TypingCommand assertEquals(TerminalOutputStatus.TypingCommand, shellIntegration.outputStatus.value) From 1c4e411cbe20beb9452ed59d4c14a26c1da439b4 Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 20 Oct 2025 13:38:38 +0300 Subject: [PATCH 064/105] [terminal] IJPL-211122 Refactor: move terminal session implementation related classes to impl package GitOrigin-RevId: e5e9a1e150544d1ba98d8eb70c07a446ebb9eef4 --- .../intellij/terminal/backend/BackendTerminalInput.kt | 2 +- .../terminal/backend/BackendTerminalSession.kt | 2 +- .../terminal/backend/BackendTerminalSessionImpl.kt | 6 +++--- .../terminal/backend/StateAwareTerminalSession.kt | 8 ++++---- .../terminal/backend/TerminalContentChangesTracker.kt | 6 +++--- .../terminal/backend/TerminalCursorPositionTracker.kt | 2 +- .../com/intellij/terminal/backend/TerminalOutput.kt | 4 ++-- .../intellij/terminal/backend/TerminalSessionStart.kt | 2 +- .../backend/TerminalShellIntegrationController.kt | 2 +- .../intellij/terminal/backend/TerminalTabsManager.kt | 4 ++-- .../hyperlinks/BackendHyperlinkInfoServiceImpl.kt | 2 +- .../hyperlinks/BackendTerminalHyperlinkFacade.kt | 10 +++++----- .../hyperlinks/BackendTerminalHyperlinkHighlighter.kt | 10 ++++------ .../backend/rpc/TerminalInputChannelsManager.kt | 2 +- .../terminal/backend/rpc/TerminalSessionApiImpl.kt | 6 +++--- .../frontend/fus/FrontendLatencyServiceImpl.kt | 2 +- .../view/hyperlinks/FrontendTerminalHyperlinkFacade.kt | 6 +++--- .../frontend/view/impl/TerminalEventsHandlerImpl.kt | 2 +- .../terminal/frontend/view/impl/TerminalInput.kt | 3 ++- .../frontend/view/impl/TerminalKeyEncodingManager.kt | 2 +- .../frontend/view/impl/TerminalOutputEventsHandler.kt | 2 +- .../view/impl/TerminalOutputModelController.kt | 4 ++-- .../view/impl/TerminalOutputModelControllerImpl.kt | 4 ++-- .../frontend/view/impl/TerminalSessionController.kt | 6 +++--- .../view/impl/TerminalShellIntegrationEventsHandler.kt | 4 ++-- .../impl/TerminalTypeAheadOutputModelController.kt | 6 +++--- .../terminal/frontend/view/impl/TerminalViewImpl.kt | 4 ++-- .../terminal/block/output/PartialCommandOutput.kt | 2 +- .../terminal/block/output/TerminalOutputController.kt | 2 +- .../block/prompt/renderer/ShellPromptRenderer.kt | 2 +- .../block/reworked/MutableTerminalOutputModel.kt | 8 ++++---- .../block/reworked/MutableTerminalOutputModelImpl.kt | 4 ++-- .../terminal/block/reworked/TerminalAliasesStorage.kt | 2 +- .../terminal/block/reworked/TerminalSessionModel.kt | 2 +- .../block/reworked/TerminalSessionModelImpl.kt | 2 +- .../reworked/TerminalShellIntegrationEventsListener.kt | 2 +- .../hyperlinks/HyperlinkIdDataContextSerializer.kt | 2 +- .../reworked/hyperlinks/TerminalHyperlinksModel.kt | 6 +++--- .../block/reworked/session/FrontendTerminalSession.kt | 8 ++++---- .../block/reworked/session/ShellStartupOptionsDto.kt | 6 +++--- .../block/reworked/session/rpc/TerminalSessionApi.kt | 4 ++-- .../block/session/ShellCommandOutputScraperImpl.kt | 2 +- .../scraper/StylesCollectingTerminalLinesCollector.kt | 2 +- .../plugins/terminal/fus/FrontendLatencyService.kt | 2 +- .../terminal/hyperlinks/BackendHyperlinkInfoService.kt | 2 +- .../hyperlinks/HyperlinkContextMenuActionGroup.kt | 2 +- .../plugins/terminal/session/{ => impl}/StyleRange.kt | 2 +- .../terminal/session/{ => impl}/TerminalAliasesInfo.kt | 2 +- .../session/{ => impl}/TerminalBlocksModelState.kt | 2 +- .../session/{ => impl}/TerminalHyperlinksModelState.kt | 2 +- .../terminal/session/{ => impl}/TerminalInputEvent.kt | 3 ++- .../terminal/session/{ => impl}/TerminalOutputEvent.kt | 4 ++-- .../session/{ => impl}/TerminalOutputModelState.kt | 2 +- .../terminal/session/{ => impl}/TerminalSession.kt | 2 +- .../terminal/session/{ => impl}/TerminalState.kt | 2 +- .../terminal/session/{ => impl}/dto/CursorShapeDto.kt | 2 +- .../terminal/session/{ => impl}/dto/MouseFormatDto.kt | 2 +- .../terminal/session/{ => impl}/dto/MouseModeDto.kt | 2 +- .../terminal/session/{ => impl}/dto/StyleRangeDto.kt | 4 ++-- .../{ => impl}/dto/TerminalBlocksModelStateDto.kt | 4 ++-- .../session/{ => impl}/dto/TerminalColorDto.kt | 2 +- .../session/{ => impl}/dto/TerminalCommandBlockDto.kt | 2 +- .../session/{ => impl}/dto/TerminalHyperlinksDto.kt | 4 ++-- .../{ => impl}/dto/TerminalOutputModelStateDto.kt | 4 ++-- .../terminal/session/{ => impl}/dto/TerminalSizeDto.kt | 2 +- .../session/{ => impl}/dto/TerminalStateDto.kt | 4 ++-- .../terminal/session/{ => impl}/dto/TextStyleDto.kt | 2 +- .../session/{ => impl}/dto/TextStyleOptionDto.kt | 2 +- .../shellIntegration/impl/TerminalBlocksModelImpl.kt | 2 +- .../terminal/tests/reworked/TerminalBlocksModelTest.kt | 2 +- .../terminal/tests/reworked/TerminalOutputModelTest.kt | 4 ++-- .../backend/BackendTerminalHyperlinkHighlighterTest.kt | 2 +- .../tests/reworked/backend/ShellIntegrationTest.kt | 6 +++--- .../backend/TerminalCursorPositionTrackerTest.kt | 2 +- .../tests/reworked/backend/TerminalSessionTest.kt | 2 +- .../tests/reworked/util/TerminalSessionTestUtil.kt | 6 +++--- .../terminal/tests/reworked/util/TerminalTestUtil.kt | 4 ++-- .../tests/reworked/util/TextWithReplacement.kt | 2 +- .../tests/reworked/util/TextWithReplacementTest.kt | 2 +- 79 files changed, 133 insertions(+), 133 deletions(-) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/StyleRange.kt (87%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalAliasesInfo.kt (85%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalBlocksModelState.kt (87%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalHyperlinksModelState.kt (97%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalInputEvent.kt (94%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalOutputEvent.kt (97%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalOutputModelState.kt (88%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalSession.kt (96%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/TerminalState.kt (94%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/CursorShapeDto.kt (96%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/MouseFormatDto.kt (95%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/MouseModeDto.kt (96%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/StyleRangeDto.kt (85%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalBlocksModelStateDto.kt (87%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalColorDto.kt (94%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalCommandBlockDto.kt (96%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalHyperlinksDto.kt (97%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalOutputModelStateDto.kt (90%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalSizeDto.kt (90%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TerminalStateDto.kt (94%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TextStyleDto.kt (94%) rename plugins/terminal/src/org/jetbrains/plugins/terminal/session/{ => impl}/dto/TextStyleOptionDto.kt (96%) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt index ae54ceb0e298..400dd3d9a511 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalInput.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.launch import org.jetbrains.plugins.terminal.block.reworked.TerminalUsageLocalStorage import org.jetbrains.plugins.terminal.block.ui.withLock -import org.jetbrains.plugins.terminal.session.* +import org.jetbrains.plugins.terminal.session.impl.* import org.jetbrains.plugins.terminal.util.STOP_EMULATOR_TIMEOUT import org.jetbrains.plugins.terminal.util.waitFor import java.util.concurrent.CancellationException diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSession.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSession.kt index 9fae85b1e1b7..fd3c88ee0571 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSession.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSession.kt @@ -4,7 +4,7 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.logger import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalSession +import org.jetbrains.plugins.terminal.session.impl.TerminalSession @ApiStatus.Internal interface BackendTerminalSession : TerminalSession { diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSessionImpl.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSessionImpl.kt index a3fa4b0d43ee..eef855ae8f03 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSessionImpl.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/BackendTerminalSessionImpl.kt @@ -10,9 +10,9 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext import org.jetbrains.plugins.terminal.TerminalUtil -import org.jetbrains.plugins.terminal.session.TerminalInputEvent -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent -import org.jetbrains.plugins.terminal.session.TerminalSessionTerminatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalInputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalSessionTerminatedEvent internal class BackendTerminalSessionImpl( private val inputChannel: SendChannel, diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt index 52312c904818..828ee0111629 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt @@ -17,16 +17,16 @@ import kotlinx.coroutines.flow.merge import org.jetbrains.plugins.terminal.block.reworked.* import org.jetbrains.plugins.terminal.block.ui.TerminalUiUtils import org.jetbrains.plugins.terminal.fus.* -import org.jetbrains.plugins.terminal.session.* -import org.jetbrains.plugins.terminal.session.dto.toDto -import org.jetbrains.plugins.terminal.session.dto.toTerminalState +import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.toDto +import org.jetbrains.plugins.terminal.session.impl.dto.toTerminalState import org.jetbrains.plugins.terminal.view.shellIntegration.impl.TerminalBlocksModelImpl import kotlin.coroutines.cancellation.CancellationException import kotlin.time.TimeSource /** * TerminalSession implementation that stores the state of the [delegate] session output. - * This state is then passed as the [org.jetbrains.plugins.terminal.session.TerminalInitialStateEvent] to the output flow as the first event + * This state is then passed as the [org.jetbrains.plugins.terminal.session.impl.TerminalInitialStateEvent] to the output flow as the first event * every time when [getOutputFlow] is requested. * * So, actually it allows restoring the state of UI that requests the [getOutputFlow]. diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalContentChangesTracker.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalContentChangesTracker.kt index 6688d8adc9ab..a8a094d150a4 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalContentChangesTracker.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalContentChangesTracker.kt @@ -12,9 +12,9 @@ import org.jetbrains.plugins.terminal.block.session.collectLines import org.jetbrains.plugins.terminal.block.session.scraper.SimpleStringCollector import org.jetbrains.plugins.terminal.block.session.scraper.StylesCollectingTerminalLinesCollector import org.jetbrains.plugins.terminal.fus.* -import org.jetbrains.plugins.terminal.session.StyleRange -import org.jetbrains.plugins.terminal.session.dto.StyleRangeDto -import org.jetbrains.plugins.terminal.session.dto.toDto +import org.jetbrains.plugins.terminal.session.impl.StyleRange +import org.jetbrains.plugins.terminal.session.impl.dto.StyleRangeDto +import org.jetbrains.plugins.terminal.session.impl.dto.toDto import java.util.concurrent.CopyOnWriteArrayList import kotlin.math.min import kotlin.time.TimeSource diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalCursorPositionTracker.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalCursorPositionTracker.kt index 2a12e51a80a4..1a483257126b 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalCursorPositionTracker.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalCursorPositionTracker.kt @@ -8,7 +8,7 @@ import com.jediterm.terminal.util.CharUtils import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.ui.getLengthWithoutDwc import org.jetbrains.plugins.terminal.block.ui.withLock -import org.jetbrains.plugins.terminal.session.TerminalCursorPositionChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalCursorPositionChangedEvent @ApiStatus.Internal class TerminalCursorPositionTracker( diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt index c4f8bc4eefe2..215f5e81792b 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalOutput.kt @@ -11,8 +11,8 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import org.jetbrains.plugins.terminal.block.reworked.TerminalShellIntegrationEventsListener import org.jetbrains.plugins.terminal.block.ui.withLock -import org.jetbrains.plugins.terminal.session.* -import org.jetbrains.plugins.terminal.session.dto.toDto +import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.toDto @OptIn(ExperimentalCoroutinesApi::class) internal fun createTerminalOutputFlow( diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt index 1a9d5e9d80d1..e77a26a03cd3 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt @@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.ShellStartupOptions -import org.jetbrains.plugins.terminal.session.TerminalSessionTerminatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalSessionTerminatedEvent import org.jetbrains.plugins.terminal.util.STOP_EMULATOR_TIMEOUT import org.jetbrains.plugins.terminal.util.waitFor diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalShellIntegrationController.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalShellIntegrationController.kt index e318a079b4c5..f0c2f503b089 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalShellIntegrationController.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalShellIntegrationController.kt @@ -6,7 +6,7 @@ import com.intellij.util.EventDispatcher import com.jediterm.terminal.Terminal import org.jetbrains.plugins.terminal.block.reworked.TerminalShellIntegrationEventsListener import org.jetbrains.plugins.terminal.exp.completion.TerminalShellSupport -import org.jetbrains.plugins.terminal.session.TerminalAliasesInfo +import org.jetbrains.plugins.terminal.session.impl.TerminalAliasesInfo import org.jetbrains.plugins.terminal.util.ShellType import java.util.* diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt index 3ea1c91af5e9..f512a15b15e6 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt @@ -17,8 +17,8 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.jetbrains.plugins.terminal.ShellStartupOptions import org.jetbrains.plugins.terminal.block.reworked.session.TerminalSessionTab -import org.jetbrains.plugins.terminal.session.TerminalCloseEvent -import org.jetbrains.plugins.terminal.session.TerminalStateChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalCloseEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalStateChangedEvent import java.util.concurrent.atomic.AtomicInteger @OptIn(AwaitCancellationAndInvoke::class) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendHyperlinkInfoServiceImpl.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendHyperlinkInfoServiceImpl.kt index 4a37a9bb4d50..28a8d7c9d482 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendHyperlinkInfoServiceImpl.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendHyperlinkInfoServiceImpl.kt @@ -5,7 +5,7 @@ import com.intellij.terminal.backend.TerminalSessionsManager import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId import org.jetbrains.plugins.terminal.hyperlinks.BackendHyperlinkInfo import org.jetbrains.plugins.terminal.hyperlinks.BackendHyperlinkInfoService -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId internal class BackendHyperlinkInfoServiceImpl : BackendHyperlinkInfoService { override fun getHyperlinkInfo(sessionId: TerminalSessionId, isAlternateBuffer: Boolean, hyperlinkId: TerminalHyperlinkId): BackendHyperlinkInfo? { diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkFacade.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkFacade.kt index 12669c53dda6..08f1656abba1 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkFacade.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkFacade.kt @@ -18,11 +18,11 @@ import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.hyperlinks.TerminalHyperlinksModel import org.jetbrains.plugins.terminal.fus.ReworkedTerminalUsageCollector import org.jetbrains.plugins.terminal.hyperlinks.BackendHyperlinkInfo -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId -import org.jetbrains.plugins.terminal.session.TerminalHyperlinksChangedEvent -import org.jetbrains.plugins.terminal.session.TerminalHyperlinksHeartbeatEvent -import org.jetbrains.plugins.terminal.session.TerminalHyperlinksModelState -import org.jetbrains.plugins.terminal.session.dto.toFilterResultInfo +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinksChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinksHeartbeatEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinksModelState +import org.jetbrains.plugins.terminal.session.impl.dto.toFilterResultInfo @ApiStatus.Internal class BackendTerminalHyperlinkFacade( diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkHighlighter.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkHighlighter.kt index e92c847b5810..bb8d762c69f1 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkHighlighter.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/hyperlinks/BackendTerminalHyperlinkHighlighter.kt @@ -22,12 +22,10 @@ import kotlinx.coroutines.flow.* import org.jetbrains.annotations.TestOnly import org.jetbrains.plugins.terminal.block.hyperlinks.CompositeFilterWrapper import org.jetbrains.plugins.terminal.block.reworked.* -import org.jetbrains.plugins.terminal.block.reworked.TerminalLineIndex -import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId -import org.jetbrains.plugins.terminal.session.TerminalHyperlinksChangedEvent -import org.jetbrains.plugins.terminal.session.TerminalHyperlinksHeartbeatEvent -import org.jetbrains.plugins.terminal.session.dto.* +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinksChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinksHeartbeatEvent +import org.jetbrains.plugins.terminal.session.impl.dto.* import java.awt.event.MouseEvent import java.util.* import java.util.concurrent.LinkedBlockingDeque diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalInputChannelsManager.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalInputChannelsManager.kt index 4fd5307ac009..e3d2299905e9 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalInputChannelsManager.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalInputChannelsManager.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId -import org.jetbrains.plugins.terminal.session.TerminalInputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalInputEvent /** * Service that has separate instances for each [com.intellij.openapi.client.ClientAppSession] diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalSessionApiImpl.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalSessionApiImpl.kt index e948cc934cfd..da5cdc93dfd3 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalSessionApiImpl.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/rpc/TerminalSessionApiImpl.kt @@ -5,9 +5,9 @@ import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.flow.Flow import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionApi import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId -import org.jetbrains.plugins.terminal.session.TerminalInputEvent -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent -import org.jetbrains.plugins.terminal.session.TerminalSession +import org.jetbrains.plugins.terminal.session.impl.TerminalInputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalSession internal class TerminalSessionApiImpl : TerminalSessionApi { override suspend fun getInputChannel(sessionId: TerminalSessionId): SendChannel { diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/fus/FrontendLatencyServiceImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/fus/FrontendLatencyServiceImpl.kt index 61ac914979d6..c36d24250e03 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/fus/FrontendLatencyServiceImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/fus/FrontendLatencyServiceImpl.kt @@ -3,7 +3,7 @@ package com.intellij.terminal.frontend.fus import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.editor.impl.EditorImpl import org.jetbrains.plugins.terminal.fus.* -import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalContentUpdatedEvent import java.util.concurrent.ArrayBlockingQueue import kotlin.time.TimeMark import kotlin.time.TimeSource diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/hyperlinks/FrontendTerminalHyperlinkFacade.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/hyperlinks/FrontendTerminalHyperlinkFacade.kt index 781fc81558f7..d09965732dae 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/hyperlinks/FrontendTerminalHyperlinkFacade.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/hyperlinks/FrontendTerminalHyperlinkFacade.kt @@ -9,9 +9,9 @@ import kotlinx.coroutines.CoroutineScope import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.hyperlinks.TerminalHyperlinksModel -import org.jetbrains.plugins.terminal.session.* -import org.jetbrains.plugins.terminal.session.dto.TerminalHyperlinksModelStateDto -import org.jetbrains.plugins.terminal.session.dto.toFilterResultInfo +import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.TerminalHyperlinksModelStateDto +import org.jetbrains.plugins.terminal.session.impl.dto.toFilterResultInfo internal class FrontendTerminalHyperlinkFacade( private val isInAlternateBuffer: Boolean, diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt index ffe912b23dd2..1ee052c7263f 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventsHandlerImpl.kt @@ -18,7 +18,7 @@ import kotlinx.coroutines.Deferred import org.jetbrains.plugins.terminal.TerminalOptionsProvider import org.jetbrains.plugins.terminal.block.reworked.* import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputModelEditor -import org.jetbrains.plugins.terminal.session.TerminalState +import org.jetbrains.plugins.terminal.session.impl.TerminalState import org.jetbrains.plugins.terminal.util.getNow import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalOutputStatus import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalInput.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalInput.kt index 472bfbaddfa0..fb06f19a49dd 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalInput.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalInput.kt @@ -14,7 +14,8 @@ import kotlinx.coroutines.future.await import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel import org.jetbrains.plugins.terminal.block.ui.sanitizeLineSeparators import org.jetbrains.plugins.terminal.fus.* -import org.jetbrains.plugins.terminal.session.* +import org.jetbrains.plugins.terminal.session.TerminalGridSize +import org.jetbrains.plugins.terminal.session.impl.* import org.jetbrains.plugins.terminal.view.impl.TerminalSendTextOptions import java.awt.event.KeyEvent import java.nio.charset.StandardCharsets diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalKeyEncodingManager.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalKeyEncodingManager.kt index 04624679df7f..041128ba0cfe 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalKeyEncodingManager.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalKeyEncodingManager.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel -import org.jetbrains.plugins.terminal.session.TerminalState +import org.jetbrains.plugins.terminal.session.impl.TerminalState /** * Actually a wrapper around [TerminalKeyEncoder]. diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputEventsHandler.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputEventsHandler.kt index b0c811f7f4ad..dc910b082ce4 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputEventsHandler.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputEventsHandler.kt @@ -1,6 +1,6 @@ package com.intellij.terminal.frontend.view.impl -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent internal interface TerminalOutputEventsHandler { suspend fun handleEvent(event: TerminalOutputEvent) diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelController.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelController.kt index a01d04699909..7279a8dcafda 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelController.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelController.kt @@ -2,8 +2,8 @@ package com.intellij.terminal.frontend.view.impl import com.intellij.util.concurrency.annotations.RequiresEdt import org.jetbrains.plugins.terminal.block.reworked.MutableTerminalOutputModel -import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent -import org.jetbrains.plugins.terminal.session.TerminalCursorPositionChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalContentUpdatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalCursorPositionChangedEvent /** * A controller for the output model updates. diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelControllerImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelControllerImpl.kt index c30644c5fc86..b9b4c1c1207e 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelControllerImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalOutputModelControllerImpl.kt @@ -2,8 +2,8 @@ package com.intellij.terminal.frontend.view.impl import org.jetbrains.plugins.terminal.block.reworked.MutableTerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.updateContent -import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent -import org.jetbrains.plugins.terminal.session.TerminalCursorPositionChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalContentUpdatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalCursorPositionChangedEvent /** * Simple implementation of the [TerminalOutputModelController] that just updates the output model immediately. diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt index 5dc80cbe5a32..50e1d22c584e 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt @@ -12,9 +12,9 @@ import com.intellij.terminal.frontend.view.hyperlinks.FrontendTerminalHyperlinkF import com.intellij.util.containers.DisposableWrapperList import kotlinx.coroutines.* import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel -import org.jetbrains.plugins.terminal.session.* -import org.jetbrains.plugins.terminal.session.dto.toState -import org.jetbrains.plugins.terminal.session.dto.toTerminalState +import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.toState +import org.jetbrains.plugins.terminal.session.impl.dto.toTerminalState import java.awt.Toolkit import kotlin.coroutines.cancellation.CancellationException diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt index c6cd20840730..cf132b02426e 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalShellIntegrationEventsHandler.kt @@ -10,8 +10,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jetbrains.plugins.terminal.block.reworked.TerminalAliasesStorage import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel -import org.jetbrains.plugins.terminal.session.* -import org.jetbrains.plugins.terminal.session.dto.toState +import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.toState import org.jetbrains.plugins.terminal.util.getNow import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration import org.jetbrains.plugins.terminal.view.shellIntegration.impl.TerminalShellIntegrationImpl diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt index fbb0b6408446..a7b08d6c0479 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalTypeAheadOutputModelController.kt @@ -16,9 +16,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.debounce import org.jetbrains.plugins.terminal.block.reworked.* import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isReworkedTerminalEditor -import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent -import org.jetbrains.plugins.terminal.session.TerminalCursorPositionChangedEvent -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalContentUpdatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalCursorPositionChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent import org.jetbrains.plugins.terminal.util.getNow import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalCommandBlock import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalOutputStatus diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt index 8baa44b6f414..6c7a016a33dd 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt @@ -58,8 +58,8 @@ import org.jetbrains.plugins.terminal.block.ui.isTerminalOutputScrollChangingAct import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils import org.jetbrains.plugins.terminal.fus.TerminalStartupFusInfo import org.jetbrains.plugins.terminal.session.TerminalGridSize -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId -import org.jetbrains.plugins.terminal.session.TerminalSession +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalSession import org.jetbrains.plugins.terminal.view.TerminalOutputModelsSet import org.jetbrains.plugins.terminal.view.TerminalSendTextBuilder import org.jetbrains.plugins.terminal.view.impl.TerminalOutputModelsSetImpl diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/PartialCommandOutput.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/PartialCommandOutput.kt index a4f5011b573f..4e8d30b89f21 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/PartialCommandOutput.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/PartialCommandOutput.kt @@ -1,7 +1,7 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.terminal.block.output -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange /** * @param styles ranges inside the [text] bounds. diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/TerminalOutputController.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/TerminalOutputController.kt index 294d79fde516..05481c285149 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/TerminalOutputController.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/output/TerminalOutputController.kt @@ -23,7 +23,7 @@ import org.jetbrains.plugins.terminal.block.prompt.TerminalPromptRenderingInfo import org.jetbrains.plugins.terminal.block.session.* import org.jetbrains.plugins.terminal.block.ui.* import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.IS_OUTPUT_EDITOR_KEY -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange import org.jetbrains.plugins.terminal.util.ShellType import org.jetbrains.plugins.terminal.util.terminalProjectScope import java.util.* diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/prompt/renderer/ShellPromptRenderer.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/prompt/renderer/ShellPromptRenderer.kt index 8be8d1172b82..4c59ba728429 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/prompt/renderer/ShellPromptRenderer.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/prompt/renderer/ShellPromptRenderer.kt @@ -18,7 +18,7 @@ import org.jetbrains.plugins.terminal.block.prompt.TerminalPromptState import org.jetbrains.plugins.terminal.block.session.ShellCommandOutputScraperImpl import org.jetbrains.plugins.terminal.block.session.StyledCommandOutput import org.jetbrains.plugins.terminal.block.ui.normalize -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange internal class ShellPromptRenderer( private val colorPalette: TerminalColorPalette, diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModel.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModel.kt index 8a99874a1548..8b0944d6f335 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModel.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModel.kt @@ -3,10 +3,10 @@ package org.jetbrains.plugins.terminal.block.reworked import com.intellij.openapi.editor.Document import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.StyleRange -import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent -import org.jetbrains.plugins.terminal.session.TerminalOutputModelState -import org.jetbrains.plugins.terminal.session.dto.toStyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange +import org.jetbrains.plugins.terminal.session.impl.TerminalContentUpdatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputModelState +import org.jetbrains.plugins.terminal.session.impl.dto.toStyleRange /** * Model that should manage the terminal output content: text, highlightings, and cursor position. diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModelImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModelImpl.kt index 827889883f37..a18c3d733466 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModelImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/MutableTerminalOutputModelImpl.kt @@ -15,8 +15,8 @@ import org.jetbrains.plugins.terminal.block.output.HighlightingInfo import org.jetbrains.plugins.terminal.block.output.TerminalOutputHighlightingsSnapshot import org.jetbrains.plugins.terminal.block.output.TextStyleAdapter import org.jetbrains.plugins.terminal.block.ui.BlockTerminalColorPalette -import org.jetbrains.plugins.terminal.session.StyleRange -import org.jetbrains.plugins.terminal.session.TerminalOutputModelState +import org.jetbrains.plugins.terminal.session.impl.StyleRange +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputModelState /** * [maxOutputLength] limits the length of the document. Zero means unlimited length. diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalAliasesStorage.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalAliasesStorage.kt index e0231f836dff..2567b6913d8a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalAliasesStorage.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalAliasesStorage.kt @@ -3,7 +3,7 @@ package org.jetbrains.plugins.terminal.block.reworked import com.intellij.openapi.util.Key import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalAliasesInfo +import org.jetbrains.plugins.terminal.session.impl.TerminalAliasesInfo @ApiStatus.Internal class TerminalAliasesStorage { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModel.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModel.kt index 067560acfa05..cd4a80fecd46 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModel.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModel.kt @@ -4,7 +4,7 @@ package org.jetbrains.plugins.terminal.block.reworked import com.intellij.openapi.util.Key import kotlinx.coroutines.flow.StateFlow import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalState +import org.jetbrains.plugins.terminal.session.impl.TerminalState @ApiStatus.Internal interface TerminalSessionModel { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModelImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModelImpl.kt index d5136710c278..c8f2601d2a86 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModelImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalSessionModelImpl.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalState +import org.jetbrains.plugins.terminal.session.impl.TerminalState @ApiStatus.Internal class TerminalSessionModelImpl : TerminalSessionModel { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalShellIntegrationEventsListener.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalShellIntegrationEventsListener.kt index f0057944c034..84d1cc1e73d1 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalShellIntegrationEventsListener.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/TerminalShellIntegrationEventsListener.kt @@ -2,7 +2,7 @@ package org.jetbrains.plugins.terminal.block.reworked import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalAliasesInfo +import org.jetbrains.plugins.terminal.session.impl.TerminalAliasesInfo import java.util.* @ApiStatus.Internal diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/HyperlinkIdDataContextSerializer.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/HyperlinkIdDataContextSerializer.kt index 29aa488b28c9..70e62c3a441a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/HyperlinkIdDataContextSerializer.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/HyperlinkIdDataContextSerializer.kt @@ -4,7 +4,7 @@ package org.jetbrains.plugins.terminal.block.reworked.hyperlinks import com.intellij.ide.CustomDataContextSerializer import com.intellij.openapi.actionSystem.DataKey import kotlinx.serialization.KSerializer -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId internal class HyperlinkIdDataContextSerializer : CustomDataContextSerializer { override val key: DataKey diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalHyperlinksModel.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalHyperlinksModel.kt index 9bd2f105b1f4..94ef2c19063a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalHyperlinksModel.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/hyperlinks/TerminalHyperlinksModel.kt @@ -5,9 +5,9 @@ import com.intellij.openapi.diagnostic.logger import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel -import org.jetbrains.plugins.terminal.session.TerminalFilterResultInfo -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId -import org.jetbrains.plugins.terminal.session.TerminalHyperlinksModelState +import org.jetbrains.plugins.terminal.session.impl.TerminalFilterResultInfo +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinksModelState @ApiStatus.Internal class TerminalHyperlinksModel(private val debugName: String, private val outputModel: TerminalOutputModel) { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/FrontendTerminalSession.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/FrontendTerminalSession.kt index 7abacc89d6d7..093de2dd6a09 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/FrontendTerminalSession.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/FrontendTerminalSession.kt @@ -10,10 +10,10 @@ import kotlinx.coroutines.flow.onEach import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionApi import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId -import org.jetbrains.plugins.terminal.session.TerminalInputEvent -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent -import org.jetbrains.plugins.terminal.session.TerminalSession -import org.jetbrains.plugins.terminal.session.TerminalSessionTerminatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalInputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalSession +import org.jetbrains.plugins.terminal.session.impl.TerminalSessionTerminatedEvent /** * [TerminalSession] implementation that is delegating the methods to [TerminalSessionApi] RPC to backend. diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/ShellStartupOptionsDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/ShellStartupOptionsDto.kt index f5968c3139b9..6de2b8a30932 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/ShellStartupOptionsDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/ShellStartupOptionsDto.kt @@ -5,9 +5,9 @@ import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.ShellStartupOptions -import org.jetbrains.plugins.terminal.session.dto.TerminalSizeDto -import org.jetbrains.plugins.terminal.session.dto.toDto -import org.jetbrains.plugins.terminal.session.dto.toTermSize +import org.jetbrains.plugins.terminal.session.impl.dto.TerminalSizeDto +import org.jetbrains.plugins.terminal.session.impl.dto.toDto +import org.jetbrains.plugins.terminal.session.impl.dto.toTermSize /** * DTO for [org.jetbrains.plugins.terminal.ShellStartupOptions]. diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/rpc/TerminalSessionApi.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/rpc/TerminalSessionApi.kt index f0702d1181b6..fba92ad7d909 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/rpc/TerminalSessionApi.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/reworked/session/rpc/TerminalSessionApi.kt @@ -8,8 +8,8 @@ import fleet.rpc.remoteApiDescriptor import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.flow.Flow import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalInputEvent -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalInputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent @ApiStatus.Internal @Rpc diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/ShellCommandOutputScraperImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/ShellCommandOutputScraperImpl.kt index aa70611ee04e..dcd0db6ca71e 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/ShellCommandOutputScraperImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/ShellCommandOutputScraperImpl.kt @@ -8,7 +8,7 @@ import org.jetbrains.plugins.terminal.TerminalUtil import org.jetbrains.plugins.terminal.block.session.scraper.* import org.jetbrains.plugins.terminal.block.session.util.Debouncer import org.jetbrains.plugins.terminal.block.ui.withLock -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange import org.jetbrains.plugins.terminal.util.addModelListener import java.util.concurrent.CopyOnWriteArrayList import kotlin.math.max diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/scraper/StylesCollectingTerminalLinesCollector.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/scraper/StylesCollectingTerminalLinesCollector.kt index 9d1183d49d07..3496f34d39dc 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/scraper/StylesCollectingTerminalLinesCollector.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/session/scraper/StylesCollectingTerminalLinesCollector.kt @@ -6,7 +6,7 @@ import com.jediterm.terminal.model.TerminalLine import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.ui.TerminalUiUtils import org.jetbrains.plugins.terminal.block.ui.normalize -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange @ApiStatus.Internal class StylesCollectingTerminalLinesCollector( diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/FrontendLatencyService.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/FrontendLatencyService.kt index 55a6c7bf799b..e995f8cc444a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/FrontendLatencyService.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/fus/FrontendLatencyService.kt @@ -4,7 +4,7 @@ package org.jetbrains.plugins.terminal.fus import com.intellij.openapi.components.service import com.intellij.openapi.editor.impl.EditorImpl import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalContentUpdatedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalContentUpdatedEvent @ApiStatus.Internal interface FrontendLatencyService { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/BackendHyperlinkInfoService.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/BackendHyperlinkInfoService.kt index 5a195060845f..91ac445257b7 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/BackendHyperlinkInfoService.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/BackendHyperlinkInfoService.kt @@ -5,7 +5,7 @@ import com.intellij.execution.filters.HyperlinkInfo import com.intellij.openapi.components.service import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId import java.awt.event.MouseEvent /** diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/HyperlinkContextMenuActionGroup.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/HyperlinkContextMenuActionGroup.kt index 6fb895d3f4bc..27bcefcdff5c 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/HyperlinkContextMenuActionGroup.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/hyperlinks/HyperlinkContextMenuActionGroup.kt @@ -11,7 +11,7 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.trace import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.IS_ALTERNATE_BUFFER_DATA_KEY -import org.jetbrains.plugins.terminal.session.TerminalHyperlinkId +import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId internal class HyperlinkContextMenuActionGroup : ActionGroup(), ActionRemoteBehaviorSpecification.BackendOnly { override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/StyleRange.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/StyleRange.kt similarity index 87% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/StyleRange.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/StyleRange.kt index 4bdb5721baa1..19816468274f 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/StyleRange.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/StyleRange.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import com.jediterm.terminal.TextStyle import org.jetbrains.annotations.ApiStatus diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalAliasesInfo.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalAliasesInfo.kt similarity index 85% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalAliasesInfo.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalAliasesInfo.kt index 93ed91026c2a..e5996884b68d 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalAliasesInfo.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalAliasesInfo.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalBlocksModelState.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalBlocksModelState.kt similarity index 87% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalBlocksModelState.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalBlocksModelState.kt index b79e49c7021c..e2d0f75512ea 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalBlocksModelState.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalBlocksModelState.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalBlockBase diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalHyperlinksModelState.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalHyperlinksModelState.kt similarity index 97% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalHyperlinksModelState.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalHyperlinksModelState.kt index 4f0a32ef94f6..0874698e75ed 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalHyperlinksModelState.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalHyperlinksModelState.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import com.intellij.execution.filters.HyperlinkInfo import com.intellij.execution.impl.EditorTextDecorationId diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalInputEvent.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalInputEvent.kt similarity index 94% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalInputEvent.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalInputEvent.kt index 174867a18778..27cdc6b9feae 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalInputEvent.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalInputEvent.kt @@ -1,10 +1,11 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import com.intellij.openapi.editor.event.EditorMouseEvent import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import org.jetbrains.annotations.ApiStatus +import org.jetbrains.plugins.terminal.session.TerminalGridSize import java.util.concurrent.atomic.AtomicInteger @ApiStatus.Internal diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalOutputEvent.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt similarity index 97% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalOutputEvent.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt index 588bf7ddeeea..bddffb65f317 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalOutputEvent.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt @@ -1,10 +1,10 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.dto.* +import org.jetbrains.plugins.terminal.session.impl.dto.* import kotlin.time.TimeMark @ApiStatus.Internal diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalOutputModelState.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputModelState.kt similarity index 88% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalOutputModelState.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputModelState.kt index 57aa7bbde8b4..dd3033c1560b 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalOutputModelState.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputModelState.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import org.jetbrains.annotations.ApiStatus diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalSession.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalSession.kt similarity index 96% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalSession.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalSession.kt index 12d12ae9d1ff..c65c9056787f 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalSession.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalSession.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.flow.Flow diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalState.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalState.kt similarity index 94% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalState.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalState.kt index 3cbe19c7725b..269b53004c37 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalState.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalState.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session +package org.jetbrains.plugins.terminal.session.impl import com.jediterm.terminal.CursorShape import com.jediterm.terminal.emulator.mouse.MouseFormat diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/CursorShapeDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/CursorShapeDto.kt similarity index 96% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/CursorShapeDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/CursorShapeDto.kt index 2c3869beb3c6..0744969676f0 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/CursorShapeDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/CursorShapeDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.terminal.CursorShape import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/MouseFormatDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/MouseFormatDto.kt similarity index 95% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/MouseFormatDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/MouseFormatDto.kt index c2e9f2657c71..f88fa97c648c 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/MouseFormatDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/MouseFormatDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.terminal.emulator.mouse.MouseFormat import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/MouseModeDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/MouseModeDto.kt similarity index 96% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/MouseModeDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/MouseModeDto.kt index 0821fc7727ea..d99314491cc0 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/MouseModeDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/MouseModeDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.terminal.emulator.mouse.MouseMode import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/StyleRangeDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/StyleRangeDto.kt similarity index 85% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/StyleRangeDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/StyleRangeDto.kt index 9221d5d55639..382e157e40f1 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/StyleRangeDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/StyleRangeDto.kt @@ -1,9 +1,9 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange @ApiStatus.Internal @Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalBlocksModelStateDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalBlocksModelStateDto.kt similarity index 87% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalBlocksModelStateDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalBlocksModelStateDto.kt index b8f03fa1c2d1..644bb0f9e118 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalBlocksModelStateDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalBlocksModelStateDto.kt @@ -1,9 +1,9 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalBlocksModelState +import org.jetbrains.plugins.terminal.session.impl.TerminalBlocksModelState import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalCommandBlock @Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalColorDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalColorDto.kt similarity index 94% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalColorDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalColorDto.kt index dcf0d1c5ddb0..5257cdfde6b3 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalColorDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalColorDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.terminal.TerminalColor import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalCommandBlockDto.kt similarity index 96% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalCommandBlockDto.kt index 2a8c496dd239..a56f8955f86f 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalCommandBlockDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalCommandBlockDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalHyperlinksDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalHyperlinksDto.kt similarity index 97% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalHyperlinksDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalHyperlinksDto.kt index d34dae220ae7..570d4eb55c80 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalHyperlinksDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalHyperlinksDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.intellij.execution.filters.HyperlinkInfo import com.intellij.execution.impl.InlayProvider @@ -10,7 +10,7 @@ import com.intellij.openapi.editor.markup.TextAttributes import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.* +import org.jetbrains.plugins.terminal.session.impl.* @ApiStatus.Internal @Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalOutputModelStateDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalOutputModelStateDto.kt similarity index 90% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalOutputModelStateDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalOutputModelStateDto.kt index 040e824f34be..f4a06bcaf000 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalOutputModelStateDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalOutputModelStateDto.kt @@ -1,9 +1,9 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalOutputModelState +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputModelState @Serializable @ApiStatus.Internal diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalSizeDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalSizeDto.kt similarity index 90% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalSizeDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalSizeDto.kt index 68e2deca7fd6..71bbe42ff9a4 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalSizeDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalSizeDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.core.util.TermSize import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalStateDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStateDto.kt similarity index 94% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalStateDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStateDto.kt index a01b9c0719bc..4daf7be66c34 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TerminalStateDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStateDto.kt @@ -1,9 +1,9 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus -import org.jetbrains.plugins.terminal.session.TerminalState +import org.jetbrains.plugins.terminal.session.impl.TerminalState @ApiStatus.Internal @Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TextStyleDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TextStyleDto.kt similarity index 94% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TextStyleDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TextStyleDto.kt index 01d1060f01d4..de83bb531fb0 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TextStyleDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TextStyleDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.terminal.TextStyle import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TextStyleOptionDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TextStyleOptionDto.kt similarity index 96% rename from plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TextStyleOptionDto.kt rename to plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TextStyleOptionDto.kt index 83419798d3e9..735350eac814 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/dto/TextStyleOptionDto.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TextStyleOptionDto.kt @@ -1,5 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.plugins.terminal.session.dto +package org.jetbrains.plugins.terminal.session.impl.dto import com.jediterm.terminal.TextStyle import kotlinx.serialization.Serializable diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt index 897af36cf2af..d8d251ba6b9f 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/view/shellIntegration/impl/TerminalBlocksModelImpl.kt @@ -7,7 +7,7 @@ import com.intellij.util.text.nullize import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.plugins.terminal.block.reworked.* -import org.jetbrains.plugins.terminal.session.TerminalBlocksModelState +import org.jetbrains.plugins.terminal.session.impl.TerminalBlocksModelState import org.jetbrains.plugins.terminal.view.shellIntegration.* @ApiStatus.Internal diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt index cd63dc315b3a..fb69aa98bf22 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalBlocksModelTest.kt @@ -11,7 +11,7 @@ import org.jetbrains.plugins.terminal.block.reworked.MutableTerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModelImpl -import org.jetbrains.plugins.terminal.session.TerminalBlocksModelState +import org.jetbrains.plugins.terminal.session.impl.TerminalBlocksModelState import org.jetbrains.plugins.terminal.view.shellIntegration.* import org.jetbrains.plugins.terminal.view.shellIntegration.impl.TerminalBlocksModelImpl import org.jetbrains.plugins.terminal.view.shellIntegration.impl.TerminalCommandBlockImpl diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalOutputModelTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalOutputModelTest.kt index 9e8598c03a4a..f6e5f0d1637c 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalOutputModelTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/TerminalOutputModelTest.kt @@ -16,8 +16,8 @@ import org.jetbrains.plugins.terminal.block.output.TerminalOutputHighlightingsSn import org.jetbrains.plugins.terminal.block.output.TextStyleAdapter import org.jetbrains.plugins.terminal.block.reworked.* import org.jetbrains.plugins.terminal.block.ui.BlockTerminalColorPalette -import org.jetbrains.plugins.terminal.session.StyleRange -import org.jetbrains.plugins.terminal.session.TerminalOutputModelState +import org.jetbrains.plugins.terminal.session.impl.StyleRange +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputModelState import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/BackendTerminalHyperlinkHighlighterTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/BackendTerminalHyperlinkHighlighterTest.kt index 05d95e503322..97fe243362f2 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/BackendTerminalHyperlinkHighlighterTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/BackendTerminalHyperlinkHighlighterTest.kt @@ -24,7 +24,7 @@ import org.assertj.core.api.Assertions.assertThat import org.jetbrains.plugins.terminal.block.reworked.TerminalOffset import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.updateContent -import org.jetbrains.plugins.terminal.session.* +import org.jetbrains.plugins.terminal.session.impl.* import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/ShellIntegrationTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/ShellIntegrationTest.kt index ac3114e9cf05..9bc15bf37bd0 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/ShellIntegrationTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/ShellIntegrationTest.kt @@ -16,9 +16,9 @@ import kotlinx.coroutines.channels.SendChannel import org.assertj.core.api.Assertions.assertThat import org.jetbrains.plugins.terminal.LocalTerminalCustomizer import org.jetbrains.plugins.terminal.ShellStartupOptions -import org.jetbrains.plugins.terminal.session.* -import org.jetbrains.plugins.terminal.session.dto.toState -import org.jetbrains.plugins.terminal.session.dto.toStyleRange +import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.toState +import org.jetbrains.plugins.terminal.session.impl.dto.toStyleRange import org.junit.Assume import org.junit.Rule import org.junit.Test diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalCursorPositionTrackerTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalCursorPositionTrackerTest.kt index 77bb9f90713c..a4dae2be7e0d 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalCursorPositionTrackerTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalCursorPositionTrackerTest.kt @@ -7,7 +7,7 @@ import com.jediterm.terminal.model.StyleState import com.jediterm.terminal.model.TerminalTextBuffer import com.jediterm.terminal.ui.settings.DefaultSettingsProvider import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.plugins.terminal.session.TerminalCursorPositionChangedEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalCursorPositionChangedEvent import org.junit.Test internal class TerminalCursorPositionTrackerTest { diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalSessionTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalSessionTest.kt index 93e7171f15a4..d9d22a1c76fa 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalSessionTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/backend/TerminalSessionTest.kt @@ -11,7 +11,7 @@ import com.intellij.testFramework.common.timeoutRunBlocking import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.launch -import org.jetbrains.plugins.terminal.session.* +import org.jetbrains.plugins.terminal.session.impl.* import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalSessionTestUtil.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalSessionTestUtil.kt index f369c371efb8..27efbbdb7964 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalSessionTestUtil.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalSessionTestUtil.kt @@ -23,8 +23,8 @@ import org.jetbrains.plugins.terminal.TerminalEngine import org.jetbrains.plugins.terminal.TerminalOptionsProvider import org.jetbrains.plugins.terminal.runner.LocalShellIntegrationInjector import org.jetbrains.plugins.terminal.runner.LocalTerminalStartCommandBuilder -import org.jetbrains.plugins.terminal.session.TerminalOutputEvent -import org.jetbrains.plugins.terminal.session.TerminalSession +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputEvent +import org.jetbrains.plugins.terminal.session.impl.TerminalSession import org.junit.Assume import java.nio.file.Files import java.nio.file.Path @@ -47,7 +47,7 @@ internal object TerminalSessionTestUtil { * * @param isLowLevelSession whether the same session should be used as in the production or its low-level JediTerm implementation. * Low-level session outputs the events in their natural order, - * while production one replaces some initial events with [org.jetbrains.plugins.terminal.session.TerminalInitialStateEvent]. + * while production one replaces some initial events with [org.jetbrains.plugins.terminal.session.impl.TerminalInitialStateEvent]. * Prefer low-level session in exceptional cases only when you need to test the exact sequences of events. * Use production session (specify `false`) in all other cases. */ diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalTestUtil.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalTestUtil.kt index 0bb060f50114..b16477da042e 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalTestUtil.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TerminalTestUtil.kt @@ -12,8 +12,8 @@ import org.jetbrains.plugins.terminal.TerminalEngine import org.jetbrains.plugins.terminal.TerminalOptionsProvider import org.jetbrains.plugins.terminal.block.reworked.MutableTerminalOutputModel import org.jetbrains.plugins.terminal.block.reworked.MutableTerminalOutputModelImpl -import org.jetbrains.plugins.terminal.session.StyleRange -import org.jetbrains.plugins.terminal.session.TerminalOutputModelState +import org.jetbrains.plugins.terminal.session.impl.StyleRange +import org.jetbrains.plugins.terminal.session.impl.TerminalOutputModelState @ApiStatus.Internal object TerminalTestUtil { diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacement.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacement.kt index 9ff1400a0699..add4878eec81 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacement.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacement.kt @@ -8,7 +8,7 @@ import com.intellij.terminal.tests.reworked.util.TextWithReplacement.Companion.S import com.jediterm.terminal.TerminalColor import com.jediterm.terminal.TextStyle import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange internal fun parseTextWithReplacement(textWithReplacement: String): TextWithReplacement { diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacementTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacementTest.kt index a3d082fd1597..20ab67fcf559 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacementTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/util/TextWithReplacementTest.kt @@ -7,7 +7,7 @@ import com.intellij.terminal.tests.reworked.util.TextWithReplacement.Companion.S import com.intellij.terminal.tests.reworked.util.TextWithReplacement.Companion.STYLE4 import com.jediterm.terminal.TextStyle import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.plugins.terminal.session.StyleRange +import org.jetbrains.plugins.terminal.session.impl.StyleRange import org.junit.Test class TextWithReplacementTest { From ebd1b591982fa90b7c2d602c24465b559075a0a0 Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 20 Oct 2025 14:13:45 +0300 Subject: [PATCH 065/105] [terminal] IJPL-211122 Add an ability to get shell startup options from TerminalView Introduce the new `TerminalStartupOptions` interface for that. Decided to avoid using existing `ShellStartupOptions` because it is polluted with unrelated fields that are hard to remove due to external usages. So, let's use the new interface and consider further migration to it. GitOrigin-RevId: 81e598bf5aec1a69c048e444e8eea5f425aef008 --- .../backend/StateAwareTerminalSession.kt | 3 ++ .../backend/TerminalSessionsManager.kt | 10 +++++- .../terminal/frontend/view/TerminalView.kt | 3 ++ .../view/impl/TerminalSessionController.kt | 4 +++ .../frontend/view/impl/TerminalViewImpl.kt | 3 ++ .../session/TerminalStartupOptions.kt | 12 +++++++ .../session/impl/TerminalOutputEvent.kt | 1 + .../impl/TerminalStartupOptionsImpl.kt | 12 +++++++ .../impl/dto/TerminalStartupOptionsDto.kt | 33 +++++++++++++++++++ 9 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt create mode 100644 plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalStartupOptionsImpl.kt create mode 100644 plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStartupOptionsDto.kt diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt index 828ee0111629..97e867d3b672 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/StateAwareTerminalSession.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.merge import org.jetbrains.plugins.terminal.block.reworked.* import org.jetbrains.plugins.terminal.block.ui.TerminalUiUtils import org.jetbrains.plugins.terminal.fus.* +import org.jetbrains.plugins.terminal.session.TerminalStartupOptions import org.jetbrains.plugins.terminal.session.impl.* import org.jetbrains.plugins.terminal.session.impl.dto.toDto import org.jetbrains.plugins.terminal.session.impl.dto.toTerminalState @@ -38,6 +39,7 @@ import kotlin.time.TimeSource internal class StateAwareTerminalSession( project: Project, private val delegate: BackendTerminalSession, + private val startupOptions: TerminalStartupOptions, override val coroutineScope: CoroutineScope, ) : BackendTerminalSession { private val outputFlowProducer = IncrementalUpdateFlowProducer(State()) @@ -218,6 +220,7 @@ internal class StateAwareTerminalSession( override suspend fun takeSnapshot(): List> { val event = TerminalInitialStateEvent( + startupOptions = startupOptions.toDto(), sessionState = sessionModel.terminalState.value.toDto(), outputModelState = outputModel.dumpState().toDto(), alternateBufferState = alternateBufferModel.dumpState().toDto(), diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionsManager.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionsManager.kt index 779d9dda2df0..8701b4af3385 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionsManager.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionsManager.kt @@ -13,6 +13,7 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.JBTerminalSystemSettingsProvider import org.jetbrains.plugins.terminal.ShellStartupOptions import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId +import org.jetbrains.plugins.terminal.session.impl.TerminalStartupOptionsImpl import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -49,7 +50,14 @@ class TerminalSessionsManager { // and cancel the main scope. val jediTermScope = scope.childScope("JediTerm session") val jediTermSession = createTerminalSession(project, observableTtyConnector, configuredOptions, JBTerminalSystemSettingsProvider(), jediTermScope) - val stateAwareSession = StateAwareTerminalSession(project, jediTermSession, scope) + + // It should be guaranteed that the shell command and working directory are not null. + val options = TerminalStartupOptionsImpl( + shellCommand = configuredOptions.shellCommand!!, + workingDirectory = configuredOptions.workingDirectory!!, + envVariables = configuredOptions.envVariables, + ) + val stateAwareSession = StateAwareTerminalSession(project, jediTermSession, options, scope) val sessionId = storeSession(stateAwareSession, scope) diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt index 1ee2c66ddc66..f6a64e5c6b82 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/TerminalView.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.StateFlow import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import org.jetbrains.plugins.terminal.session.TerminalGridSize +import org.jetbrains.plugins.terminal.session.TerminalStartupOptions import org.jetbrains.plugins.terminal.view.TerminalOutputModelsSet import org.jetbrains.plugins.terminal.view.TerminalSendTextBuilder import org.jetbrains.plugins.terminal.view.shellIntegration.TerminalShellIntegration @@ -32,6 +33,8 @@ interface TerminalView { val shellIntegrationDeferred: Deferred + val startupOptionsDeferred: Deferred + suspend fun hasChildProcesses(): Boolean fun getCurrentDirectory(): String? diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt index 50e1d22c584e..adf2c11193dc 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalSessionController.kt @@ -12,7 +12,9 @@ import com.intellij.terminal.frontend.view.hyperlinks.FrontendTerminalHyperlinkF import com.intellij.util.containers.DisposableWrapperList import kotlinx.coroutines.* import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel +import org.jetbrains.plugins.terminal.session.TerminalStartupOptions import org.jetbrains.plugins.terminal.session.impl.* +import org.jetbrains.plugins.terminal.session.impl.dto.toOptions import org.jetbrains.plugins.terminal.session.impl.dto.toState import org.jetbrains.plugins.terminal.session.impl.dto.toTerminalState import java.awt.Toolkit @@ -24,6 +26,7 @@ internal class TerminalSessionController( private val outputHyperlinkFacade: FrontendTerminalHyperlinkFacade?, private val alternateBufferModelController: TerminalOutputModelController, private val alternateBufferHyperlinkFacade: FrontendTerminalHyperlinkFacade?, + private val startupOptionsDeferred: CompletableDeferred, private val settings: JBTerminalSystemSettingsProviderBase, private val coroutineScope: CoroutineScope, ) { @@ -67,6 +70,7 @@ internal class TerminalSessionController( when (event) { is TerminalInitialStateEvent -> { sessionModel.updateTerminalState(event.sessionState.toTerminalState()) + startupOptionsDeferred.complete(event.startupOptions.toOptions()) withContext(edtContext) { outputModelController.applyPendingUpdates() alternateBufferModelController.applyPendingUpdates() diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt index 6c7a016a33dd..0f806b0f97be 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalViewImpl.kt @@ -58,6 +58,7 @@ import org.jetbrains.plugins.terminal.block.ui.isTerminalOutputScrollChangingAct import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils import org.jetbrains.plugins.terminal.fus.TerminalStartupFusInfo import org.jetbrains.plugins.terminal.session.TerminalGridSize +import org.jetbrains.plugins.terminal.session.TerminalStartupOptions import org.jetbrains.plugins.terminal.session.impl.TerminalHyperlinkId import org.jetbrains.plugins.terminal.session.impl.TerminalSession import org.jetbrains.plugins.terminal.view.TerminalOutputModelsSet @@ -128,6 +129,7 @@ class TerminalViewImpl( override val sessionState: StateFlow = mutableSessionState.asStateFlow() override val shellIntegrationDeferred: CompletableDeferred = CompletableDeferred(coroutineScope.coroutineContext.job) + override val startupOptionsDeferred: CompletableDeferred = CompletableDeferred(coroutineScope.coroutineContext.job) init { // Cancell the hanging callbacks that wait for future completion if the coroutine scope is cancelled. @@ -254,6 +256,7 @@ class TerminalViewImpl( outputHyperlinkFacade, alternateBufferModelController, alternateBufferHyperlinkFacade, + startupOptionsDeferred, settings, coroutineScope.childScope("TerminalSessionController") ) diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt new file mode 100644 index 000000000000..4f8ba26e1941 --- /dev/null +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt @@ -0,0 +1,12 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.terminal.session + +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Experimental +@ApiStatus.NonExtendable +interface TerminalStartupOptions { + val shellCommand: List + val workingDirectory: String + val envVariables: Map +} \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt index bddffb65f317..ad27291de2a8 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalOutputEvent.kt @@ -44,6 +44,7 @@ data object TerminalSessionTerminatedEvent : TerminalOutputEvent @ApiStatus.Internal @Serializable data class TerminalInitialStateEvent( + val startupOptions: TerminalStartupOptionsDto, val sessionState: TerminalStateDto, val outputModelState: TerminalOutputModelStateDto, val alternateBufferState: TerminalOutputModelStateDto, diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalStartupOptionsImpl.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalStartupOptionsImpl.kt new file mode 100644 index 000000000000..136515670815 --- /dev/null +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/TerminalStartupOptionsImpl.kt @@ -0,0 +1,12 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.terminal.session.impl + +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.plugins.terminal.session.TerminalStartupOptions + +@ApiStatus.Internal +data class TerminalStartupOptionsImpl( + override val shellCommand: List, + override val workingDirectory: String, + override val envVariables: Map, +) : TerminalStartupOptions \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStartupOptionsDto.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStartupOptionsDto.kt new file mode 100644 index 000000000000..2b901bfd9458 --- /dev/null +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/impl/dto/TerminalStartupOptionsDto.kt @@ -0,0 +1,33 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.terminal.session.impl.dto + +import kotlinx.serialization.Serializable +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.plugins.terminal.session.TerminalStartupOptions +import org.jetbrains.plugins.terminal.session.impl.TerminalStartupOptionsImpl + +@ApiStatus.Internal +@Serializable +data class TerminalStartupOptionsDto( + val shellCommand: List, + val workingDirectory: String, + val envVariables: Map, +) + +@ApiStatus.Internal +fun TerminalStartupOptions.toDto(): TerminalStartupOptionsDto { + return TerminalStartupOptionsDto( + shellCommand = shellCommand, + workingDirectory = workingDirectory, + envVariables = envVariables + ) +} + +@ApiStatus.Internal +fun TerminalStartupOptionsDto.toOptions(): TerminalStartupOptions { + return TerminalStartupOptionsImpl( + shellCommand = shellCommand, + workingDirectory = workingDirectory, + envVariables = envVariables + ) +} \ No newline at end of file From 2b142a7b860ccca22abcfbf8a24655f099452fed Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 20 Oct 2025 14:53:58 +0300 Subject: [PATCH 066/105] [terminal] IJPL-211122 Add a utility to get the shell name from TerminalStartupOptions Introduce the new interface instead of using the one from the `intellij.terminal.completion` module. It sounds better to have this utility defined in the main module. And to replace usages in completion later. GitOrigin-RevId: 8a2e6386fca44773d88fd3b90853f029b3c42787 --- .../plugins/terminal/session/ShellName.kt | 20 +++++++++++++++++++ .../session/TerminalStartupOptions.kt | 8 ++++++++ 2 files changed, 28 insertions(+) create mode 100644 plugins/terminal/src/org/jetbrains/plugins/terminal/session/ShellName.kt diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/ShellName.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/ShellName.kt new file mode 100644 index 000000000000..a688669db02b --- /dev/null +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/ShellName.kt @@ -0,0 +1,20 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.terminal.session + +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Experimental +sealed interface ShellName { + val value: String + + companion object { + fun of(value: String): ShellName = ShellNameImpl(value.lowercase()) + + val BASH: ShellName = of("bash") + val ZSH: ShellName = of("zsh") + val FISH: ShellName = of("fish") + val POWERSHELL: ShellName = of("powershell") + } +} + +private class ShellNameImpl(override val value: String) : ShellName \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt index 4f8ba26e1941..3fb0ea05aa19 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/session/TerminalStartupOptions.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 org.jetbrains.plugins.terminal.session +import com.intellij.util.PathUtil import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental @@ -9,4 +10,11 @@ interface TerminalStartupOptions { val shellCommand: List val workingDirectory: String val envVariables: Map +} + +@ApiStatus.Experimental +fun TerminalStartupOptions.getShellName(): ShellName { + val executablePath = shellCommand.first() // it should be guaranteed that it is not empty + val executableName = PathUtil.getFileName(executablePath) + return ShellName.of(executableName) } \ No newline at end of file From caa1fb55fe9efead917adb6de116d9139c9ebd96 Mon Sep 17 00:00:00 2001 From: Dmitry Zhuravlev Date: Tue, 14 Oct 2025 14:07:37 +0200 Subject: [PATCH 067/105] [diff] preserve viewport position while perform two sided diff rediff (IJPL-212174) GitOrigin-RevId: c8656c707d2883694e513c0dcc474682d63a732c --- .../diff/tools/simple/SimpleDiffViewer.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index 5c355d7deaba..1c97b138b75c 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -29,6 +29,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.util.EditorScrollingPositionKeeper; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.DumbAwareAction; @@ -40,6 +41,7 @@ import com.intellij.ui.DirtyUI; import com.intellij.util.concurrency.annotations.RequiresEdt; import com.intellij.util.concurrency.annotations.RequiresWriteLock; import com.intellij.util.containers.ContainerUtil; +import kotlin.enums.EnumEntries; import org.jetbrains.annotations.*; import javax.swing.*; @@ -67,6 +69,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { protected boolean aligningViewModeSupported; + private final EditorScrollingPositionKeeper @NotNull [] myPositionKeepers = new EditorScrollingPositionKeeper[Side.getEntries().size()]; + public SimpleDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) { super(context, (ContentDiffRequest)request); @@ -83,8 +87,15 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { myTextDiffProvider = DiffUtil.createTextDiffProvider(getProject(), getRequest(), getTextSettings(), this::rediff, this); - for (Side side : Side.values()) { - DiffUtil.installLineConvertor(getEditor(side), getContent(side), myFoldingModel, side.getIndex()); + EnumEntries<@NotNull Side> sides = Side.getEntries(); + for (int i = 0; i < sides.size(); i++) { + Side side = sides.get(i); + EditorEx editor = getEditor(side); + EditorScrollingPositionKeeper positionKeeper = new EditorScrollingPositionKeeper(editor); + Disposer.register(this, positionKeeper); + myPositionKeepers[i] = positionKeeper; + + DiffUtil.installLineConvertor(editor, getContent(side), myFoldingModel, side.getIndex()); } } @@ -248,7 +259,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { List nonSkipped = changes != null ? ContainerUtil.filter(changes, it -> !it.isSkipped()) : null; FoldingModelSupport.Data foldingState = myFoldingModel.createState(nonSkipped, getFoldingModelSettings()); - return () -> { + return () -> runPreservingScrollingPosition(() -> { + myFoldingModel.updateContext(myRequest, getFoldingModelSettings()); clearDiffPresentation(); @@ -271,7 +283,19 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { myContentPanel.repaintDivider(); myStatusPanel.update(); - }; + }); + } + + private void runPreservingScrollingPosition(@NotNull Runnable action) { + for (EditorScrollingPositionKeeper keeper : myPositionKeepers) { + keeper.savePosition(); + } + + action.run(); + + for (EditorScrollingPositionKeeper keeper : myPositionKeepers) { + keeper.restorePosition(true); + } } protected @NotNull Runnable applyNotification(final @Nullable JComponent notification) { From 3aeac6ef2fc9105c0d3a38f29478ea88313e3bd4 Mon Sep 17 00:00:00 2001 From: Timur Malanin Date: Mon, 20 Oct 2025 16:16:50 +0000 Subject: [PATCH 068/105] PY-84846 Log Python "Run with" tool usage. GitOrigin-RevId: ee13b27a80b759d2c12169a30499ba8dd39a03ea --- .../intellij.python.community.impl.xml | 2 + .../python/run/PythonCommandLineState.java | 3 ++ .../python/run/features/PyRunToolProvider.kt | 9 +++- .../run/features/PyRunToolUsageCollector.kt | 49 +++++++++++++++++++ .../python/sdk/uv/run/UvRunToolProvider.kt | 2 +- 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 python/src/com/jetbrains/python/run/features/PyRunToolUsageCollector.kt diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index a16bd436c2ad..b843ff1c972d 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -83,6 +83,8 @@ + + diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index 113dcb4edad3..f448dcef3b9a 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -67,8 +67,10 @@ import com.jetbrains.python.library.PythonLibraryType; import com.jetbrains.python.packaging.PyExecutionException; import com.jetbrains.python.remote.PyRemotePathMapper; import com.jetbrains.python.remote.PyRemoteSdkAdditionalData; +import com.jetbrains.python.run.features.PyRunToolIds; import com.jetbrains.python.run.features.PyRunToolParameters; import com.jetbrains.python.run.features.PyRunToolProvider; +import com.jetbrains.python.run.features.PyRunToolUsageCollector; import com.jetbrains.python.run.target.HelpersAwareTargetEnvironmentRequest; import com.jetbrains.python.run.target.PySdkTargetPaths; import com.jetbrains.python.run.target.PythonCommandLineTargetEnvironmentProvider; @@ -355,6 +357,7 @@ public abstract class PythonCommandLineState extends CommandLineState { PyRunToolProvider runToolProvider = PyRunToolProvider.forSdk(sdk); if (runToolProvider != null && useRunTool(myConfig, sdk)) { runToolParameters = runToolProvider.getRunToolParameters(); + PyRunToolUsageCollector.logRun(myConfig.getProject(), PyRunToolIds.idOf(runToolProvider)); } } diff --git a/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt b/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt index 98e20b12bd92..06aeb31b6980 100644 --- a/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt +++ b/python/src/com/jetbrains/python/run/features/PyRunToolProvider.kt @@ -17,18 +17,25 @@ internal val enableRunTool: Boolean get() = Registry.`is`("run.with.py.tool") @ApiStatus.Internal value class PyRunToolId(@param:NonNls val value: String) +internal object PyRunToolIds { + @JvmStatic + fun idOf(provider: PyRunToolProvider): String = provider.runToolData.id.value +} + /** * Metadata describing a concrete Python "Run with …" tool option. * * - id: a unique identifier of the tool. * - name: user‑visible action/configuration name (localized). * - group: user‑visible group name under which the action is shown in UI (localized). + * - idForStatistics: identifier for FUS statistics (should not change to avoid breaking statistics). */ @ApiStatus.Internal data class PyRunToolData( @param:NonNls val id: PyRunToolId, @param:Nls val name: String, - @param:Nls val group: String + @param:Nls val group: String, + @param:NonNls val idForStatistics: String = id.value ) /** diff --git a/python/src/com/jetbrains/python/run/features/PyRunToolUsageCollector.kt b/python/src/com/jetbrains/python/run/features/PyRunToolUsageCollector.kt new file mode 100644 index 000000000000..db384ad3810d --- /dev/null +++ b/python/src/com/jetbrains/python/run/features/PyRunToolUsageCollector.kt @@ -0,0 +1,49 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.jetbrains.python.run.features + +import com.intellij.internal.statistic.eventLog.EventLogGroup +import com.intellij.internal.statistic.eventLog.events.EventFields +import com.intellij.internal.statistic.eventLog.events.EventId1 +import com.intellij.internal.statistic.eventLog.validator.ValidationResultType +import com.intellij.internal.statistic.eventLog.validator.rules.EventContext +import com.intellij.internal.statistic.eventLog.validator.rules.impl.CustomValidationRule +import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector +import com.intellij.internal.statistic.utils.getPluginInfo +import com.intellij.openapi.project.Project + +/** + * FUS collector for "Run with …" Python tools exposed via [PyRunToolProvider]. + * Logs how many runs are started via a run tool and the tool id. + */ +internal object PyRunToolUsageCollector : CounterUsagesCollector() { + private val GROUP = EventLogGroup("python.run.tool", 1, "FUS", "Tracks Python run configurations executed with specific run tools (e.g., uv run)") + + private val TOOL_ID = EventFields.StringValidatedByCustomRule("run_tool_id", PyRunToolIdValidator::class.java) + + private val RUN_USED: EventId1 = GROUP.registerEvent("run.used", TOOL_ID, "Execution of Python run configurations with specific run tool") + + @JvmStatic + fun logRun(project: Project, runToolId: String) { + RUN_USED.log(project, runToolId) + } + + override fun getGroup(): EventLogGroup = GROUP +} + +/** + * Custom validator for PyRunTool IDs in FUS statistics. + * Validates that the ID corresponds to a known PyRunToolProvider. + */ +internal class PyRunToolIdValidator : CustomValidationRule() { + override fun getRuleId(): String = "python_run_tool" + + override fun doValidate(data: String, context: EventContext): ValidationResultType { + val validIds = PyRunToolProvider.EP.extensionList + .asSequence() + .filter { getPluginInfo(it.javaClass).isDevelopedByJetBrains() } + .map { it.runToolData.idForStatistics } + .toSet() + + return if (data in validIds) ValidationResultType.ACCEPTED else ValidationResultType.REJECTED + } +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/sdk/uv/run/UvRunToolProvider.kt b/python/src/com/jetbrains/python/sdk/uv/run/UvRunToolProvider.kt index 4d6f8bd8b1db..10b0614206a3 100644 --- a/python/src/com/jetbrains/python/sdk/uv/run/UvRunToolProvider.kt +++ b/python/src/com/jetbrains/python/sdk/uv/run/UvRunToolProvider.kt @@ -15,7 +15,7 @@ import com.jetbrains.python.sdk.uv.isUv private class UvRunToolProvider : PyRunToolProvider { override val runToolData: PyRunToolData = PyRunToolData( - PyRunToolId("py.run.with.uv"), + PyRunToolId("uv.run"), PyBundle.message("uv.run.configuration.type.display.name"), PyBundle.message("python.run.configuration.fragments.python.group"), ) From 733c6e17184155b2cfba8d65d9d464d8cb1930a8 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Sun, 19 Oct 2025 10:45:24 +0200 Subject: [PATCH 069/105] IJPL-213640 update kotlinx serialization to 1.9.0 GitOrigin-RevId: 05927722ce98708d58d505f720323b5f327cba9a --- .../libraries/kotlinx_serialization_cbor.xml | 10 +- .../libraries/kotlinx_serialization_core.xml | 10 +- .../libraries/kotlinx_serialization_json.xml | 10 +- .../kotlinx_serialization_json_io.xml | 10 +- .../kotlinx_serialization_protobuf.xml | 10 +- lib/BUILD.bazel | 20 ++-- lib/MODULE.bazel | 94 +++++++++++-------- 7 files changed, 89 insertions(+), 75 deletions(-) diff --git a/.idea/libraries/kotlinx_serialization_cbor.xml b/.idea/libraries/kotlinx_serialization_cbor.xml index ad2f6d9edd8e..ea92d0da61cb 100644 --- a/.idea/libraries/kotlinx_serialization_cbor.xml +++ b/.idea/libraries/kotlinx_serialization_cbor.xml @@ -1,18 +1,18 @@ - + - - 604c4130ca7a0e979a449cf550087a5c2c586485e8af01dcf24b03b7b287306d + + e45f32f0d80e1164593cfcf9f1907ae39871084c6aa08e49acc2e8e22e1d93ce - + - + \ No newline at end of file diff --git a/.idea/libraries/kotlinx_serialization_core.xml b/.idea/libraries/kotlinx_serialization_core.xml index 2ef10996c861..4691d70eaafe 100644 --- a/.idea/libraries/kotlinx_serialization_core.xml +++ b/.idea/libraries/kotlinx_serialization_core.xml @@ -1,18 +1,18 @@ - + - - 3565b6d4d789bf70683c45566944287fc1d8dc75c23d98bd87d01059cc76f2b3 + + 1f0afa172110e45a7231ef1b44ae8fd84c1ebaff96f3fc3ad68ef8c48120b59c - + - + \ No newline at end of file diff --git a/.idea/libraries/kotlinx_serialization_json.xml b/.idea/libraries/kotlinx_serialization_json.xml index eb577b4dca44..c1645e98c530 100644 --- a/.idea/libraries/kotlinx_serialization_json.xml +++ b/.idea/libraries/kotlinx_serialization_json.xml @@ -1,18 +1,18 @@ - + - - 8769e5647557e3700919c32d508f5c5dad53c5d8234cd10846354fbcff14aa24 + + d94cc34cae39246a1af74fda63f9c4812ce12216ef641d5fa3bbbb539a6922d8 - + - + \ No newline at end of file diff --git a/.idea/libraries/kotlinx_serialization_json_io.xml b/.idea/libraries/kotlinx_serialization_json_io.xml index cf3eaa57a974..4ea1fe885b08 100644 --- a/.idea/libraries/kotlinx_serialization_json_io.xml +++ b/.idea/libraries/kotlinx_serialization_json_io.xml @@ -1,18 +1,18 @@ - + - - ab5ec8ea48815197c46a7df9f418a62791fe91cda5023b941ce4de872d810cc1 + + 98abb132b03e11b80318d45350ec9ebbb5453d26505e81331bf2eea7143b6fbe - + - + \ No newline at end of file diff --git a/.idea/libraries/kotlinx_serialization_protobuf.xml b/.idea/libraries/kotlinx_serialization_protobuf.xml index 609d78da90eb..57063bccd172 100644 --- a/.idea/libraries/kotlinx_serialization_protobuf.xml +++ b/.idea/libraries/kotlinx_serialization_protobuf.xml @@ -1,18 +1,18 @@ - + - - fbd174f72ea82364baa112069f2d7b361758f6b68bd81d2ac51b02c66a48e924 + + 555035205ad1a21254b49fd6b5ddbe6ad1abb5c7baba244258bd758f80687bca - + - + \ No newline at end of file diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel index 2f3eebd1146b..5fdf422272b3 100644 --- a/lib/BUILD.bazel +++ b/lib/BUILD.bazel @@ -4741,37 +4741,37 @@ jvm_import( jvm_import( name = "kotlinx-serialization-cbor", - jar = "@org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_8_1_http//file", - source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_8_1_http//file", + jar = "@org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_9_0_http//file", + source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_9_0_http//file", visibility = ["//visibility:public"] ) jvm_import( name = "kotlinx-serialization-core", - jar = "@org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_8_1_http//file", - source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_8_1_http//file", + jar = "@org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_9_0_http//file", + source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_9_0_http//file", exported_compiler_plugins = ["@lib//:kotlin-serialization-plugin"], visibility = ["//visibility:public"] ) jvm_import( name = "kotlinx-serialization-json", - jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_8_1_http//file", - source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_8_1_http//file", + jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_9_0_http//file", + source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_9_0_http//file", visibility = ["//visibility:public"] ) jvm_import( name = "kotlinx-serialization-json-io", - jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_8_1_http//file", - source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_8_1_http//file", + jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_9_0_http//file", + source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_9_0_http//file", visibility = ["//visibility:public"] ) jvm_import( name = "kotlinx-serialization-protobuf", - jar = "@org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_8_1_http//file", - source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_8_1_http//file", + jar = "@org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_9_0_http//file", + source_jar = "@org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_9_0_http//file", visibility = ["//visibility:public"] ) diff --git a/lib/MODULE.bazel b/lib/MODULE.bazel index 729c460690db..f229cc00aeb4 100644 --- a/lib/MODULE.bazel +++ b/lib/MODULE.bazel @@ -7084,73 +7084,73 @@ http_file( ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_8_1_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.8.1/kotlinx-serialization-cbor-jvm-1.8.1.jar", - sha256 = "604c4130ca7a0e979a449cf550087a5c2c586485e8af01dcf24b03b7b287306d", - downloaded_file_path = "kotlinx-serialization-cbor-jvm-1.8.1.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_9_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.9.0/kotlinx-serialization-cbor-jvm-1.9.0.jar", + sha256 = "e45f32f0d80e1164593cfcf9f1907ae39871084c6aa08e49acc2e8e22e1d93ce", + downloaded_file_path = "kotlinx-serialization-cbor-jvm-1.9.0.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_8_1-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.8.1/kotlinx-serialization-cbor-jvm-1.8.1-sources.jar", - sha256 = "5764d9dd773e48dca2ad2376005281b09bc4a9d38b2512f82421cae43eb163b2", - downloaded_file_path = "kotlinx-serialization-cbor-jvm-1.8.1-sources.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-cbor-jvm-1_9_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.9.0/kotlinx-serialization-cbor-jvm-1.9.0-sources.jar", + sha256 = "aaf83c4c7e24060608bff3f7279b0f543b21ed1544103bdfebbcb88adb7021cd", + downloaded_file_path = "kotlinx-serialization-cbor-jvm-1.9.0-sources.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_8_1_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1.jar", - sha256 = "3565b6d4d789bf70683c45566944287fc1d8dc75c23d98bd87d01059cc76f2b3", - downloaded_file_path = "kotlinx-serialization-core-jvm-1.8.1.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_9_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.9.0/kotlinx-serialization-core-jvm-1.9.0.jar", + sha256 = "1f0afa172110e45a7231ef1b44ae8fd84c1ebaff96f3fc3ad68ef8c48120b59c", + downloaded_file_path = "kotlinx-serialization-core-jvm-1.9.0.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_8_1-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1-sources.jar", - sha256 = "d4a7221a0c2ccb47bc736340344b256506cf8a5a49882883f8f56385ae69a1bc", - downloaded_file_path = "kotlinx-serialization-core-jvm-1.8.1-sources.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_9_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.9.0/kotlinx-serialization-core-jvm-1.9.0-sources.jar", + sha256 = "72ad8ca04529d29cb144f845858f650ad8f06ca3a43c637967ca88b733c06f19", + downloaded_file_path = "kotlinx-serialization-core-jvm-1.9.0-sources.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_8_1_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.8.1/kotlinx-serialization-json-jvm-1.8.1.jar", - sha256 = "8769e5647557e3700919c32d508f5c5dad53c5d8234cd10846354fbcff14aa24", - downloaded_file_path = "kotlinx-serialization-json-jvm-1.8.1.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_9_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.9.0/kotlinx-serialization-json-jvm-1.9.0.jar", + sha256 = "d94cc34cae39246a1af74fda63f9c4812ce12216ef641d5fa3bbbb539a6922d8", + downloaded_file_path = "kotlinx-serialization-json-jvm-1.9.0.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_8_1-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.8.1/kotlinx-serialization-json-jvm-1.8.1-sources.jar", - sha256 = "5c76c02a6bc319d307a73b2bb4e77d1a0d3c1a25b78e559f662e2eb8ed29cdad", - downloaded_file_path = "kotlinx-serialization-json-jvm-1.8.1-sources.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-json-jvm-1_9_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.9.0/kotlinx-serialization-json-jvm-1.9.0-sources.jar", + sha256 = "6c231ddcd95b1023ffb72cfd98023a035f666e46b4479d68eff83cc30892396c", + downloaded_file_path = "kotlinx-serialization-json-jvm-1.9.0-sources.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_8_1_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.8.1/kotlinx-serialization-json-io-jvm-1.8.1.jar", - sha256 = "ab5ec8ea48815197c46a7df9f418a62791fe91cda5023b941ce4de872d810cc1", - downloaded_file_path = "kotlinx-serialization-json-io-jvm-1.8.1.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_9_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.9.0/kotlinx-serialization-json-io-jvm-1.9.0.jar", + sha256 = "98abb132b03e11b80318d45350ec9ebbb5453d26505e81331bf2eea7143b6fbe", + downloaded_file_path = "kotlinx-serialization-json-io-jvm-1.9.0.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_8_1-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.8.1/kotlinx-serialization-json-io-jvm-1.8.1-sources.jar", - sha256 = "d2576ea868d4654376dbbb103c53144a3574dc4e5e9a37eee7844dc00342418e", - downloaded_file_path = "kotlinx-serialization-json-io-jvm-1.8.1-sources.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-json-io-jvm-1_9_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.9.0/kotlinx-serialization-json-io-jvm-1.9.0-sources.jar", + sha256 = "60a176e88a074452f03e7f359f33bed4ca734e24e008c50677468fd253af9e98", + downloaded_file_path = "kotlinx-serialization-json-io-jvm-1.9.0-sources.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_8_1_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.8.1/kotlinx-serialization-protobuf-jvm-1.8.1.jar", - sha256 = "fbd174f72ea82364baa112069f2d7b361758f6b68bd81d2ac51b02c66a48e924", - downloaded_file_path = "kotlinx-serialization-protobuf-jvm-1.8.1.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_9_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.9.0/kotlinx-serialization-protobuf-jvm-1.9.0.jar", + sha256 = "555035205ad1a21254b49fd6b5ddbe6ad1abb5c7baba244258bd758f80687bca", + downloaded_file_path = "kotlinx-serialization-protobuf-jvm-1.9.0.jar" ) http_file( - name = "org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_8_1-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.8.1/kotlinx-serialization-protobuf-jvm-1.8.1-sources.jar", - sha256 = "e4a4de5cff003c53d5d3c5b979de4b68207e4c3b861a92f490f0ea88e2cd5330", - downloaded_file_path = "kotlinx-serialization-protobuf-jvm-1.8.1-sources.jar" + name = "org_jetbrains_kotlinx-kotlinx-serialization-protobuf-jvm-1_9_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.9.0/kotlinx-serialization-protobuf-jvm-1.9.0-sources.jar", + sha256 = "63d88633e89e5cc37d84ff21e57a73ad28710b3ff143fd13b16694dfbd0c73f9", + downloaded_file_path = "kotlinx-serialization-protobuf-jvm-1.9.0-sources.jar" ) http_file( @@ -8728,6 +8728,20 @@ http_file( downloaded_file_path = "ktor-client-encoding-jvm-3.1.3-sources.jar" ) +http_file( + name = "org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_8_1_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1.jar", + sha256 = "3565b6d4d789bf70683c45566944287fc1d8dc75c23d98bd87d01059cc76f2b3", + downloaded_file_path = "kotlinx-serialization-core-jvm-1.8.1.jar" +) + +http_file( + name = "org_jetbrains_kotlinx-kotlinx-serialization-core-jvm-1_8_1-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1-sources.jar", + sha256 = "d4a7221a0c2ccb47bc736340344b256506cf8a5a49882883f8f56385ae69a1bc", + downloaded_file_path = "kotlinx-serialization-core-jvm-1.8.1-sources.jar" +) + http_file( name = "com_miglayout-miglayout-swing-11_4_2_http", url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/miglayout/miglayout-swing/11.4.2/miglayout-swing-11.4.2.jar", From 941e5c176481fa8115a50e3c98020f4694f3f548 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Mon, 20 Oct 2025 13:35:13 +0200 Subject: [PATCH 070/105] [kotlin] code reformat GitOrigin-RevId: 17bc40613b839a89a2793308d27cdc2aa0d8006c --- .../KtCompilerPluginsProviderIdeImpl.kt | 86 ++++++++----------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/extensions/KtCompilerPluginsProviderIdeImpl.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/extensions/KtCompilerPluginsProviderIdeImpl.kt index bec0fdce712a..e9d1cab87095 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/extensions/KtCompilerPluginsProviderIdeImpl.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/extensions/KtCompilerPluginsProviderIdeImpl.kt @@ -50,10 +50,10 @@ import org.jetbrains.kotlin.idea.base.projectStructure.openapiModule import org.jetbrains.kotlin.idea.base.projectStructure.sourceModuleKind import org.jetbrains.kotlin.idea.base.util.caching.getChanges import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerPluginsScriptConfigurationListener import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettingsListener import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.isKotlinFacet -import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerPluginsScriptConfigurationListener import org.jetbrains.kotlin.idea.util.getOriginalOrDelegateFileOrSelf import org.jetbrains.kotlin.idea.workspaceModel.KotlinSettingsEntity import org.jetbrains.kotlin.scripting.compiler.plugin.impl.makeScriptCompilerArguments @@ -75,8 +75,7 @@ internal class KtCompilerPluginsProviderIdeImpl( private val pluginsCache: PluginsCache? get() = pluginsCacheCachedValue.value - private val onlyBundledPluginsEnabledRegistryValue: RegistryValue = - Registry.get("kotlin.k2.only.bundled.compiler.plugins.enabled") + private val onlyBundledPluginsEnabledRegistryValue: RegistryValue = Registry.get("kotlin.k2.only.bundled.compiler.plugins.enabled") private val onlyBundledPluginsEnabled: Boolean get() = onlyBundledPluginsEnabledRegistryValue.asBoolean() @@ -96,29 +95,25 @@ internal class KtCompilerPluginsProviderIdeImpl( } } val messageBusConnection = project.messageBus.connect(this) - messageBusConnection.subscribe(KotlinCompilerSettingsListener.TOPIC, - object : KotlinCompilerSettingsListener { + messageBusConnection.subscribe( + KotlinCompilerSettingsListener.TOPIC, object : KotlinCompilerSettingsListener { override fun settingsChanged(oldSettings: T?, newSettings: T?) { resetPluginsCache() } - } - ) + }) messageBusConnection.subscribe( - KotlinCompilerPluginsScriptConfigurationListener.TOPIC, - object : KotlinCompilerPluginsScriptConfigurationListener { + KotlinCompilerPluginsScriptConfigurationListener.TOPIC, object : KotlinCompilerPluginsScriptConfigurationListener { override fun scriptConfigurationsChanged() { resetScriptCache(pluginsCacheCachedValue.valueIfInitialized ?: return) } - } - ) + }) onlyBundledPluginsEnabledRegistryValue.addListener( object : RegistryValueListener { override fun afterValueChanged(value: RegistryValue) { resetPluginsCache() } - }, - this + }, this ) } @@ -135,8 +130,7 @@ internal class KtCompilerPluginsProviderIdeImpl( return PluginsCache( pluginsClassLoader, ContainerUtil.createConcurrentWeakMap(), - SynchronizedClearableLazy { ContainerUtil.createConcurrentWeakMap() } - ) + SynchronizedClearableLazy { ContainerUtil.createConcurrentWeakMap() }) } private class PluginsCache( @@ -158,19 +152,20 @@ internal class KtCompilerPluginsProviderIdeImpl( val registrarForModule = pluginsCache?.registrarForSourceModule ?: return emptyList() module.getExtensionsForModule(registrarForModule, module, extensionType) } + is KaScriptModule -> { val registrarForModule = pluginsCache?.registrarForScriptModule?.value ?: return emptyList() - val cacheKey = module.file.virtualFile - .getOriginalOrDelegateFileOrSelf() + val cacheKey = module.file.virtualFile.getOriginalOrDelegateFileOrSelf() module.getExtensionsForModule(registrarForModule, cacheKey, extensionType) } + else -> emptyList() } } @OptIn(KaExperimentalApi::class) - private fun KaModule.getExtensionsForModule( + private fun KaModule.getExtensionsForModule( registrarForModule: ConcurrentMap>, cacheKey: K, extensionType: ProjectExtensionDescriptor @@ -179,8 +174,7 @@ internal class KtCompilerPluginsProviderIdeImpl( Optional.ofNullable(computeExtensionStorage(this)) }.orNull() ?: return emptyList() val registrars = extensionStorage.registeredExtensions[extensionType] ?: return emptyList() - @Suppress("UNCHECKED_CAST") - return registrars as List + @Suppress("UNCHECKED_CAST") return registrars as List } override fun isPluginOfTypeRegistered(module: KaModule, pluginType: CompilerPluginType): Boolean { @@ -189,8 +183,7 @@ internal class KtCompilerPluginsProviderIdeImpl( else -> return false } - return getRegisteredExtensions(module, FirExtensionRegistrarAdapter) - .map { (it as FirExtensionRegistrar).configure() } + return getRegisteredExtensions(module, FirExtensionRegistrarAdapter).map { (it as FirExtensionRegistrar).configure() } .any { it.extensions[extension]?.isNotEmpty() == true } } @@ -207,6 +200,7 @@ internal class KtCompilerPluginsProviderIdeImpl( val providedOptions = scriptConfiguration[ScriptCompilationConfiguration.compilerOptions] ?: return null makeScriptCompilerArguments(providedOptions) } + else -> { return null } @@ -220,8 +214,7 @@ internal class KtCompilerPluginsProviderIdeImpl( val pluginRegistrars = logger.runAndLogException { ServiceLoaderLite.loadImplementations(pluginClasspaths, classLoader) } - ?.takeIf { it.isNotEmpty() } - ?: return null + ?.takeIf { it.isNotEmpty() } ?: return null ProgressManager.checkCanceled() @@ -231,23 +224,23 @@ internal class KtCompilerPluginsProviderIdeImpl( ProgressManager.checkCanceled() - val compilerConfiguration = CompilerConfiguration().apply { - // Temporary work-around for KTIJ-24320. Calls to 'setupCommonArguments()' and 'setupJvmSpecificArguments()' - // (or even a platform-agnostic alternative) should be added. - if (compilerArguments is K2JVMCompilerArguments && module is KaSourceModule) { - val compilerExtension = CompilerModuleExtension.getInstance(module.openapiModule) - val outputUrl = when (module.sourceModuleKind) { - KaSourceModuleKind.TEST -> compilerExtension?.compilerOutputUrlForTests - KaSourceModuleKind.PRODUCTION, null -> compilerExtension?.compilerOutputUrl + val compilerConfiguration = + CompilerConfiguration().apply { // Temporary work-around for KTIJ-24320. Calls to 'setupCommonArguments()' and 'setupJvmSpecificArguments()' + // (or even a platform-agnostic alternative) should be added. + if (compilerArguments is K2JVMCompilerArguments && module is KaSourceModule) { + val compilerExtension = CompilerModuleExtension.getInstance(module.openapiModule) + val outputUrl = when (module.sourceModuleKind) { + KaSourceModuleKind.TEST -> compilerExtension?.compilerOutputUrlForTests + KaSourceModuleKind.PRODUCTION, null -> compilerExtension?.compilerOutputUrl + } + + putIfNotNull(JVMConfigurationKeys.JVM_TARGET, compilerArguments.jvmTarget?.let(JvmTarget::fromString)) + putIfNotNull(JVMConfigurationKeys.OUTPUT_DIRECTORY, outputUrl?.let { File(it) }) } - putIfNotNull(JVMConfigurationKeys.JVM_TARGET, compilerArguments.jvmTarget?.let(JvmTarget::fromString)) - putIfNotNull(JVMConfigurationKeys.OUTPUT_DIRECTORY, outputUrl?.let { File(it) }) + processCompilerPluginsOptions(this, compilerArguments.pluginOptions?.toList(), commandLineProcessors) } - processCompilerPluginsOptions(this, compilerArguments.pluginOptions?.toList(), commandLineProcessors) - } - val storage = CompilerPluginRegistrar.ExtensionStorage() for (pluginRegistrar in pluginRegistrars) { ProgressManager.checkCanceled() @@ -258,11 +251,9 @@ internal class KtCompilerPluginsProviderIdeImpl( pluginRegistrar, compilerConfiguration ) ?: compilerConfiguration storage.registerExtensions(configuration) - } - catch (e : ProcessCanceledException) { + } catch (e: ProcessCanceledException) { throw e - } - catch (e: Throwable) { + } catch (e: Throwable) { LOG.error(e) } } @@ -294,9 +285,7 @@ internal class KtCompilerPluginsProviderIdeImpl( * compiler plugins jars. */ private fun collectSubstitutedPluginClasspaths(compilerArguments: List): List { - val combinedOriginalClasspaths = compilerArguments.asSequence() - .flatMap { it.getOriginalPluginClasspaths() } - .distinct() + val combinedOriginalClasspaths = compilerArguments.asSequence().flatMap { it.getOriginalPluginClasspaths() }.distinct() val substitutedClasspaths = combinedOriginalClasspaths.mapNotNull(::substitutePluginJar).distinct() @@ -319,8 +308,9 @@ internal class KtCompilerPluginsProviderIdeImpl( private fun Module.getCompilerArguments(): CommonCompilerArguments { - return KotlinFacet.get(this)?.configuration?.settings?.mergedCompilerArguments - ?: KotlinCommonCompilerArgumentsHolder.getInstance(project).settings + return KotlinFacet.get(this)?.configuration?.settings?.mergedCompilerArguments ?: KotlinCommonCompilerArgumentsHolder.getInstance( + project + ).settings } override fun dispose() { @@ -334,8 +324,7 @@ internal class KtCompilerPluginsProviderIdeImpl( private fun resetPluginsCache() { val droppedCache = pluginsCacheCachedValue.drop() ?: return - droppedCache.registrarForSourceModule - .values.mapNotNull { it.orNull() }.disposeAll() + droppedCache.registrarForSourceModule.values.mapNotNull { it.orNull() }.disposeAll() resetScriptCache(droppedCache) } @@ -363,6 +352,7 @@ internal class KtCompilerPluginsProviderIdeImpl( fun getInstance(project: Project): KtCompilerPluginsProviderIdeImpl { return KotlinCompilerPluginsProvider.getInstance(project) as KtCompilerPluginsProviderIdeImpl } + private val LOG = logger() } } From bde5fe7446d0e3a9a74c620763510405f9f0734c Mon Sep 17 00:00:00 2001 From: Nikita Katkov Date: Mon, 20 Oct 2025 16:10:51 +0200 Subject: [PATCH 071/105] [run dashboard] IJPL-208298 Respect configuration statuses on project reopening GitOrigin-RevId: 88d21f97eeb2fc63c1b63140ef5922678d32a4bc --- .../src/BackendRunDashboardManagerState.kt | 9 ++++++--- .../execution.dashboard/src/RunDashboardManagerImpl.java | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt b/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt index 0e1f78b1dfd6..79390481c197 100644 --- a/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt +++ b/platform/execution.dashboard/src/BackendRunDashboardManagerState.kt @@ -51,9 +51,12 @@ internal class BackendRunDashboardManagerState(private val project: Project) { sharedSettings.value = RunDashboardSettingsDto(openRunningConfigInTab) } - fun fireStatusUpdated(backendService: RunDashboardService) { - sharedStatuses.tryEmit(ServiceStatusDto(backendService.uuid, - RunDashboardRunConfigurationStatus.getStatus(backendService.descriptor).id)) + fun fireStatusUpdated(backendService: RunDashboardService, persistedStatus: RunDashboardRunConfigurationStatus?) { + val effectiveStatus = when { + backendService.descriptor == null && persistedStatus != null -> persistedStatus + else -> RunDashboardRunConfigurationStatus.getStatus(backendService.descriptor) + } + sharedStatuses.tryEmit(ServiceStatusDto(backendService.uuid, effectiveStatus.id)) } fun getStatuses(): Flow { diff --git a/platform/execution.dashboard/src/RunDashboardManagerImpl.java b/platform/execution.dashboard/src/RunDashboardManagerImpl.java index 4709a3075035..48b13ca46bc2 100644 --- a/platform/execution.dashboard/src/RunDashboardManagerImpl.java +++ b/platform/execution.dashboard/src/RunDashboardManagerImpl.java @@ -485,7 +485,7 @@ public final class RunDashboardManagerImpl implements RunDashboardManager, Persi @Override public void updateDashboard(boolean withStructure) { for (RunDashboardService backendService : getRunConfigurations()) { - mySharedState.fireStatusUpdated(backendService); + mySharedState.fireStatusUpdated(backendService, getPersistedStatus(backendService.getConfigurationSettings().getConfiguration())); var applicableCustomizers = getCustomizers(backendService.getConfigurationSettings(), backendService.getDescriptor()); if (applicableCustomizers.isEmpty()) continue; From d159c83306ac333129e233c3c36ed24507a436bc Mon Sep 17 00:00:00 2001 From: Daniil Kalinin Date: Mon, 20 Oct 2025 14:37:41 +0200 Subject: [PATCH 072/105] PY-82344 Update the dialog background to a new one GitOrigin-RevId: 5e5ccd6e8acbcac5e25cd8fd476de6b7d67c23fc --- ...rm-onboarding-gradient-background-dark.svg | 52 ------------------- ...m-onboarding-gradient-background-light.svg | 51 ------------------ ...pycharm-onboarding-gradient-background.svg | 27 ++++++++++ .../PyCharmDialogBackgroundImageProvider.kt | 3 +- 4 files changed, 28 insertions(+), 105 deletions(-) delete mode 100644 python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-dark.svg delete mode 100644 python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-light.svg create mode 100644 python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background.svg diff --git a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-dark.svg b/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-dark.svg deleted file mode 100644 index e6a0758c24a3..000000000000 --- a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-dark.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-light.svg b/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-light.svg deleted file mode 100644 index 894275f4b10f..000000000000 --- a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background-light.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background.svg b/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background.svg new file mode 100644 index 000000000000..b44f39ae9be3 --- /dev/null +++ b/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/resources/img/pycharm-onboarding-gradient-background.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/src/com/intellij/pycharm/community/ide/implTraining/PyCharmDialogBackgroundImageProvider.kt b/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/src/com/intellij/pycharm/community/ide/implTraining/PyCharmDialogBackgroundImageProvider.kt index b8c6ae258245..88723d49d071 100644 --- a/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/src/com/intellij/pycharm/community/ide/implTraining/PyCharmDialogBackgroundImageProvider.kt +++ b/python/ide/implTraining/intellij.pycharm.community.ide.implTraining/src/com/intellij/pycharm/community/ide/implTraining/PyCharmDialogBackgroundImageProvider.kt @@ -5,6 +5,5 @@ import com.intellij.openapi.wm.impl.DialogBackgroundImageProviderBase import java.net.URL internal class PyCharmDialogBackgroundImageProvider : DialogBackgroundImageProviderBase() { - override fun getImageUrl(isDark: Boolean): URL? = javaClass.getResource(if (isDark) "/img/pycharm-onboarding-gradient-background-dark.svg" - else "/img/pycharm-onboarding-gradient-background-light.svg") + override fun getImageUrl(isDark: Boolean): URL? = javaClass.getResource("/img/pycharm-onboarding-gradient-background.svg") } \ No newline at end of file From 72e2c3d36533c4672f56afd82781b33ccc5b87d6 Mon Sep 17 00:00:00 2001 From: Daniil Kalinin Date: Mon, 20 Oct 2025 15:41:44 +0200 Subject: [PATCH 073/105] PY-82344 Implement `Welcome to unified PyCharm!` promo welcome screen banner, refactoring GitOrigin-RevId: b80df8b40facfbf114d0d8965756768cb799f198 --- .../promo/backgrounds/promotion_bg.svg | 103 ---------------- .../PyCommunityToUnifiedPromoService.kt | 3 + ...PyCommunityToUnifiedWelcomeScreenBanner.kt | 67 ++--------- .../PyPromoSharedComponents.kt | 2 +- ...PycharmCommunityIdeImplPromotionIcons.java | 9 +- .../resources/backgrounds/promotion_bg.svg | 113 ++++++++++++++++++ python/ide/impl/resources/icons/close.svg | 4 + .../ide/impl/resources/icons/close_dark.svg | 4 + .../intellij.pycharm.community.ide.impl.xml | 4 + ...armCommunityCustomizationBundle.properties | 7 +- .../icons/PycharmCommunityIdeImplIcons.java | 26 ++++ ...mmunityToUnifiedWelcomeScreenBannerBase.kt | 93 ++++++++++++++ .../WelcomeToUnifiedWelcomeScreenBanner.kt | 112 +++++++++++++++++ 13 files changed, 375 insertions(+), 172 deletions(-) delete mode 100644 python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg create mode 100644 python/ide/impl/resources/backgrounds/promotion_bg.svg create mode 100644 python/ide/impl/resources/icons/close.svg create mode 100644 python/ide/impl/resources/icons/close_dark.svg create mode 100644 python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java create mode 100644 python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt create mode 100644 python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg deleted file mode 100644 index ba211d578e33..000000000000 --- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt index 82c0bc0846d9..194d3b9bafa9 100644 --- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt +++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.updateSettings.UpdateStrategyCustomization import com.intellij.openapi.updateSettings.impl.* import com.intellij.openapi.util.registry.Registry +import com.intellij.pycharm.community.ide.impl.promo.WelcomeToUnifiedWelcomeScreenBanner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.sync.Mutex @@ -48,6 +49,8 @@ internal class PyCommunityToUnifiedPromoService(val serviceScope: CoroutineScope val propertiesComponent = PropertiesComponent.getInstance() propertiesComponent.unsetValue(PROMO_UPDATE_DECLINED) propertiesComponent.unsetValue(PROMO_REMIND_ME_LATER_LAST_TIME_CLICKED) + propertiesComponent.unsetValue(WelcomeToUnifiedWelcomeScreenBanner.BANNER_CLOSED_PROPERTY) + Registry.get(REG_PROMO_INTERVAL_SECONDS).resetToDefault() } diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt index 781bc1a6f0f1..5966df278b54 100644 --- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt +++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt @@ -4,18 +4,16 @@ package com.intellij.pycharm.community.ide.impl.promotion.communityToUnified import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI import com.intellij.openapi.application.EDT import com.intellij.openapi.components.service -import com.intellij.openapi.wm.BannerStartPagePromoter import com.intellij.openapi.wm.StartPagePromoter +import com.intellij.pycharm.community.ide.impl.icons.PycharmCommunityIdeImplIcons +import com.intellij.pycharm.community.ide.impl.promo.PyCommunityToUnifiedWelcomeScreenBannerBase import com.intellij.pycharm.community.ide.impl.promotion.communityToUnified.statistics.PyCommunityUnifiedPromoFusCollector -import com.intellij.pycharm.community.ide.impl.promotion.icons.PycharmCommunityIdeImplPromotionIcons -import com.intellij.ui.components.panels.BackgroundRoundedPanel import com.intellij.ui.components.panels.Wrapper import com.intellij.ui.dsl.builder.EmptySpacingConfiguration import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.gridLayout.UnscaledGaps import com.intellij.ui.dsl.gridLayout.UnscaledGapsY -import com.intellij.util.IconUtil import com.intellij.util.PlatformUtils import com.intellij.util.ui.JBFont import com.intellij.util.ui.JBUI @@ -24,21 +22,16 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls -import java.awt.Graphics -import java.awt.Graphics2D -import java.awt.geom.RoundRectangle2D -import javax.swing.BoxLayout import javax.swing.Icon import javax.swing.JComponent import javax.swing.JPanel -import kotlin.math.min @ApiStatus.Internal -internal class PyCommunityToUnifiedWelcomeScreenBanner : BannerStartPagePromoter() { +internal class PyCommunityToUnifiedWelcomeScreenBanner : PyCommunityToUnifiedWelcomeScreenBannerBase() { override val headerLabel: @Nls String = "" override val actionLabel: @Nls String = "" override val description: @Nls String = "" - override val promoImage: Icon = PycharmCommunityIdeImplPromotionIcons.Backgrounds.Promotion_bg + override val promoImage: Icon = PycharmCommunityIdeImplIcons.Backgrounds.Promotion_bg private val mainBannerPanel = createMainPanel() @@ -58,19 +51,19 @@ internal class PyCommunityToUnifiedWelcomeScreenBanner : BannerStartPagePromoter } } } - return container + return wrapIntoSvgBackgroundPanel(container) } - override fun runAction() {} - override fun canCreatePromo(isEmptyState: Boolean): Boolean { return PlatformUtils.isPyCharmCommunity() } + override fun getMainPanel(): JPanel = container + override fun getPriorityLevel(): Int = StartPagePromoter.PRIORITY_LEVEL_HIGH - private fun createMainPanel(): JPanel { - val panel = panel { + private fun createMainPanel(): JPanel = + panel { customizeSpacingConfiguration(EmptySpacingConfiguration()) { row { label(PyPromoSharedComponents.headerTitle) @@ -101,46 +94,4 @@ internal class PyCommunityToUnifiedWelcomeScreenBanner : BannerStartPagePromoter isOpaque = false border = JBUI.Borders.empty(0, 12, 16, 0) } - return wrapIntoSvgBackgroundPanel(panel, JBUI.scale(16), promoImage) - } - - private fun wrapIntoSvgBackgroundPanel(panel: JPanel, arc: Int, bgImage: Icon): JPanel { - val svgPanel = SvgBackgroundRightAnchoredPanel(arc, bgImage).apply { - layout = BoxLayout(this, BoxLayout.X_AXIS) - } - svgPanel.add(panel) - return svgPanel - } - - private class SvgBackgroundRightAnchoredPanel( - private val arc: Int, - private val bgImage: Icon, - ) : BackgroundRoundedPanel(arc) { - - override fun paintComponent(g: Graphics) { - super.paintComponent(g) - if (width <= 0 || height <= 0) return - - val iw = bgImage.iconWidth - val ih = bgImage.iconHeight - if (iw <= 0 || ih <= 0) return - - val g2 = g.create() as Graphics2D - try { - g2.clip(RoundRectangle2D.Float(0f, 0f, width.toFloat(), height.toFloat(), arc.toFloat(), arc.toFloat())) - val sx = width.toFloat() / iw - val sy = height.toFloat() / ih - val scale = min(sx, sy) - - val scaled = IconUtil.scale(bgImage, this, scale) - // Right-anchored (no padding) - val x = width - scaled.iconWidth - val y = 0 - scaled.paintIcon(this, g2, x, y) - } - finally { - g2.dispose() - } - } - } } \ No newline at end of file diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt index 156c2b63f08a..33406bf2a405 100644 --- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt +++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt @@ -9,7 +9,7 @@ import java.awt.Color internal object PyPromoSharedComponents { - val popUpImg = PycharmCommunityIdeImplPromotionIcons.Images.Popup_img + val popUpImg = PycharmCommunityIdeImplPromotionIcons.Popup_img val infoFontColor = JBColor(Color.decode("#6C707E"), Color.decode("#A8ADBD")) // URLs used by promo-related messages diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java index a4ad5d26b7fd..370c819993a6 100644 --- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java +++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java @@ -15,12 +15,5 @@ public final class PycharmCommunityIdeImplPromotionIcons { private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) { return IconManager.getInstance().loadRasterizedIcon(path, PycharmCommunityIdeImplPromotionIcons.class.getClassLoader(), cacheKey, flags); } - - public static final class Backgrounds { - /** 360x128 */ public static final @NotNull Icon Promotion_bg = load("promo/backgrounds/promotion_bg.svg", -882473096, 0); - } - - public static final class Images { - /** 384x242 */ public static final @NotNull Icon Popup_img = load("promo/images/popup_img.svg", 986597622, 0); - } + /** 384x242 */ public static final @NotNull Icon Popup_img = load("promo/images/popup_img.svg", 986597622, 0); } diff --git a/python/ide/impl/resources/backgrounds/promotion_bg.svg b/python/ide/impl/resources/backgrounds/promotion_bg.svg new file mode 100644 index 000000000000..6795402cbc55 --- /dev/null +++ b/python/ide/impl/resources/backgrounds/promotion_bg.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/ide/impl/resources/icons/close.svg b/python/ide/impl/resources/icons/close.svg new file mode 100644 index 000000000000..4000ed8344b0 --- /dev/null +++ b/python/ide/impl/resources/icons/close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/python/ide/impl/resources/icons/close_dark.svg b/python/ide/impl/resources/icons/close_dark.svg new file mode 100644 index 000000000000..1ded8acf432f --- /dev/null +++ b/python/ide/impl/resources/icons/close_dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml b/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml index 1dd8037757f3..92b2a4495d21 100644 --- a/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml +++ b/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml @@ -144,6 +144,8 @@ + @@ -168,6 +170,8 @@ + diff --git a/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties b/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties index 6cc92ba28ac3..378a29e9f4a2 100644 --- a/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties +++ b/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties @@ -77,11 +77,14 @@ promotion.update.to.unified.banner.header.title=Update to the unified PyCharm promotion.update.to.unified.info.text=Starting with version 2025.3, all updates will be released exclusively for the unified PyCharm.
    Update the IDE to keep receiving new features \u2013 all core functionalities will remain free. promotion.update.to.unified.update.now.button.label=Update Now promotion.update.to.unified.learn.more.hyper.link=Learn More -promotion.update.to.unified.closed.banner.go.to.settings.info=You can find and install the new update in the Settings section promotion.update.to.unified.skip.button.label=Skip action.ShowPyCommunityToUnifiedTooltipAction.text=Community To Unified Tooltip action.ShowPyCommunityToUnifiedTooltipAction.description=For testing purposes action.ShowPyCommunityToUnifiedPromoDialogAction.text=Community To Unified Modal Promo action.ShowPyCommunityToUnifiedPromoDialogAction.description=For testing purposes action.ResetCommunityToUnifiedPromoStateAction.text=Reset Community To Unified Promo State -action.ResetCommunityToUnifiedPromoStateAction.description=For testing purposes \ No newline at end of file +action.ResetCommunityToUnifiedPromoStateAction.description=For testing purposes +promotion.welcome.to.unified.main.text=Everything is in one place! Switch between core and advanced
    features within the same installation. +promotion.welcome.to.unified.title=Welcome to unified PyCharm! +promotion.welcome.to.unified.close=Close +promotion.welcome.to.unified.close.banner=Close banner \ No newline at end of file diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java new file mode 100644 index 000000000000..08864ecbaf8e --- /dev/null +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java @@ -0,0 +1,26 @@ +// 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.pycharm.community.ide.impl.icons; + +import com.intellij.ui.IconManager; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * NOTE THIS FILE IS AUTO-GENERATED + * DO NOT EDIT IT BY HAND, run "Generate icon classes" configuration instead + */ +@org.jetbrains.annotations.ApiStatus.Internal +public final class PycharmCommunityIdeImplIcons { + private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) { + return IconManager.getInstance().loadRasterizedIcon(path, PycharmCommunityIdeImplIcons.class.getClassLoader(), cacheKey, flags); + } + + public static final class Backgrounds { + /** 770x128 */ public static final @NotNull Icon Promotion_bg = load("backgrounds/promotion_bg.svg", -1173415960, 0); + } + + public static final class Icons { + /** 16x16 */ public static final @NotNull Icon Close = load("icons/close.svg", -540869824, 2); + } +} diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt new file mode 100644 index 000000000000..47ee28c8e8cf --- /dev/null +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt @@ -0,0 +1,93 @@ +// 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.pycharm.community.ide.impl.promo + +import com.intellij.openapi.Disposable +import com.intellij.openapi.observable.util.whenDisposed +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.wm.BannerStartPagePromoter +import com.intellij.openapi.wm.impl.IdeBackgroundUtil +import com.intellij.util.SVGLoader +import com.intellij.util.ui.GraphicsUtil +import com.intellij.util.ui.JBInsets +import com.intellij.util.ui.JBSwingUtilities +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.Nls +import java.awt.* +import javax.swing.JComponent +import javax.swing.JPanel + +@ApiStatus.Internal +abstract class PyCommunityToUnifiedWelcomeScreenBannerBase : BannerStartPagePromoter() { + override val headerLabel: @Nls String = "" + override val actionLabel: @Nls String = "" + override val description: @Nls String = "" + + abstract fun getMainPanel(): JPanel + + override fun getPromotion(isEmptyState: Boolean): JComponent { + val mainPanel = getMainPanel() + return wrapIntoSvgBackgroundPanel(mainPanel) + } + + override fun runAction() {} + + fun wrapIntoSvgBackgroundPanel(panel: JPanel): JPanel { + val disposable = Disposer.newDisposable("BackgroundPanel") + val url = WelcomeToUnifiedWelcomeScreenBanner::class.java.getResource("/backgrounds/promotion_bg.svg") ?: return panel + val backgroundImage = SVGLoader.load(url, 1f) + val backgroundPanel = BackgroundPanel(BorderLayout(), backgroundImage, disposable) + backgroundPanel.add(panel, BorderLayout.CENTER) + backgroundPanel.isOpaque = false + return backgroundPanel + } + + private class BackgroundPanel(layout: LayoutManager, private val image: Image, parentDisposable: Disposable) : JPanel(layout) { + + private var disposable: Disposable? = null + + init { + parentDisposable.whenDisposed { clearResources() } + installBackground() + } + + override fun paintComponent(g: Graphics) { + super.paintComponent(g) + val g2 = g.create() as Graphics2D + try { + GraphicsUtil.setupAAPainting(g2) + g2.color = background + g2.fillRoundRect(0, 0, width, height, 16, 16) + } + finally { + g2.dispose() + } + } + + override fun getComponentGraphics(g: Graphics?): Graphics? { + return JBSwingUtilities.runGlobalCGTransform(this, super.getComponentGraphics(g)) + } + + override fun updateUI() { + super.updateUI() + @Suppress("SENSELESS_COMPARISON") + if (image != null) { + installBackground() + } + } + + private fun installBackground() { + clearResources() + disposable = Disposer.newDisposable("BackgroundPanel") + IdeBackgroundUtil.createTemporaryBackgroundTransform(this, image, + IdeBackgroundUtil.Fill.SCALE, + IdeBackgroundUtil.Anchor.MIDDLE_RIGHT, + 1f, JBInsets.emptyInsets(), + disposable) + } + + private fun clearResources() { + disposable?.let { Disposer.dispose(it) } + disposable = null + } + } +} \ No newline at end of file diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt new file mode 100644 index 000000000000..457546a62987 --- /dev/null +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt @@ -0,0 +1,112 @@ +// 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.pycharm.community.ide.impl.promo + + +import com.intellij.ide.AppLifecycleListener +import com.intellij.ide.util.PropertiesComponent +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.ApplicationInfo +import com.intellij.openapi.project.DumbAwareAction +import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle +import com.intellij.pycharm.community.ide.impl.icons.PycharmCommunityIdeImplIcons +import com.intellij.ui.JBColor +import com.intellij.ui.components.panels.Wrapper +import com.intellij.ui.dsl.builder.* +import com.intellij.ui.dsl.gridLayout.UnscaledGaps +import com.intellij.ui.dsl.gridLayout.UnscaledGapsY +import com.intellij.util.IconUtil +import com.intellij.util.PlatformUtils +import com.intellij.util.ui.JBFont +import com.intellij.util.ui.JBUI +import java.awt.Color +import java.awt.Dimension +import javax.swing.Icon +import javax.swing.JPanel + +private const val PREV_BUILD_WAS_PC_PROP = "pycharm.previous.version.was.community" + +class WelcomeToUnifiedWelcomeScreenBanner : PyCommunityToUnifiedWelcomeScreenBannerBase() { + + companion object { + const val BANNER_CLOSED_PROPERTY: String = "WELCOME_TO_UNIFIED_BANNER_CLOSED" + } + + private fun isBannerClosed(): Boolean = PropertiesComponent.getInstance().getBoolean(BANNER_CLOSED_PROPERTY) + private fun onBannerClosed() = PropertiesComponent.getInstance().setValue(BANNER_CLOSED_PROPERTY, true) + + override val promoImage: Icon = PycharmCommunityIdeImplIcons.Backgrounds.Promotion_bg + + private val infoFontColor = JBColor(Color.decode("#6C707E"), Color.decode("#A8ADBD")) + private val closeIcon = IconUtil.scale(PycharmCommunityIdeImplIcons.Icons.Close, null, 1.1f) + + private val myCloseAction = + object : DumbAwareAction(PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.close"), + PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.close.banner"), + closeIcon) { + override fun getActionUpdateThread() = ActionUpdateThread.EDT + override fun actionPerformed(e: AnActionEvent) { + onBannerClosed() + container.isVisible = false + container.repaint() + } + } + + val container: Wrapper = Wrapper(createMainPanel()) + + override fun canCreatePromo(isEmptyState: Boolean): Boolean { + val isUnified = ApplicationInfo.getInstance().build.productCode.equals("PY", ignoreCase = true) + return isUnified && !isBannerClosed() && isUpgradeFromCommunityToUnified() + } + + private fun createMainPanel(): JPanel { + val panel = panel { + customizeSpacingConfiguration(EmptySpacingConfiguration()) { + row { + label(PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.title")) + .applyToComponent { + font = JBFont.h3().asBold() + } + actionButton(myCloseAction) + .applyToComponent { + setMinimumButtonSize { Dimension(closeIcon.iconWidth, closeIcon.iconHeight) } + } + .align(AlignX.RIGHT) + .align(AlignY.TOP) + .customize(UnscaledGaps(right = 8)) + }.customize(customRowGaps = UnscaledGapsY(top = 12)) + row { + text(PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.main.text")) + .applyToComponent { + foreground = infoFontColor + } + }.customize(customRowGaps = UnscaledGapsY(top = 4, bottom = 8)) + } + row { + browserLink(PyCharmCommunityCustomizationBundle.message("promotion.update.to.unified.learn.more.hyper.link"), + "https://blog.jetbrains.com/pycharm/2025/04/unified-pycharm/") + }.topGap(TopGap.NONE) + }.apply { + isOpaque = false + border = JBUI.Borders.empty(0, 12, 16, 0) + } + return panel + } + + override fun getMainPanel(): JPanel = container + + private fun isUpgradeFromCommunityToUnified(): Boolean { + val currentCode = ApplicationInfo.getInstance().build.productCode + + val prevWasCommunity = PropertiesComponent.getInstance().getBoolean(PREV_BUILD_WAS_PC_PROP, false) + return prevWasCommunity && currentCode.equals("PY", ignoreCase = true) + } +} + +class CommunityToUnifiedListener : AppLifecycleListener { + override fun appStarted() { + if (PlatformUtils.isPyCharmCommunity()) { + PropertiesComponent.getInstance().setValue(PREV_BUILD_WAS_PC_PROP, true) + } + } +} \ No newline at end of file From 44106a4d47229701cb163679b74ef682a0dd100a Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Mon, 20 Oct 2025 14:21:24 +0300 Subject: [PATCH 074/105] [build tw] IJPL-211700 Freeze in Slow BuildTreeView.updateNavigationContext update IJPL-188924/IJPL-209213 implementation - add caching for tree node indices GitOrigin-RevId: bf4656f6649a3b6538eb3717bd277b52d35fae01 --- .../buildView/frontend/src/BuildTreeView.kt | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/platform/buildView/frontend/src/BuildTreeView.kt b/platform/buildView/frontend/src/BuildTreeView.kt index 35bcea25a629..3da676c55221 100644 --- a/platform/buildView/frontend/src/BuildTreeView.kt +++ b/platform/buildView/frontend/src/BuildTreeView.kt @@ -348,6 +348,7 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin } private var cachedVisibleChildren: MutableList? = null + private var cachedIndex = -1 private var cachedFilteringState: BuildTreeFilteringState? = null val occurrenceNavigatable: NavigatableId? @@ -363,7 +364,9 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin clearCacheIfInvalid() if (node.isVisible()) { cachedVisibleChildren?.add(node) - treeModel.nodesWereInserted(this, intArrayOf(childCount - 1)) + val newIndex = childCount - 1 + node.cachedIndex = newIndex + treeModel.nodesWereInserted(this, intArrayOf(newIndex)) return true } return false @@ -392,7 +395,7 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin } } else if (wasVisible) { - parent.cachedVisibleChildren?.removeAt(nodeIndex) + parent.clearCache() treeModel.nodesWereRemoved(parent, intArrayOf(nodeIndex), arrayOf(this)) } else { @@ -414,6 +417,7 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin private fun clearCache() { cachedVisibleChildren = null + cachedFilteringState = null } private fun clearCacheIfInvalid() { @@ -431,17 +435,26 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin } fun getVisibleChildren(): List { - val allChildren = children ?: return emptyList() + if (children == null) return emptyList() val cached = cachedVisibleChildren - val currentFilteringState = filteringState - if (cached != null && cachedFilteringState == currentFilteringState) { + if (cached != null && cachedFilteringState == filteringState) { return cached } - cachedFilteringState = currentFilteringState + return rebuildCache() + } + + private fun rebuildCache(): List { + cachedFilteringState = filteringState val result = mutableListOf() - allChildren.forEach { - if ((it as MyNode).isVisible()) { + var index = 0 + children.forEach { + val child = it as MyNode + if (child.isVisible()) { result.add(it) + child.cachedIndex = index++ + } + else { + child.cachedIndex = -1 } } cachedVisibleChildren = result @@ -461,7 +474,11 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin } override fun getIndex(node: TreeNode): Int { - return if ((node as? MyNode)?.isVisible() == true) getVisibleChildren().indexOf(node) else -1 + if (node !is MyNode || children == null || node.parent !== this) return -1 + if (cachedFilteringState != filteringState) { + rebuildCache() + } + return node.cachedIndex } override fun children(): Enumeration { From 36c3dd26d6134a1e4c55185b5a8f6383c2fd774f Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Mon, 20 Oct 2025 15:25:00 +0300 Subject: [PATCH 075/105] [build tw] IJPL-211700 Freeze in Slow BuildTreeView.updateNavigationContext update IJPL-188924/IJPL-209213 implementation - avoid unnecessary navigation context updates GitOrigin-RevId: e6c12548b0a1526cab78fc0d3a4f8d29194db0c9 --- platform/buildView/frontend/src/BuildTreeView.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/platform/buildView/frontend/src/BuildTreeView.kt b/platform/buildView/frontend/src/BuildTreeView.kt index 3da676c55221..e8858cac167c 100644 --- a/platform/buildView/frontend/src/BuildTreeView.kt +++ b/platform/buildView/frontend/src/BuildTreeView.kt @@ -173,11 +173,13 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin is BuildNodesUpdate -> { timeDiff = event.currentTimestamp - System.currentTimeMillis() val nodeInfos = event.nodes + var needsNavigationContextUpdate = false if (nodeInfos.isEmpty()) { LOG.debug("Clearing nodes") durationUpdater.reset() nodeMap.clear() rootNode.removeChildren() + needsNavigationContextUpdate = true } else { nodeInfos.forEach { nodeInfo -> @@ -195,16 +197,23 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin nodeMap[nodeInfo.id] = newNode if (parentNode.addChild(newNode)) { maybeExpand(TreePathUtil.toTreePath(parentNode)) + needsNavigationContextUpdate = needsNavigationContextUpdate || + parentNode.childCount == 1 || // adding first child might change parent's navigatable status + newNode.occurrenceNavigatable != null } } else { LOG.debug { "Updating node (id=${nodeInfo.id})" } + val wasNavigatable = node.isNavigatable durationUpdater.onNodeUpdated(node.content, nodeInfo) node.content = nodeInfo + needsNavigationContextUpdate = needsNavigationContextUpdate || node.childCount != 0 || node.isNavigatable != wasNavigatable } } } - updateNavigationContext() + if (needsNavigationContextUpdate) { + updateNavigationContext() + } } is BuildTreeExposeRequest -> { val nodeId = event.nodeId @@ -354,6 +363,9 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin val occurrenceNavigatable: NavigatableId? get() = if (content.hasProblems && childCount == 0) content.navigatables.firstOrNull() else null + val isNavigatable: Boolean + get() = isVisible() && occurrenceNavigatable != null + fun addChild(node: MyNode): Boolean { assert(node.parent == null) node.parent = this From 4e6b856f79d24851269def12295072315bbee6fc Mon Sep 17 00:00:00 2001 From: Ilya Korennoy Date: Mon, 20 Oct 2025 16:23:54 +0200 Subject: [PATCH 076/105] [platform UI] IJPL-198987 Add a balloon icon if type is not MessageType.INFO GitOrigin-RevId: b7e110cab6da034f82386d0e51f42fa6b23f94c8 --- .../openapi/wm/impl/status/InfoAndProgressPanel.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 40ed8bd31fb8..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) From 3f97aacca46d258b360397c1ba0f1833f39848d9 Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Fri, 17 Oct 2025 16:02:54 +0200 Subject: [PATCH 077/105] [docs] Remove obsolete documentation GitOrigin-RevId: a35bf532660e2c1877022550cabefa273c7c57fa --- .../src/com/intellij/openapi/vfs/AsyncFileListener.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java b/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java index 9fb216127607..b96d223a17b1 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java @@ -1,4 +1,4 @@ -// Copyright 2000-2023 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.vfs; import com.intellij.openapi.progress.ProgressManager; @@ -72,9 +72,6 @@ public interface AsyncFileListener { @Nullable ChangeApplier prepareChange(@NotNull List events); - /** - * Consider using {@link ChangeApplierBackgroundable} to allow running your listener in background threads - */ interface ChangeApplier { /** * This method is called in write action before the VFS events are delivered and applied, and allows From 50219eff579cb986d05064c7840dc2b6e0b6a73f Mon Sep 17 00:00:00 2001 From: Ivan Chirkov Date: Mon, 20 Oct 2025 16:59:16 +0200 Subject: [PATCH 078/105] [plugins] use `getSearchPluginsUpdatesUrl` in `getCompatibleUpdateByModule` GitOrigin-RevId: 500888eb864a16b040c620f1b0da6f38f9f6d12f --- .../plugins/marketplace/MarketplaceRequests.kt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) 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..4a99d36ca149 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 From d450f088ebba95be450f8e4720ec58b0b8ffff8f Mon Sep 17 00:00:00 2001 From: Ivan Chirkov Date: Mon, 20 Oct 2025 17:03:04 +0200 Subject: [PATCH 079/105] [plugins] remove obsolete `getSearchCompatibleUpdatesUrl` and `CompatibleUpdateForModuleRequest` GitOrigin-RevId: 8091054def7f86cda939e682684c2435338ec77b --- .../plugins/marketplace/MarketplaceRequests.kt | 17 ----------------- .../marketplace/utils/MarketplaceUrls.kt | 3 --- 2 files changed, 20 deletions(-) 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 4a99d36ca149..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 @@ -975,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() From 169cf49c67e4ac51d2c7f70546d38f467cddba4d Mon Sep 17 00:00:00 2001 From: Denis Zaichenko Date: Mon, 20 Oct 2025 16:05:30 +0200 Subject: [PATCH 080/105] [remote dev] IJPL-213823 Added sheduleApply method to PluginManagerConfigurable In cases when a user closes the plugin manager from the welcome screen, disposal of the configurable was executed before plugin application, which prevented the plugin from being applied. Added a schedule method that saves the plugin model in a lambda for subsequent plugin application. GitOrigin-RevId: 94cabbb7e588e569c54ed4f67b9b315460ad2f67 --- .../ide/plugins/PluginManagerConfigurable.java | 6 ++++++ .../plugins/PluginManagerConfigurablePanel.java | 15 +++++++++++++++ .../wm/impl/welcomeScreen/PluginsTabFactory.java | 11 +---------- 3 files changed, 22 insertions(+), 10 deletions(-) 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/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(); } }); From 7a18bdf088f9b369044bef9ada4a6e7585aecfab Mon Sep 17 00:00:00 2001 From: "Evgeny.Terekhin" Date: Mon, 20 Oct 2025 12:46:18 +0200 Subject: [PATCH 081/105] [Dotnet Mixed Mode] RIDER-131427. Delegate pausing to the low-level session until the high-level session is ready GitOrigin-RevId: 7fb072e2e06b1cf8b25ab4a9bae3e9072bb8952f --- .../impl/mixedmode/XMixedModeCombinedDebugProcess.kt | 5 +++++ 1 file changed, 5 insertions(+) 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) } From c26cda6b5102e64bab6e2cfeb5310253115b5fb9 Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Tue, 14 Oct 2025 12:41:18 +0200 Subject: [PATCH 082/105] [threading] IJPL-199557: Remove high-level acquisition of write-intent lock from window events and focus handling GitOrigin-RevId: bb4e609b5a8a08a4ff1eeffecfe0a5026941fde3 --- .../com/intellij/openapi/application/ThreadingRuntimeFlags.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt b/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt index acd46b4d9e59..f68e4b4aaa31 100644 --- a/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt +++ b/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt @@ -17,7 +17,7 @@ val useBackgroundWriteAction: Boolean = System.getProperty("idea.background.writ * See IJPL-199557 */ @ApiStatus.Internal -val wrapHighLevelFunctionsInWriteIntent: Boolean = System.getProperty("idea.wrap.high.level.functions.in.write.intent", "true").toBoolean() +val wrapHighLevelFunctionsInWriteIntent: Boolean = System.getProperty("idea.wrap.high.level.functions.in.write.intent", "false").toBoolean() /** * - `false` means that [backgroundWriteAction] will block the thread during lock acquisition From d7a6272365943ba87aa4d53c2920b8ce6853cf87 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Mon, 20 Oct 2025 16:13:48 +0300 Subject: [PATCH 083/105] [debugger] IJPL-194905 Islands: fix the debugger watch editor Apply the relevant customization to the root of the editor hierarchy. GitOrigin-RevId: 018703f855a953e2b71c22f8901e31688337cda6 --- .../xdebugger/impl/ui/XDebuggerExpressionComboBox.java | 5 +++++ 1 file changed, 5 insertions(+) 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 a291de193690..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); } From 4e89ce8d1bb3aed8e18b268845dfc085edebee68 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Mon, 20 Oct 2025 16:25:11 +0300 Subject: [PATCH 084/105] [python] IJPL-210924 Fix the Python Packages dark theme detection Now that UIThemeLookAndFeelInfo has the nice isDark property, we no longer need to hardcode exact themes here, except for the high contrast one. GitOrigin-RevId: 4d8f267d1b7d2c24a04093334a49ca6b5be32a9a --- .../toolwindow/details/PyPackagingJcefHtmlPanel.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/toolwindow/details/PyPackagingJcefHtmlPanel.kt b/python/src/com/jetbrains/python/packaging/toolwindow/details/PyPackagingJcefHtmlPanel.kt index 8bf2be1ff926..1839e0bfe745 100644 --- a/python/src/com/jetbrains/python/packaging/toolwindow/details/PyPackagingJcefHtmlPanel.kt +++ b/python/src/com/jetbrains/python/packaging/toolwindow/details/PyPackagingJcefHtmlPanel.kt @@ -19,11 +19,10 @@ class PyPackagingJcefHtmlPanel(project: Project) : JCEFHtmlPanel(uniqueUrl) { private val cssStyleCodeToInject: String get() { val styleKey = when (val laf = LafManager.getInstance().getCurrentUIThemeLookAndFeel()) { - is UIThemeLookAndFeelInfo -> when (laf.id) { - "ExperimentalDark" -> "python_packaging_toolwindow_dark.css" - "ExperimentalLight" -> "python_packaging_toolwindow_light.css" - "JetBrainsHighContrastTheme" -> "python_packaging_toolwindow_high_contrast.css" - else -> "python_packaging_toolwindow_default.css" + is UIThemeLookAndFeelInfo -> when { + laf.id == "JetBrainsHighContrastTheme" -> "python_packaging_toolwindow_high_contrast.css" + laf.isDark -> "python_packaging_toolwindow_dark.css" + else -> "python_packaging_toolwindow_light.css" } else -> "python_packaging_toolwindow_default.css" } From 9ade171cbfd84e50fff0ee50bc52685ff912c00d Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Tue, 23 Sep 2025 22:58:31 +0200 Subject: [PATCH 085/105] [vcs] IJPL-173924 Replace interactions with changes panel in ChangesViewManager with higher level abstraction Replace direct usage of CommitChangesViewWithToolbarPanel in ChangesViewManager with BackendChangesView. Also introduce BackendCommitChangesViewModel, which will have 2 implementations - one using actual component and one relying on RPC. GitOrigin-RevId: 65e3ff2f014110468f38e3a925b56b383071f312 --- platform/vcs-impl/api-dump.txt | 3 - .../changes/BackendCommitChangesViewModel.kt | 156 +++++++++++++++ .../vcs/changes/ChangesViewManager.java | 180 +++++------------- .../changes/ChangesViewWorkflowManagerImpl.kt | 4 +- 4 files changed, 207 insertions(+), 136 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index 3cf7f15b4611..910a1266e49a 100644 --- a/platform/vcs-impl/api-dump.txt +++ b/platform/vcs-impl/api-dump.txt @@ -812,9 +812,6 @@ f:com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel - 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 diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt new file mode 100644 index 000000000000..052e40715f86 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt @@ -0,0 +1,156 @@ +// 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.Disposable +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vcs.FilePath +import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel.ModelProvider.ExtendedTreeModel +import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory +import com.intellij.openapi.vcs.changes.ui.ChangesListView +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 org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent +import javax.swing.tree.TreePath + +internal class BackendChangesView private constructor( + val changesPanel: JComponent, + val viewModel: BackendCommitChangesViewModel, +): Disposable { + init { + Disposer.register(this, viewModel) + } + + override fun dispose() { + } + + companion object { + @JvmStatic + fun create(project: Project, parentDisposable: Disposable): BackendChangesView { + val tree: ChangesListView = LocalChangesListView(project) + val panel = CommitChangesViewWithToolbarPanel(tree, parentDisposable) + return BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) + } + } +} + +// TODO make RD-friendly implementation, cleanup methods returning tree/component +internal interface BackendCommitChangesViewModel: Disposable { + fun initPanel() + + fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) + + fun isToolbarHorizontal(): Boolean + fun getToolbarComponent(): JComponent + fun setToolbarHorizontal(horizontal: Boolean) + fun getActions(): List + fun isModelUpdateInProgress(): Boolean + + fun setBusy(busy: Boolean) + fun scheduleRefreshNow(callback: Runnable?) + fun scheduleDelayedRefresh() + fun setGrouping(groupingKey: String) + fun resetViewImmediatelyAndRefreshLater() + + fun selectFile(vFile: VirtualFile?) + fun selectChanges(changes: List) + + @ApiStatus.Obsolete + fun getTree(): ChangesListView + @ApiStatus.Obsolete + fun getPreferredFocusableComponent(): JComponent +} + +private class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel): BackendCommitChangesViewModel { + private var commitWorkflowHandler: ChangesViewCommitWorkflowHandler? = null + + init { + Disposer.register(this, panel) + } + + override fun initPanel() { + panel.initPanel(ModelProvider()) + } + + override fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) { + commitWorkflowHandler = handler + } + + override fun isToolbarHorizontal(): Boolean = panel.isToolbarHorizontal + + override fun getToolbarComponent(): JComponent = panel.toolbar.component + + 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 setBusy(busy: Boolean) { + panel.setBusy(busy) + } + + 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 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) } + } + TreeUtil.selectPaths(panel.changesView, paths) + } + + override fun getTree(): ChangesListView = panel.changesView + + override fun getPreferredFocusableComponent(): JComponent = panel.changesView.preferredFocusedComponent + + override fun dispose() { + } + + private inner class ModelProvider : CommitChangesViewWithToolbarPanel.ModelProvider { + override fun getModel(grouping: ChangesGroupingPolicyFactory): ExtendedTreeModel { + val project = panel.project + val changeListManager = ChangeListManagerImpl.getInstanceImpl(project) + val changeLists = changeListManager.changeLists + val unversionedFiles = changeListManager.unversionedFilesPaths + + val treeModel = ChangesViewUtil.createTreeModel( + project, + grouping, + changeLists, + unversionedFiles, + ChangesViewSettings.getInstance(project).showIgnored + ) { commitWorkflowHandler?.isActive == true } + return ExtendedTreeModel(changeLists, unversionedFiles, treeModel) + } + + override fun synchronizeInclusion(changeLists: List, unversionedFiles: List) { + commitWorkflowHandler?.synchronizeInclusion(changeLists, unversionedFiles) + } + } +} 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..fa637f675410 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,7 +23,10 @@ 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.*; @@ -39,7 +42,6 @@ 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 +49,17 @@ 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.commit.*; -import com.intellij.vcsUtil.VcsUtil; 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 +67,24 @@ 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 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, this); activity.end(); } - return myChangesPanel; + return myChangesView; } @RequiresEdt @@ -98,9 +92,9 @@ 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, changesView); Disposer.register(this, panel); panel.updateCommitWorkflow(); @@ -108,7 +102,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { myToolWindowPanel = panel; Disposer.register(panel, () -> { // Content is removed from TW - myChangesPanel = null; + myChangesView = null; myToolWindowPanel = null; }); @@ -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 changes) { - if (myToolWindowPanel == null) return; - myToolWindowPanel.selectChanges(changes); + if (myChangesView == null) return; + myChangesView.getViewModel().selectChanges(new ArrayList<>(changes)); } @Override @@ -213,14 +206,14 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { @Override public void setBusy(boolean b) { - if (myToolWindowPanel == null) return; - myToolWindowPanel.myChangesPanel.setBusy(b); + if (myChangesView == null) return; + myChangesView.getViewModel().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 +234,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { @Override public void resetViewImmediatelyAndRefreshLater() { - if (myToolWindowPanel != null) { - myToolWindowPanel.myChangesPanel.resetViewImmediatelyAndRefreshLater(); + if (myChangesView != null) { + myChangesView.getViewModel().resetViewImmediatelyAndRefreshLater(); } } @@ -273,7 +266,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { content.setHelpId(ChangesListView.HELP_ID); content.setComponent(panel); - content.setPreferredFocusableComponent(panel.myView); + content.setPreferredFocusableComponent(panel.myChangesView.getViewModel().getPreferredFocusableComponent()); } } @@ -328,8 +321,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 +338,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,14 +363,14 @@ 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() + return isMinimumSizeSet() || myChangesView.getViewModel().isToolbarHorizontal() ? super.getMinimumSize() - : myChangesPanel.getToolbar().getComponent().getPreferredSize(); + : myChangesView.getViewModel().getToolbarComponent().getPreferredSize(); } }; myContentPanel.addToCenter(myCommitPanelSplitter); @@ -391,17 +382,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 +403,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 +444,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 +482,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 +535,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 +573,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { myCommitWorkflowHandler = null; myCommitPanel = null; } + myChangesView.getViewModel().setCommitWorkflowHandler(myCommitWorkflowHandler); myDispatcher.getMulticaster().allowExcludeFromCommitChanged(); configureToolbars(); @@ -598,12 +589,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 +602,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 +629,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 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 +648,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 changeLists, - @NotNull List 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..87ae4715ef44 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.getTree()) _commitWorkflowHandler = ChangesViewCommitWorkflowHandler(workflow, commitPanel) project.messageBus.syncPublisher(TOPIC).commitWorkflowChanged() From 03ea036f5a1d8f74b748ad4c209a089559046f55 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 24 Sep 2025 10:42:41 +0200 Subject: [PATCH 086/105] [vcs] IJPL-173924 Use provided coroutine scope in CommitChangesViewWithToolbarPanel GitOrigin-RevId: d43d617bff846da9ff03dc92389009faf9c4d042 --- .../CommitChangesViewWithToolbarPanel.kt | 36 ++++++------------- .../changes/BackendCommitChangesViewModel.kt | 32 ++++++++--------- .../vcs/changes/ChangesViewManager.java | 1 - 3 files changed, 24 insertions(+), 45 deletions(-) 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..634bc20721c8 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,16 +3,12 @@ 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 @@ -21,7 +17,6 @@ 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,6 +25,7 @@ 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 @@ -42,22 +38,18 @@ import javax.swing.tree.DefaultTreeModel import kotlin.time.Duration.Companion.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, 100.milliseconds) { refreshView() } private var modelProvider: ModelProvider? = null - private var disposed = false - init { refresher.start() - Disposer.register(parentDisposable, this) } @CalledInAny @@ -69,13 +61,13 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp fun initPanel(modelProvider: ModelProvider) { 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() } @@ -92,10 +84,10 @@ 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() } @@ -125,7 +117,7 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp else { refresher.requestNow() } - scope.launch { + cs.launch { refresher.awaitNotBusy() callback?.run() setBusy(false) @@ -134,7 +126,7 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp @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 { @@ -162,11 +154,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) @@ -206,7 +193,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/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt index 052e40715f86..e18639b62de3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt @@ -4,6 +4,8 @@ package com.intellij.openapi.vcs.changes import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.FilePath @@ -11,9 +13,12 @@ import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel.ModelP import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory import com.intellij.openapi.vcs.changes.ui.ChangesListView import com.intellij.openapi.vfs.VirtualFile +import com.intellij.platform.util.coroutines.childScope 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.CoroutineScope +import kotlinx.coroutines.cancel import org.jetbrains.annotations.ApiStatus import javax.swing.JComponent import javax.swing.tree.TreePath @@ -21,26 +26,24 @@ import javax.swing.tree.TreePath internal class BackendChangesView private constructor( val changesPanel: JComponent, val viewModel: BackendCommitChangesViewModel, -): Disposable { - init { - Disposer.register(this, viewModel) - } - - override fun dispose() { - } - +) { companion object { @JvmStatic fun create(project: Project, parentDisposable: Disposable): BackendChangesView { - val tree: ChangesListView = LocalChangesListView(project) - val panel = CommitChangesViewWithToolbarPanel(tree, parentDisposable) + val scope = project.service().cs.childScope("CommitChangesViewWithToolbarPanel") + Disposer.register(parentDisposable) { scope.cancel() } + + val panel = CommitChangesViewWithToolbarPanel(LocalChangesListView(project), scope) return BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) } } + + @Service(Service.Level.PROJECT) + internal class ScopeProvider(val cs: CoroutineScope) } // TODO make RD-friendly implementation, cleanup methods returning tree/component -internal interface BackendCommitChangesViewModel: Disposable { +internal interface BackendCommitChangesViewModel { fun initPanel() fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) @@ -69,10 +72,6 @@ internal interface BackendCommitChangesViewModel: Disposable { private class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel): BackendCommitChangesViewModel { private var commitWorkflowHandler: ChangesViewCommitWorkflowHandler? = null - init { - Disposer.register(this, panel) - } - override fun initPanel() { panel.initPanel(ModelProvider()) } @@ -129,9 +128,6 @@ private class BackendLocalCommitChangesViewModel(private val panel: CommitChange override fun getPreferredFocusableComponent(): JComponent = panel.changesView.preferredFocusedComponent - override fun dispose() { - } - private inner class ModelProvider : CommitChangesViewWithToolbarPanel.ModelProvider { override fun getModel(grouping: ChangesGroupingPolicyFactory): ExtendedTreeModel { val project = panel.project 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 fa637f675410..83c727b49ab7 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 @@ -94,7 +94,6 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { BackendChangesView changesView = initChangesView(); ChangesViewToolWindowPanel panel = new ChangesViewToolWindowPanel(myProject, changesView); - Disposer.register(this, changesView); Disposer.register(this, panel); panel.updateCommitWorkflow(); From 01be208e17e14e78afbfc2872855c3beedfd93a5 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 24 Sep 2025 11:24:34 +0200 Subject: [PATCH 087/105] [vcs] IJPL-173924 Use SplitComponentBinding for showing changes view GitOrigin-RevId: 07511d78ba8e7e4d9a440652b6f71b45a4a3ba2c --- .../intellij.platform.vcs.impl.frontend.xml | 3 +- .../ChangesViewSplitComponentProvider.kt | 49 +++++++++++++ .../LocalChangesViewContentProvider.kt | 56 --------------- .../openapi/vcs/changes/ChangesViewId.kt | 15 ++++ .../CommitChangesViewWithToolbarPanel.kt | 17 ++++- .../changes/BackendCommitChangesViewModel.kt | 70 ++++++++++++++++++- .../vcs/changes/ChangesViewManager.java | 15 ---- 7 files changed, 148 insertions(+), 77 deletions(-) create mode 100644 platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/ChangesViewSplitComponentProvider.kt delete mode 100644 platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/LocalChangesViewContentProvider.kt create mode 100644 platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewId.kt 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..f54e95e36ae9 --- /dev/null +++ b/platform/vcs-impl/frontend/src/com/intellij/platform/vcs/impl/frontend/changes/ChangesViewSplitComponentProvider.kt @@ -0,0 +1,49 @@ +// 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.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.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 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) { + } + }) + + scope.launch { + ChangeListsViewModel.getInstance(project).changeLists.collect { + panel.scheduleRefresh() + } + } + + return panel + } + + private fun buildModel( + project: Project, + grouping: ChangesGroupingPolicyFactory, + lists: ChangeListsViewModel.ChangeLists + ) = TreeModelBuilder(project, grouping).setChangeLists(lists.lists, false, null).build() +} 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/CommitChangesViewWithToolbarPanel.kt b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/CommitChangesViewWithToolbarPanel.kt index 634bc20721c8..772547d6a5fa 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 @@ -38,7 +38,10 @@ import javax.swing.tree.DefaultTreeModel import kotlin.time.Duration.Companion.milliseconds @ApiStatus.Internal -class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, private val cs: CoroutineScope) : ChangesViewPanel(changesView) { +class CommitChangesViewWithToolbarPanel( + changesView: ChangesListView, + private val cs: CoroutineScope, +) : ChangesViewPanel(changesView) { val project: Project get() = changesView.project private val settings get() = ChangesViewSettings.getInstance(project) @@ -48,6 +51,12 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, private va private var modelProvider: ModelProvider? = null + var id: ChangesViewId? = null + set(value) { + if (field == null) field = value + else error("Id is already assigned") + } + init { refresher.start() } @@ -59,6 +68,8 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, private va @RequiresEdt fun initPanel(modelProvider: ModelProvider) { + checkNotNull(id) + this.modelProvider = modelProvider cs.launch(Dispatchers.UI) { @@ -93,7 +104,7 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, private va } @CalledInAny - fun scheduleRefresh() { + fun scheduleRefresh(){ scheduleRefresh(withDelay = true) } @@ -193,4 +204,4 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, private va internal val EP_NAME = ExtensionPointName("com.intellij.vcs.commitChangesViewInitializer") } } -} \ No newline at end of file +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt index e18639b62de3..cce6646248ff 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt @@ -13,8 +13,13 @@ import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel.ModelP import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory import com.intellij.openapi.vcs.changes.ui.ChangesListView import com.intellij.openapi.vfs.VirtualFile +import com.intellij.platform.kernel.ids.BackendValueIdType +import com.intellij.platform.kernel.ids.storeValueGlobally import com.intellij.platform.util.coroutines.childScope +import com.intellij.platform.vcs.impl.shared.RdLocalChanges import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings +import com.intellij.ui.components.JBLabel +import com.intellij.ui.split.createComponent import com.intellij.util.ui.tree.TreeUtil import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler import kotlinx.coroutines.CoroutineScope @@ -33,8 +38,18 @@ internal class BackendChangesView private constructor( val scope = project.service().cs.childScope("CommitChangesViewWithToolbarPanel") Disposer.register(parentDisposable) { scope.cancel() } - val panel = CommitChangesViewWithToolbarPanel(LocalChangesListView(project), scope) - return BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) + 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 backendChangesView = BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) + val id = storeValueGlobally(scope, backendChangesView.viewModel, BackendChangesViewValueIdType) + panel.id = id + return backendChangesView + } } } @@ -69,6 +84,55 @@ internal interface BackendCommitChangesViewModel { fun getPreferredFocusableComponent(): JComponent } +// FIXME +private class BackendRemoteCommitChangesViewModel(private val project: Project) : BackendCommitChangesViewModel { + private var horizontal: Boolean = true + private val treeView: ChangesListView by lazy { LocalChangesListView(project) } + + override fun initPanel() { + } + + override fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) { + } + + override fun isToolbarHorizontal(): Boolean = horizontal + + override fun getToolbarComponent(): JComponent = JBLabel("Toolbar (RD placeholder)") + + override fun setToolbarHorizontal(horizontal: Boolean) { + this.horizontal = horizontal + } + + override fun getActions(): List = emptyList() + + override fun isModelUpdateInProgress(): Boolean = false + + override fun setBusy(busy: Boolean) { + } + + override fun scheduleRefreshNow(callback: Runnable?) { + } + + override fun scheduleDelayedRefresh() { + } + + override fun setGrouping(groupingKey: String) { + } + + override fun resetViewImmediatelyAndRefreshLater() { + } + + override fun selectFile(vFile: VirtualFile?) { + } + + override fun selectChanges(changes: List) { + } + + override fun getTree(): ChangesListView = treeView + + override fun getPreferredFocusableComponent(): JComponent = treeView.preferredFocusedComponent +} + private class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel): BackendCommitChangesViewModel { private var commitWorkflowHandler: ChangesViewCommitWorkflowHandler? = null @@ -150,3 +214,5 @@ private class BackendLocalCommitChangesViewModel(private val panel: CommitChange } } } + +private object BackendChangesViewValueIdType : BackendValueIdType(::ChangesViewId) \ No newline at end of file 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 83c727b49ab7..ebe884d9abbc 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 @@ -32,11 +32,9 @@ 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; @@ -247,19 +245,6 @@ 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(); From 86a870ca04a22fc8848d750d40e2f26422efa3f8 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 24 Sep 2025 14:08:32 +0200 Subject: [PATCH 088/105] [vcs] IJPL-173924 Get rid of ChangesViewI#setBusy GitOrigin-RevId: 6728bbfd6bc5ab875486eba9abc55790941b39e5 --- platform/vcs-impl/api-dump.txt | 2 -- .../openapi/vcs/changes/BackendCommitChangesViewModel.kt | 8 -------- .../openapi/vcs/changes/ChangeListManagerImpl.java | 1 - .../com/intellij/openapi/vcs/changes/ChangesViewI.java | 1 - .../intellij/openapi/vcs/changes/ChangesViewManager.java | 6 ------ .../intellij/openapi/vcs/changes/DummyChangesView.java | 4 ---- 6 files changed, 22 deletions(-) diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index 910a1266e49a..fddc397e502d 100644 --- a/platform/vcs-impl/api-dump.txt +++ b/platform/vcs-impl/api-dump.txt @@ -781,7 +781,6 @@ 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 @@ -802,7 +801,6 @@ 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 diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt index cce6646248ff..8d9735d49e11 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt @@ -69,7 +69,6 @@ internal interface BackendCommitChangesViewModel { fun getActions(): List fun isModelUpdateInProgress(): Boolean - fun setBusy(busy: Boolean) fun scheduleRefreshNow(callback: Runnable?) fun scheduleDelayedRefresh() fun setGrouping(groupingKey: String) @@ -107,9 +106,6 @@ private class BackendRemoteCommitChangesViewModel(private val project: Project) override fun isModelUpdateInProgress(): Boolean = false - override fun setBusy(busy: Boolean) { - } - override fun scheduleRefreshNow(callback: Runnable?) { } @@ -156,10 +152,6 @@ private class BackendLocalCommitChangesViewModel(private val panel: CommitChange override fun isModelUpdateInProgress(): Boolean = panel.changesView.isModelUpdateInProgress - override fun setBusy(busy: Boolean) { - panel.setBusy(busy) - } - override fun scheduleRefreshNow(callback: Runnable?) { panel.scheduleRefreshNow(callback) } 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/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 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 ebe884d9abbc..6469427fdd96 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 @@ -201,12 +201,6 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { myToolWindowPanel.updateProgressComponent(progress); } - @Override - public void setBusy(boolean b) { - if (myChangesView == null) return; - myChangesView.getViewModel().setBusy(b); - } - @Override public void setGrouping(@NotNull String groupingKey) { if (myChangesView == null) return; 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) { } From ad9fd9bf0f2054b39b0486519432a0ac64009087 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 24 Sep 2025 14:26:03 +0200 Subject: [PATCH 089/105] [vcs] IJPL-173924 Hide private API GitOrigin-RevId: 3c88e13bca5b874849f0371ed102c3afe23b21d8 --- platform/vcs-impl/api-dump.txt | 16 ---------------- .../openapi/vcs/changes/ChangesViewManager.java | 6 +++--- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index fddc397e502d..0000291ee9fe 100644 --- a/platform/vcs-impl/api-dump.txt +++ b/platform/vcs-impl/api-dump.txt @@ -803,22 +803,6 @@ c:com.intellij.openapi.vcs.changes.ChangesViewManager - selectFile(com.intellij.openapi.vfs.VirtualFile):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 -- 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 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 6469427fdd96..2d5ed0fd5905 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 @@ -150,10 +150,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; } @@ -289,7 +289,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"); From 2d95b8520bdcf6b4b06da29e43142ba3ea412f6d Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 24 Sep 2025 15:08:12 +0200 Subject: [PATCH 090/105] [vcs] IJPL-173924 Inline ChangeListViewCommitPanel in ChangesViewCommitPanel GitOrigin-RevId: 80895b7b8072611c6a427237b16926917b6b3111 --- platform/vcs-impl/api-dump.txt | 18 ++- .../vcs/commit/ChangeListViewCommitPanel.kt | 2 +- .../vcs/commit/ChangesViewCommitPanel.kt | 117 ++++++++++++++++-- 3 files changed, 127 insertions(+), 10 deletions(-) diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index 0000291ee9fe..083b314282f1 100644 --- a/platform/vcs-impl/api-dump.txt +++ b/platform/vcs-impl/api-dump.txt @@ -3955,11 +3955,27 @@ a:com.intellij.vcs.commit.ChangeListViewCommitPanel - f:setEditedCommit(com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation):V - f: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 +- getInclusionModel():com.intellij.openapi.vcs.changes.InclusionModel - 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 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..a8c78e117c21 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangeListViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangeListViewCommitPanel.kt @@ -123,7 +123,7 @@ abstract class ChangeListViewCommitPanel @ApiStatus.Internal constructor( } } -private class ChangeListViewCommitProgressPanel( +internal class ChangeListViewCommitProgressPanel( project: Project, private val commitWorkflowUi: ChangesViewCommitWorkflowUi, commitMessage: EditorTextComponent, 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 364e952ff184..58ce7b1228f7 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -2,33 +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.ChangesBrowserNode.UNVERSIONED_FILES_TAG 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.vcs.changes.ui.VcsTreeModelData.* import com.intellij.openapi.wm.ToolWindow +import com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation +import com.intellij.util.application +import com.intellij.util.ui.UIUtil +import com.intellij.util.ui.tree.TreeUtil.* import kotlinx.coroutines.CompletableDeferred import org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent +import kotlin.properties.Delegates.observable class ChangesViewCommitPanel @ApiStatus.Internal constructor( project: Project, private val changesView: ChangesListView, -) : ChangeListViewCommitPanel(project, changesView) { +) : NonModalCommitPanel(project), ChangesViewCommitWorkflowUi { private var isHideToolWindowOnCommit = false + private val progressPanel = ChangeListViewCommitProgressPanel(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) + } + + changesView.setInclusionListener { + WriteIntentReadAction.run { fireInclusionChanged() } + } + changesView.isShowCheckboxes = true + + 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) { + val node = changesView.findNodeInTree(item) + node?.let { changesView.expandSafe(it) } + } + + override fun select(item: Any) { + val path = changesView.findNodePathInTree(item) + path?.let { selectPath(changesView, it, false) } + } + + override fun selectFirst(items: Collection) { + if (items.isEmpty()) return + + val path = treePathTraverser(changesView).preOrderDfsTraversal().find { getLastUserObject(it) in items } + path?.let { selectPath(changesView, it, false) } + } + + override fun setCompletionContext(changeLists: List) { + commitMessage.setChangesSupplier(ChangeListChangesSupplier(changeLists)) + } + + override fun getDisplayedChanges(): List = all(changesView).userObjects(Change::class.java) + override fun getIncludedChanges(): List = included(changesView).userObjects(Change::class.java) + + override fun getDisplayedUnversionedFiles(): List = + allUnderTag(changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java) + + override fun getIncludedUnversionedFiles(): List = + includedUnderTag(changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java) + + override var inclusionModel: InclusionModel? + get() = changesView.inclusionModel + set(value) { + changesView.setInclusionModel(value) + } + + 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() + changesView.isShowCheckboxes = false + changesView.setInclusionListener(null) + } + override fun activate(): Boolean { val toolWindow = getVcsToolWindow() ?: return false val contentManager = ChangesViewContentManager.getInstance(project) @@ -94,8 +196,7 @@ private class ChangesViewCommitTabTitleUpdater(private val project: Project) : C tabContent.displayName = when { contentsCount == 1 -> null project.isCommitToolWindowShown -> message("tab.title.commit") - else -> message("local.changes.tab") - } - } + else ->message("local.changes.tab") + }} } } From a212b24e51cfe982c7fc09621be44cd68b2f733f Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Wed, 24 Sep 2025 15:33:56 +0200 Subject: [PATCH 091/105] [vcs] IJPL-173924 Operate BackendChangesView in ChangesViewCommitPanel GitOrigin-RevId: 6f8540692734381792167207e22306155f1f5843 --- .../changes/BackendCommitChangesViewModel.kt | 85 ++++++++++++++++++- .../changes/ChangesViewWorkflowManagerImpl.kt | 2 +- .../vcs/commit/ChangesViewCommitPanel.kt | 47 ++++------ 3 files changed, 101 insertions(+), 33 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt index 8d9735d49e11..a7b93c53f718 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt @@ -10,8 +10,10 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel.ModelProvider.ExtendedTreeModel +import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode.UNVERSIONED_FILES_TAG import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory 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.kernel.ids.BackendValueIdType import com.intellij.platform.kernel.ids.storeValueGlobally @@ -21,6 +23,7 @@ import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings import com.intellij.ui.components.JBLabel import com.intellij.ui.split.createComponent import com.intellij.util.ui.tree.TreeUtil +import com.intellij.util.ui.tree.TreeUtil.* import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel @@ -40,10 +43,11 @@ internal class BackendChangesView private constructor( if (RdLocalChanges.isEnabled()) { val viewModel = BackendRemoteCommitChangesViewModel(project) - val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType) + val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType) val panel = ChangesViewSplitComponentBinding.createComponent(project, scope, id) return BackendChangesView(panel, viewModel) - } else { + } + else { val panel = CommitChangesViewWithToolbarPanel(LocalChangesListView(project), scope) val backendChangesView = BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) val id = storeValueGlobally(scope, backendChangesView.viewModel, BackendChangesViewValueIdType) @@ -74,11 +78,25 @@ internal interface BackendCommitChangesViewModel { fun setGrouping(groupingKey: String) fun resetViewImmediatelyAndRefreshLater() + fun setInclusionListener(listener: Runnable?) + var inclusionModel: 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 } @@ -88,6 +106,8 @@ private class BackendRemoteCommitChangesViewModel(private val project: Project) private var horizontal: Boolean = true private val treeView: ChangesListView by lazy { LocalChangesListView(project) } + override var inclusionModel: InclusionModel? = null + override fun initPanel() { } @@ -118,6 +138,27 @@ private class BackendRemoteCommitChangesViewModel(private val project: Project) override fun resetViewImmediatelyAndRefreshLater() { } + override fun setInclusionListener(listener: Runnable?) {} + + 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?) { } @@ -129,9 +170,15 @@ private class BackendRemoteCommitChangesViewModel(private val project: Project) override fun getPreferredFocusableComponent(): JComponent = treeView.preferredFocusedComponent } -private class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel): BackendCommitChangesViewModel { +private class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel) : BackendCommitChangesViewModel { private var commitWorkflowHandler: ChangesViewCommitWorkflowHandler? = null + override var inclusionModel: InclusionModel? + get() = panel.changesView.inclusionModel + set(value) { + panel.changesView.setInclusionModel(value) + } + override fun initPanel() { panel.initPanel(ModelProvider()) } @@ -168,6 +215,38 @@ private class BackendLocalCommitChangesViewModel(private val panel: CommitChange panel.resetViewImmediatelyAndRefreshLater() } + override fun setInclusionListener(listener: Runnable?) { + panel.changesView.setInclusionListener(listener) + } + + 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) } 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 87ae4715ef44..8d5176315c8b 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 @@ -42,7 +42,7 @@ internal class ChangesViewWorkflowManagerImpl( // ChangesViewPanel can be reused between workflow instances -> should clean up after ourselves val changesView = (ChangesViewManager.getInstance(project) as ChangesViewManager).initChangesView() val workflow = ChangesViewCommitWorkflow(project) - val commitPanel = ChangesViewCommitPanel(project, changesView.viewModel.getTree()) + val commitPanel = ChangesViewCommitPanel(project, changesView) _commitWorkflowHandler = ChangesViewCommitWorkflowHandler(workflow, commitPanel) project.messageBus.syncPublisher(TOPIC).commitWorkflowChanged() 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 58ce7b1228f7..71a8ec7eaac5 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -8,28 +8,22 @@ 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.InclusionModel -import com.intellij.openapi.vcs.changes.LocalChangeList +import com.intellij.openapi.vcs.changes.* import com.intellij.openapi.vcs.changes.ui.* -import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode.UNVERSIONED_FILES_TAG 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.VcsTreeModelData.* import com.intellij.openapi.wm.ToolWindow import com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation import com.intellij.util.application import com.intellij.util.ui.UIUtil -import com.intellij.util.ui.tree.TreeUtil.* import kotlinx.coroutines.CompletableDeferred 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, + private val changesView: BackendChangesView, ) : NonModalCommitPanel(project), ChangesViewCommitWorkflowUi { private var isHideToolWindowOnCommit = false @@ -49,10 +43,10 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor( support.installSearch(commitMessage.editorField, commitMessage.editorField) } - changesView.setInclusionListener { + changesView.viewModel.setInclusionListener { WriteIntentReadAction.run { fireInclusionChanged() } } - changesView.isShowCheckboxes = true + changesView.viewModel.setShowCheckboxes(true) commitActionsPanel.isCommitButtonDefault = { !progressPanel.isDumbMode && UIUtil.isFocusAncestor(rootComponent ?: component) @@ -77,39 +71,34 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor( } override fun expand(item: Any) { - val node = changesView.findNodeInTree(item) - node?.let { changesView.expandSafe(it) } + changesView.viewModel.expand(item) } override fun select(item: Any) { - val path = changesView.findNodePathInTree(item) - path?.let { selectPath(changesView, it, false) } + changesView.viewModel.select(item) } override fun selectFirst(items: Collection) { - if (items.isEmpty()) return - - val path = treePathTraverser(changesView).preOrderDfsTraversal().find { getLastUserObject(it) in items } - path?.let { selectPath(changesView, it, false) } + changesView.viewModel.selectFirst(items) } override fun setCompletionContext(changeLists: List) { commitMessage.setChangesSupplier(ChangeListChangesSupplier(changeLists)) } - override fun getDisplayedChanges(): List = all(changesView).userObjects(Change::class.java) - override fun getIncludedChanges(): List = included(changesView).userObjects(Change::class.java) + override fun getDisplayedChanges(): List = changesView.viewModel.getDisplayedChanges() + override fun getIncludedChanges(): List = changesView.viewModel.getIncludedChanges() override fun getDisplayedUnversionedFiles(): List = - allUnderTag(changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java) + changesView.viewModel.getDisplayedUnversionedFiles() override fun getIncludedUnversionedFiles(): List = - includedUnderTag(changesView, UNVERSIONED_FILES_TAG).userObjects(FilePath::class.java) + changesView.viewModel.getIncludedUnversionedFiles() override var inclusionModel: InclusionModel? - get() = changesView.inclusionModel + get() = changesView.viewModel.inclusionModel set(value) { - changesView.setInclusionModel(value) + changesView.viewModel.inclusionModel = value } override val commitProgressUi: CommitProgressUi get() = progressPanel @@ -127,8 +116,8 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor( override fun dispose() { super.dispose() - changesView.isShowCheckboxes = false - changesView.setInclusionListener(null) + changesView.viewModel.setShowCheckboxes(false) + changesView.viewModel.setInclusionListener(null) } override fun activate(): Boolean { @@ -136,7 +125,7 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor( val contentManager = ChangesViewContentManager.getInstance(project) saveToolWindowState() - changesView.isShowCheckboxes = true + changesView.viewModel.setShowCheckboxes(true) component.isVisible = true commitActionsPanel.isActive = true @@ -153,7 +142,7 @@ class ChangesViewCommitPanel @ApiStatus.Internal constructor( } clearToolWindowState() - changesView.isShowCheckboxes = false + changesView.viewModel.setShowCheckboxes(false) component.isVisible = false commitActionsPanel.isActive = false From c34dcd654c46578a24aafca4d083645be19ca610 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Thu, 25 Sep 2025 14:20:02 +0200 Subject: [PATCH 092/105] [vcs] IJPL-173924 Get rid of getMinimumSize calculation Toolbar is horizontal by default, so this method is rather redundant GitOrigin-RevId: 49caefe1e3a68e38a1e90d9e0e3134a3ead80d06 --- .../vcs/changes/BackendCommitChangesViewModel.kt | 11 ----------- .../openapi/vcs/changes/ChangesViewManager.java | 10 +--------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt index a7b93c53f718..d57e81bbc7a2 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt @@ -20,7 +20,6 @@ import com.intellij.platform.kernel.ids.storeValueGlobally import com.intellij.platform.util.coroutines.childScope import com.intellij.platform.vcs.impl.shared.RdLocalChanges import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings -import com.intellij.ui.components.JBLabel import com.intellij.ui.split.createComponent import com.intellij.util.ui.tree.TreeUtil import com.intellij.util.ui.tree.TreeUtil.* @@ -67,8 +66,6 @@ internal interface BackendCommitChangesViewModel { fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) - fun isToolbarHorizontal(): Boolean - fun getToolbarComponent(): JComponent fun setToolbarHorizontal(horizontal: Boolean) fun getActions(): List fun isModelUpdateInProgress(): Boolean @@ -114,10 +111,6 @@ private class BackendRemoteCommitChangesViewModel(private val project: Project) override fun setCommitWorkflowHandler(handler: ChangesViewCommitWorkflowHandler?) { } - override fun isToolbarHorizontal(): Boolean = horizontal - - override fun getToolbarComponent(): JComponent = JBLabel("Toolbar (RD placeholder)") - override fun setToolbarHorizontal(horizontal: Boolean) { this.horizontal = horizontal } @@ -187,10 +180,6 @@ private class BackendLocalCommitChangesViewModel(private val panel: CommitChange commitWorkflowHandler = handler } - override fun isToolbarHorizontal(): Boolean = panel.isToolbarHorizontal - - override fun getToolbarComponent(): JComponent = panel.toolbar.component - override fun setToolbarHorizontal(horizontal: Boolean) { panel.isToolbarHorizontal = horizontal } 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 2d5ed0fd5905..a17d5cd8c0c1 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 @@ -54,7 +54,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; import java.util.ArrayList; import java.util.EventListener; import java.util.List; @@ -343,14 +342,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { Disposer.register(this, myCommitPanelSplitter); myCommitPanelSplitter.setFirstComponent(backendChangesView.getChangesPanel()); - myContentPanel = new BorderLayoutPanel() { - @Override - public Dimension getMinimumSize() { - return isMinimumSizeSet() || myChangesView.getViewModel().isToolbarHorizontal() - ? super.getMinimumSize() - : myChangesView.getViewModel().getToolbarComponent().getPreferredSize(); - } - }; + myContentPanel = new BorderLayoutPanel(); myContentPanel.addToCenter(myCommitPanelSplitter); myMainPanelContent = new Wrapper(myContentPanel); JPanel mainPanel = simplePanel(myMainPanelContent) From 962d17f9b268e890ee5bac4dda575665ef3aa2f7 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Fri, 26 Sep 2025 09:24:27 +0200 Subject: [PATCH 093/105] [vcs] IJPL-173924 Set checkboxes initial state in CommitChangesViewWithToolbarPanel.kt GitOrigin-RevId: 0d556d751956931e3d743dcb3ce27eb443652a06 --- .../openapi/vcs/changes/CommitChangesViewWithToolbarPanel.kt | 1 + .../src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) 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 772547d6a5fa..a470eae3b73d 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 @@ -84,6 +84,7 @@ class CommitChangesViewWithToolbarPanel( } } + changesView.isShowCheckboxes = true changesView.installPopupHandler( ActionManager.getInstance().getAction("ChangesViewPopupMenuShared") as ActionGroup ) 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 71a8ec7eaac5..e6dc86fdb719 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -46,7 +46,6 @@ class ChangesViewCommitPanel internal constructor( changesView.viewModel.setInclusionListener { WriteIntentReadAction.run { fireInclusionChanged() } } - changesView.viewModel.setShowCheckboxes(true) commitActionsPanel.isCommitButtonDefault = { !progressPanel.isDumbMode && UIUtil.isFocusAncestor(rootComponent ?: component) From dd0352c01d5d69b399ae3423cb341cde56113de8 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Thu, 25 Sep 2025 19:02:58 +0200 Subject: [PATCH 094/105] [vcs] IJPL-173924 Extract BackendChangesView classes to separate files GitOrigin-RevId: 71a302dbb9127c51bf2a25fdd6c43a9b875149d9 --- .../vcs/changes/ChangesViewManager.java | 1 + .../vcs/changes/BackendChangesView.kt | 47 ++++++ .../changes/BackendChangesViewValueIdType.kt | 8 + .../BackendCommitChangesViewModel.kt | 48 ++++++ .../BackendLocalCommitChangesViewModel.kt} | 150 +----------------- .../BackendRemoteCommitChangesViewModel.kt | 79 +++++++++ .../vcs/commit/ChangesViewCommitPanel.kt | 6 +- 7 files changed, 189 insertions(+), 150 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt create mode 100644 platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt create mode 100644 platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt rename platform/vcs-impl/src/com/intellij/{openapi/vcs/changes/BackendCommitChangesViewModel.kt => vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt} (50%) create mode 100644 platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt 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 a17d5cd8c0c1..3d5712be831d 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 @@ -47,6 +47,7 @@ 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.vcs.changes.BackendChangesView; import com.intellij.vcs.commit.*; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nls; 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..2c0731b75acb --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt @@ -0,0 +1,47 @@ +// 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.Disposable +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vcs.changes.* +import com.intellij.platform.kernel.ids.storeValueGlobally +import com.intellij.platform.util.coroutines.childScope +import com.intellij.platform.vcs.impl.shared.RdLocalChanges +import com.intellij.ui.split.createComponent +import com.intellij.vcs.changes.viewModel.BackendRemoteCommitChangesViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import javax.swing.JComponent + +internal class BackendChangesView private constructor( + val changesPanel: JComponent, + val viewModel: BackendCommitChangesViewModel, +) { + companion object { + @JvmStatic + fun create(project: Project, parentDisposable: Disposable): BackendChangesView { + val scope = project.service().cs.childScope("CommitChangesViewWithToolbarPanel") + Disposer.register(parentDisposable) { scope.cancel() } + + 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 backendChangesView = BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) + val id = storeValueGlobally(scope, backendChangesView.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..058c0e84ffd7 --- /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.BackendCommitChangesViewModel +import com.intellij.openapi.vcs.changes.ChangesViewId +import com.intellij.platform.kernel.ids.BackendValueIdType + +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..72135d7d4201 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt @@ -0,0 +1,48 @@ +// 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.actionSystem.AnAction +import com.intellij.openapi.vcs.FilePath +import com.intellij.openapi.vcs.changes.ui.ChangesListView +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler +import org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent + +// TODO IJPL-173924 cleanup methods returning tree/component +internal interface BackendCommitChangesViewModel { + 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 setInclusionListener(listener: Runnable?) + var inclusionModel: 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/openapi/vcs/changes/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt similarity index 50% rename from platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt rename to platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt index d57e81bbc7a2..4d258a573be0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt @@ -1,13 +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.openapi.vcs.changes -import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction -import com.intellij.openapi.components.Service -import com.intellij.openapi.components.service -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel.ModelProvider.ExtendedTreeModel import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode.UNVERSIONED_FILES_TAG @@ -15,155 +10,14 @@ import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory 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.kernel.ids.BackendValueIdType -import com.intellij.platform.kernel.ids.storeValueGlobally -import com.intellij.platform.util.coroutines.childScope -import com.intellij.platform.vcs.impl.shared.RdLocalChanges import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings -import com.intellij.ui.split.createComponent import com.intellij.util.ui.tree.TreeUtil import com.intellij.util.ui.tree.TreeUtil.* import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancel -import org.jetbrains.annotations.ApiStatus import javax.swing.JComponent import javax.swing.tree.TreePath -internal class BackendChangesView private constructor( - val changesPanel: JComponent, - val viewModel: BackendCommitChangesViewModel, -) { - companion object { - @JvmStatic - fun create(project: Project, parentDisposable: Disposable): BackendChangesView { - val scope = project.service().cs.childScope("CommitChangesViewWithToolbarPanel") - Disposer.register(parentDisposable) { scope.cancel() } - - 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 backendChangesView = BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) - val id = storeValueGlobally(scope, backendChangesView.viewModel, BackendChangesViewValueIdType) - panel.id = id - return backendChangesView - } - } - } - - @Service(Service.Level.PROJECT) - internal class ScopeProvider(val cs: CoroutineScope) -} - -// TODO make RD-friendly implementation, cleanup methods returning tree/component -internal interface BackendCommitChangesViewModel { - 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 setInclusionListener(listener: Runnable?) - var inclusionModel: 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 -} - -// FIXME -private class BackendRemoteCommitChangesViewModel(private val project: Project) : BackendCommitChangesViewModel { - private var horizontal: Boolean = true - private val treeView: ChangesListView by lazy { LocalChangesListView(project) } - - override var inclusionModel: InclusionModel? = null - - 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 setInclusionListener(listener: Runnable?) {} - - 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 -} - -private class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel) : BackendCommitChangesViewModel { +internal class BackendLocalCommitChangesViewModel(private val panel: CommitChangesViewWithToolbarPanel) : BackendCommitChangesViewModel { private var commitWorkflowHandler: ChangesViewCommitWorkflowHandler? = null override var inclusionModel: InclusionModel? @@ -274,5 +128,3 @@ private class BackendLocalCommitChangesViewModel(private val panel: CommitChange } } } - -private object BackendChangesViewValueIdType : BackendValueIdType(::ChangesViewId) \ No newline at end of file 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..024bd8fa7c88 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt @@ -0,0 +1,79 @@ +// 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.BackendCommitChangesViewModel +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 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 var inclusionModel: InclusionModel? = null + + 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 setInclusionListener(listener: Runnable?) {} + + 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/ChangesViewCommitPanel.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt index e6dc86fdb719..14a9c74702c7 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -8,7 +8,10 @@ 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.* +import com.intellij.openapi.vcs.changes.Change +import com.intellij.openapi.vcs.changes.ChangesViewManager +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 @@ -16,6 +19,7 @@ import com.intellij.openapi.wm.ToolWindow import com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation import com.intellij.util.application import com.intellij.util.ui.UIUtil +import com.intellij.vcs.changes.BackendChangesView import kotlinx.coroutines.CompletableDeferred import org.jetbrains.annotations.ApiStatus import javax.swing.JComponent From aeece045c918c1bd4fb282cf0e16628fbefee860 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Fri, 26 Sep 2025 13:22:05 +0200 Subject: [PATCH 095/105] [vcs] IJPL-173924 Don't expose inclusionModel in BackendCommitChangesViewModel The only usage of getter was `ChangeListViewCommitProgressPanel#inclusionChanged`, which is redundant as inclusion notifier ensures that it was actually changed. Also, replace `setInclusionListener` with SharedFlow and get rid of `ChangeListViewCommitProgressPanel` GitOrigin-RevId: df80b2bdff0f6b051ff615769ee39d00cda44eba --- platform/vcs-impl/api-dump.txt | 6 ++-- .../BackendCommitChangesViewModel.kt | 6 ++-- .../BackendLocalCommitChangesViewModel.kt | 18 +++++------ .../BackendRemoteCommitChangesViewModel.kt | 11 +++++-- .../vcs/commit/ChangeListViewCommitPanel.kt | 32 +++---------------- .../vcs/commit/ChangesViewCommitPanel.kt | 24 +++++++------- .../ChangesViewCommitWorkflowHandler.kt | 4 +-- .../vcs/commit/ChangesViewCommitWorkflowUi.kt | 2 +- .../vcs/commit/CommitProgressPanel.kt | 4 +++ 9 files changed, 47 insertions(+), 60 deletions(-) diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index 083b314282f1..98ecf2e9b29d 100644 --- a/platform/vcs-impl/api-dump.txt +++ b/platform/vcs-impl/api-dump.txt @@ -3948,12 +3948,11 @@ 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.NonModalCommitPanel - com.intellij.vcs.commit.ChangesViewCommitWorkflowUi @@ -3968,7 +3967,6 @@ f:com.intellij.vcs.commit.ChangesViewCommitPanel - getEditedCommit():com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation - getIncludedChanges():java.util.List - getIncludedUnversionedFiles():java.util.List -- getInclusionModel():com.intellij.openapi.vcs.changes.InclusionModel - isActive():Z - refreshChangesViewBeforeCommit(kotlin.coroutines.Continuation):java.lang.Object - select(java.lang.Object):V @@ -3981,7 +3979,6 @@ com.intellij.vcs.commit.ChangesViewCommitWorkflowUi - 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 @@ -4088,6 +4085,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/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt index 72135d7d4201..d21aff6e5480 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt @@ -6,11 +6,14 @@ import com.intellij.openapi.vcs.FilePath 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?) @@ -24,8 +27,7 @@ internal interface BackendCommitChangesViewModel { fun setGrouping(groupingKey: String) fun resetViewImmediatelyAndRefreshLater() - fun setInclusionListener(listener: Runnable?) - var inclusionModel: InclusionModel? + fun setInclusionModel(model: InclusionModel?) fun setShowCheckboxes(value: Boolean) fun getDisplayedChanges(): List 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 index 4d258a573be0..c46e1fa53bc4 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt @@ -14,20 +14,24 @@ import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings import com.intellij.util.ui.tree.TreeUtil 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 var inclusionModel: InclusionModel? - get() = panel.changesView.inclusionModel - set(value) { - panel.changesView.setInclusionModel(value) - } + 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?) { @@ -58,10 +62,6 @@ internal class BackendLocalCommitChangesViewModel(private val panel: CommitChang panel.resetViewImmediatelyAndRefreshLater() } - override fun setInclusionListener(listener: Runnable?) { - panel.changesView.setInclusionListener(listener) - } - override fun setShowCheckboxes(value: Boolean) { panel.changesView.isShowCheckboxes = value } 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 index 024bd8fa7c88..76ea446397c4 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt @@ -11,14 +11,21 @@ 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() - override var inclusionModel: InclusionModel? = null + private val inclusionModel = MutableStateFlow(null) + + override fun setInclusionModel(model: InclusionModel?) { + inclusionModel.value = model + } override fun initPanel() { } @@ -46,8 +53,6 @@ internal class BackendRemoteCommitChangesViewModel(private val project: Project) override fun resetViewImmediatelyAndRefreshLater() { } - override fun setInclusionListener(listener: Runnable?) {} - override fun setShowCheckboxes(value: Boolean) {} override fun getDisplayedChanges(): List = emptyList() 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 a8c78e117c21..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) } } - -internal 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 14a9c74702c7..63e606fdf05c 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -2,7 +2,7 @@ package com.intellij.vcs.commit import com.intellij.openapi.Disposable -import com.intellij.openapi.application.WriteIntentReadAction +import com.intellij.openapi.application.writeIntentReadAction import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer @@ -16,11 +16,16 @@ 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.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.BackendChangesView 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 @@ -31,7 +36,7 @@ class ChangesViewCommitPanel internal constructor( ) : NonModalCommitPanel(project), ChangesViewCommitWorkflowUi { private var isHideToolWindowOnCommit = false - 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 @@ -47,9 +52,9 @@ class ChangesViewCommitPanel internal constructor( support.installSearch(commitMessage.editorField, commitMessage.editorField) } - changesView.viewModel.setInclusionListener { - WriteIntentReadAction.run { fireInclusionChanged() } - } + val scope = VcsDisposable.getInstance(project).coroutineScope.childScope("ChangesViewCommitPanel") + Disposer.register(this) { scope.cancel() } + changesView.viewModel.inclusionChanged.onEach { writeIntentReadAction { fireInclusionChanged() } }.launchIn(scope) commitActionsPanel.isCommitButtonDefault = { !progressPanel.isDumbMode && UIUtil.isFocusAncestor(rootComponent ?: component) @@ -98,11 +103,9 @@ class ChangesViewCommitPanel internal constructor( override fun getIncludedUnversionedFiles(): List = changesView.viewModel.getIncludedUnversionedFiles() - override var inclusionModel: InclusionModel? - get() = changesView.viewModel.inclusionModel - set(value) { - changesView.viewModel.inclusionModel = value - } + override fun setInclusionModel(model: InclusionModel?) { + changesView.viewModel.setInclusionModel(model) + } override val commitProgressUi: CommitProgressUi get() = progressPanel @@ -120,7 +123,6 @@ class ChangesViewCommitPanel internal constructor( override fun dispose() { super.dispose() changesView.viewModel.setShowCheckboxes(false) - changesView.viewModel.setInclusionListener(null) } override fun activate(): Boolean { 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 From ee9f43f18725b5bef9324b213e83b31e2d647647 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Fri, 26 Sep 2025 15:44:00 +0200 Subject: [PATCH 096/105] [vcs] IJPL-173924 Make toolbar horizontal by default in ChangesViewPanel GitOrigin-RevId: 75d3fb9625f5c1ef572a2f4b739375b1ec79da28 --- .../src/com/intellij/openapi/vcs/changes/ChangesViewPanel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } From abdcf1913da9c1d5812f2e102b52596cf8ddf7b2 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Fri, 3 Oct 2025 13:22:10 +0200 Subject: [PATCH 097/105] [vcs] IJPL-173924 Inline ChangesTreeEditorDiffPreview GitOrigin-RevId: 494970d849802143ee665cbb8499fe906c080566 --- platform/vcs-impl/api-dump.txt | 30 +++--- .../changes/ChangesTreeEditorDiffPreview.kt | 93 ------------------- .../ui/TreeHandlerDiffRequestProcessor.kt | 85 ++++++++++++++++- 3 files changed, 95 insertions(+), 113 deletions(-) delete mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesTreeEditorDiffPreview.kt diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index 98ecf2e9b29d..825d07ffd87d 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 @@ -2623,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 @@ -2634,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 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/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()) { From b6532013513f7afa9aa71e89f1de44948e8cd09b Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Fri, 3 Oct 2025 09:29:25 +0200 Subject: [PATCH 098/105] [vcs] IJPL-173924 Make ChangesViewUtil shared GitOrigin-RevId: 9909eb2268d2dfde1cd702eab648d7bc70eeea85 --- .../ChangesViewSplitComponentProvider.kt | 16 +++------ .../openapi/vcs/changes/ChangesViewUtil.kt | 33 ++++++------------- .../CommitChangesViewWithToolbarPanel.kt | 26 ++++++++++----- .../shared/changes/ChangeListsViewModel.kt | 2 +- .../impl/shared/changes/TreeModelBuilderEx.kt | 3 +- .../BackendChangesViewModelBuilderService.kt | 11 ++++++- .../BackendLocalCommitChangesViewModel.kt | 12 +++---- 7 files changed, 49 insertions(+), 54 deletions(-) rename platform/vcs-impl/{ => shared}/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt (57%) 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 index f54e95e36ae9..8872df310abe 100644 --- 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 @@ -4,8 +4,6 @@ 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.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.ui.split.SplitComponentBinding import com.intellij.ui.split.SplitComponentProvider @@ -21,11 +19,11 @@ internal class ChangesViewSplitComponentProvider : SplitComponentProvider, unversionedFiles: List) { @@ -40,10 +38,4 @@ internal class ChangesViewSplitComponentProvider : SplitComponentProvider 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) } + .also { TreeModelBuilderEx.getInstanceOrNull(project)?.modifyTreeModelBuilder(it, unversionedFiles) } .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 a470eae3b73d..a244a8fe7bb2 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 @@ -11,7 +11,6 @@ import com.intellij.openapi.progress.checkCanceled import com.intellij.openapi.project.Project 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 @@ -34,7 +33,6 @@ 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 @ApiStatus.Internal @@ -141,16 +139,25 @@ class CommitChangesViewWithToolbarPanel( 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) } } } @@ -191,11 +198,14 @@ class CommitChangesViewWithToolbarPanel( } 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 { 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/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt index 74fa2650592b..aae0eefaaaf5 100644 --- a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt +++ b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt @@ -3,6 +3,7 @@ package com.intellij.platform.vcs.impl.shared.changes import com.intellij.openapi.components.serviceOrNull 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.ui.ChangeListRemoteState import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator @@ -19,7 +20,7 @@ interface TreeModelBuilderEx { */ fun getChangeNodeInChangelistBaseDecorator(listRemoteState: ChangeListRemoteState, change: Change, index: Int): ChangeNodeDecorator? - fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder) + fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder, unversionedFiles: List) companion object { @JvmStatic 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..f75613d9c6f9 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,7 +1,9 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.vcs.changes.ui +import com.intellij.openapi.project.InitialVfsRefreshService import com.intellij.openapi.project.Project +import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.FileStatus import com.intellij.openapi.vcs.changes.* import com.intellij.openapi.vfs.VirtualFile @@ -22,9 +24,16 @@ internal class BackendChangesViewModelBuilderService(private val project: Projec ): ChangeNodeDecorator = RemoteStatusChangeNodeDecorator(revisionsCache, listRemoteState, index) - override fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder) { + override fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder, unversionedFiles: List) { val changeListManager = ChangeListManagerImpl.getInstanceImpl(project) + val shouldShowUntrackedLoading = unversionedFiles.isEmpty() && + !project.getService(InitialVfsRefreshService::class.java).isInitialVfsRefreshFinished() && + changeListManager.isUnversionedInUpdateMode + if (shouldShowUntrackedLoading) { + modelBuilder.insertSubtreeRoot(ChangesBrowserUnversionedLoadingPendingNode()) + } + modelBuilder.setLocallyDeletedPaths(changeListManager.deletedFiles) .setModifiedWithoutEditing(changeListManager.modifiedWithoutEditing) .setSwitchedFiles(changeListManager.switchedFilesMap) 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 index c46e1fa53bc4..05927bdee37f 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt @@ -4,9 +4,7 @@ package com.intellij.openapi.vcs.changes import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.vcs.FilePath -import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel.ModelProvider.ExtendedTreeModel import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode.UNVERSIONED_FILES_TAG -import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory import com.intellij.openapi.vcs.changes.ui.ChangesListView import com.intellij.openapi.vcs.changes.ui.VcsTreeModelData.* import com.intellij.openapi.vfs.VirtualFile @@ -107,20 +105,18 @@ internal class BackendLocalCommitChangesViewModel(private val panel: CommitChang override fun getPreferredFocusableComponent(): JComponent = panel.changesView.preferredFocusedComponent private inner class ModelProvider : CommitChangesViewWithToolbarPanel.ModelProvider { - override fun getModel(grouping: ChangesGroupingPolicyFactory): ExtendedTreeModel { + override fun getModelData(): CommitChangesViewWithToolbarPanel.ModelProvider.ModelData { val project = panel.project val changeListManager = ChangeListManagerImpl.getInstanceImpl(project) val changeLists = changeListManager.changeLists val unversionedFiles = changeListManager.unversionedFilesPaths - val treeModel = ChangesViewUtil.createTreeModel( - project, - grouping, + val ignoredFiles = if (ChangesViewSettings.getInstance(project).showIgnored) changeListManager.ignoredFilePaths else emptyList() + return CommitChangesViewWithToolbarPanel.ModelProvider.ModelData( changeLists, unversionedFiles, - ChangesViewSettings.getInstance(project).showIgnored + ignoredFiles, ) { commitWorkflowHandler?.isActive == true } - return ExtendedTreeModel(changeLists, unversionedFiles, treeModel) } override fun synchronizeInclusion(changeLists: List, unversionedFiles: List) { From 36e0c8eccc0038bfe8c37bba00aab03a7e795e0c Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 13 Oct 2025 17:28:01 +0200 Subject: [PATCH 099/105] [vcs] IJPL-173924 Set `CommitChangesViewWithToolbarPanel.setBusy` reactively GitOrigin-RevId: 0d4b613443a2de05558e4279fe75881f2dfcc331 --- .../changes/CommitChangesViewWithToolbarPanel.kt | 13 +++++-------- .../platform/vcs/impl/shared/SingleTaskRunner.kt | 5 ++++- ...ackendCommitChangesViewWithToolbarInitializer.kt | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) 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 a244a8fe7bb2..172865016025 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 @@ -27,7 +27,6 @@ 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 @@ -57,11 +56,11 @@ class CommitChangesViewWithToolbarPanel( init { refresher.start() - } - - @CalledInAny - fun setBusy(busy: Boolean) { - invokeLaterIfNeeded { changesView.setPaintBusy(busy) } + cs.launch(Dispatchers.UI) { + refresher.getPendingTasksFlow().collect { + isBusy -> changesView.setPaintBusy(isBusy) + } + } } @RequiresEdt @@ -120,7 +119,6 @@ class CommitChangesViewWithToolbarPanel( @CalledInAny private fun scheduleRefresh(withDelay: Boolean, @RequiresBackgroundThread callback: Runnable? = null) { - setBusy(true) if (withDelay) { refresher.request() } @@ -130,7 +128,6 @@ class CommitChangesViewWithToolbarPanel( cs.launch { refresher.awaitNotBusy() callback?.run() - setBusy(false) } } 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..057bb8296fe3 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 @@ -7,6 +7,7 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.checkCanceled import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first @@ -29,6 +30,8 @@ class SingleTaskRunner( private val runNow = Channel(capacity = Channel.CONFLATED) + fun getPendingTasksFlow(): Flow = requested.combine(busy) { requested, busy -> !requested && !busy } + private val processorJob = cs.launch(Dispatchers.Default, CoroutineStart.LAZY) { try { while (true) { @@ -73,7 +76,7 @@ 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 } + getPendingTasksFlow().first { it } } companion object { 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()) From 92bef67ddda8ea67d7bbb1cc07a676cf5b8e7aeb Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Fri, 17 Oct 2025 16:19:06 +0200 Subject: [PATCH 100/105] [vcs] Fix race in SingleTaskRunner GitOrigin-RevId: df99f8eef9fa5a697549817cdcc75154842a2a41 --- .../CommitChangesViewWithToolbarPanel.kt | 17 ++++--- .../vcs/impl/shared/SingleTaskRunner.kt | 47 +++++++---------- .../intellij/vcsUtil/SingleTaskRunnerTest.kt | 50 ++++--------------- .../git4idea/repo/GitUntrackedFilesHolder.kt | 2 +- 4 files changed, 39 insertions(+), 77 deletions(-) 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 172865016025..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 @@ -34,6 +34,8 @@ import org.jetbrains.annotations.CalledInAny import java.lang.Runnable import kotlin.time.Duration.Companion.milliseconds +private val REFRESH_DELAY = 100.milliseconds + @ApiStatus.Internal class CommitChangesViewWithToolbarPanel( changesView: ChangesListView, @@ -42,7 +44,7 @@ class CommitChangesViewWithToolbarPanel( val project: Project get() = changesView.project private val settings get() = ChangesViewSettings.getInstance(project) - private val refresher = SingleTaskRunner(cs, 100.milliseconds) { + private val refresher = SingleTaskRunner(cs) { refreshView() } @@ -57,8 +59,8 @@ class CommitChangesViewWithToolbarPanel( init { refresher.start() cs.launch(Dispatchers.UI) { - refresher.getPendingTasksFlow().collect { - isBusy -> changesView.setPaintBusy(isBusy) + refresher.getIdleFlow().collect { idle -> + changesView.setPaintBusy(!idle) } } } @@ -119,13 +121,14 @@ class CommitChangesViewWithToolbarPanel( @CalledInAny private fun scheduleRefresh(withDelay: Boolean, @RequiresBackgroundThread callback: Runnable? = null) { - if (withDelay) { + if (!withDelay && callback == null) { refresher.request() + return } - else { - refresher.requestNow() - } + cs.launch { + if (withDelay) delay(REFRESH_DELAY) + refresher.request() refresher.awaitNotBusy() callback?.run() } 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 057bb8296fe3..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,11 +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.Flow -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 @@ -22,49 +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 getPendingTasksFlow(): Flow = requested.combine(busy) { requested, busy -> !requested && !busy } + 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() { @@ -76,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() { - getPendingTasksFlow().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/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/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.kt b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.kt index a6763757ca15..6f64c7997162 100644 --- a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.kt +++ b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.kt @@ -69,7 +69,7 @@ class GitUntrackedFilesHolder internal constructor( get() = untrackedFiles.initialized init { - updateRunner = SingleTaskRunner(cs, 500.milliseconds, ::update) + updateRunner = SingleTaskRunner.delayedTaskRunner(cs, 500.milliseconds, ::update) cs.launch(start = CoroutineStart.UNDISPATCHED) { try { project.serviceAsync().awaitInitialVfsRefreshFinished() From 89bbb17add1abba33ce5a25221697b79912e5572 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Oct 2025 11:20:29 +0200 Subject: [PATCH 101/105] [vcs] IJPL-173924 Fix review notes GitOrigin-RevId: 40d43b32032b574b6e3bb91ecd851c40b21be407 --- platform/vcs-impl/api-dump.txt | 2 +- .../openapi/vcs/changes/ChangesViewUtil.kt | 2 +- .../impl/shared/changes/TreeModelBuilderEx.kt | 3 +- .../vcs/changes/ChangesViewManager.java | 7 +++-- .../changes/ChangesViewWorkflowManagerImpl.kt | 2 +- .../BackendChangesViewModelBuilderService.kt | 6 ++-- .../vcs/changes/BackendChangesView.kt | 22 +++++++-------- .../changes/BackendChangesViewValueIdType.kt | 2 +- .../BackendCommitChangesViewModel.kt | 4 ++- .../BackendLocalCommitChangesViewModel.kt | 6 ++-- .../BackendRemoteCommitChangesViewModel.kt | 1 - .../vcs/commit/ChangesViewCommitPanel.kt | 28 +++++++++---------- 12 files changed, 42 insertions(+), 43 deletions(-) diff --git a/platform/vcs-impl/api-dump.txt b/platform/vcs-impl/api-dump.txt index 825d07ffd87d..13c0e7f122c1 100644 --- a/platform/vcs-impl/api-dump.txt +++ b/platform/vcs-impl/api-dump.txt @@ -770,7 +770,7 @@ com.intellij.openapi.vcs.changes.ChangesViewI 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 diff --git a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt index 70d5ddbbe9a4..60e99deb0cd2 100644 --- a/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt +++ b/platform/vcs-impl/shared/src/com/intellij/openapi/vcs/changes/ChangesViewUtil.kt @@ -32,7 +32,7 @@ object ChangesViewUtil { val skipSingleDefaultChangeList = !ChangeListsViewModel.getInstance(project).areChangeListsEnabled.value val treeModelBuilder = TreeModelBuilder(project, grouping) .setChangeLists(changeLists, skipSingleDefaultChangeList, getChangeDecoratorProvider(project, isAllowExcludeFromCommit)) - .also { TreeModelBuilderEx.getInstanceOrNull(project)?.modifyTreeModelBuilder(it, unversionedFiles) } + .also { TreeModelBuilderEx.getInstanceOrNull(project)?.modifyTreeModelBuilder(it) } .setUnversioned(unversionedFiles) treeModelBuilder.setIgnored(ignoredFiles) diff --git a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt index aae0eefaaaf5..74fa2650592b 100644 --- a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt +++ b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/TreeModelBuilderEx.kt @@ -3,7 +3,6 @@ package com.intellij.platform.vcs.impl.shared.changes import com.intellij.openapi.components.serviceOrNull 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.ui.ChangeListRemoteState import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator @@ -20,7 +19,7 @@ interface TreeModelBuilderEx { */ fun getChangeNodeInChangelistBaseDecorator(listRemoteState: ChangeListRemoteState, change: Change, index: Int): ChangeNodeDecorator? - fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder, unversionedFiles: List) + fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder) companion object { @JvmStatic 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 3d5712be831d..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 @@ -49,6 +49,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.vcs.changes.BackendChangesView; import com.intellij.vcs.commit.*; +import kotlinx.coroutines.CoroutineScope; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -69,6 +70,7 @@ import static com.intellij.util.ui.JBUI.Panels.simplePanel; 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 BackendChangesView myChangesView; @@ -79,7 +81,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable { BackendChangesView initChangesView() { if (myChangesView == null) { Activity activity = StartUpMeasurer.startActivity("ChangesViewPanel initialization"); - myChangesView = BackendChangesView.create(myProject, this); + myChangesView = BackendChangesView.create(myProject, myScope); activity.end(); } return myChangesView; @@ -108,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); 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 8d5176315c8b..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 @@ -42,7 +42,7 @@ internal class ChangesViewWorkflowManagerImpl( // ChangesViewPanel can be reused between workflow instances -> should clean up after ourselves val changesView = (ChangesViewManager.getInstance(project) as ChangesViewManager).initChangesView() val workflow = ChangesViewCommitWorkflow(project) - val commitPanel = ChangesViewCommitPanel(project, 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/ui/BackendChangesViewModelBuilderService.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/BackendChangesViewModelBuilderService.kt index f75613d9c6f9..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 @@ -3,7 +3,6 @@ package com.intellij.openapi.vcs.changes.ui import com.intellij.openapi.project.InitialVfsRefreshService import com.intellij.openapi.project.Project -import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.FileStatus import com.intellij.openapi.vcs.changes.* import com.intellij.openapi.vfs.VirtualFile @@ -24,10 +23,9 @@ internal class BackendChangesViewModelBuilderService(private val project: Projec ): ChangeNodeDecorator = RemoteStatusChangeNodeDecorator(revisionsCache, listRemoteState, index) - override fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder, unversionedFiles: List) { + override fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder) { val changeListManager = ChangeListManagerImpl.getInstanceImpl(project) - - val shouldShowUntrackedLoading = unversionedFiles.isEmpty() && + val shouldShowUntrackedLoading = changeListManager.unversionedFilesPaths.isEmpty() && !project.getService(InitialVfsRefreshService::class.java).isInitialVfsRefreshFinished() && changeListManager.isUnversionedInUpdateMode if (shouldShowUntrackedLoading) { diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt index 2c0731b75acb..1d4261dc700f 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesView.kt @@ -1,19 +1,19 @@ // 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.Disposable import com.intellij.openapi.components.Service -import com.intellij.openapi.components.service import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.vcs.changes.* +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.util.coroutines.childScope 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 kotlinx.coroutines.cancel import javax.swing.JComponent internal class BackendChangesView private constructor( @@ -22,10 +22,7 @@ internal class BackendChangesView private constructor( ) { companion object { @JvmStatic - fun create(project: Project, parentDisposable: Disposable): BackendChangesView { - val scope = project.service().cs.childScope("CommitChangesViewWithToolbarPanel") - Disposer.register(parentDisposable) { scope.cancel() } - + fun create(project: Project, scope: CoroutineScope): BackendChangesView { if (RdLocalChanges.isEnabled()) { val viewModel = BackendRemoteCommitChangesViewModel(project) val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType) @@ -34,8 +31,9 @@ internal class BackendChangesView private constructor( } else { val panel = CommitChangesViewWithToolbarPanel(LocalChangesListView(project), scope) - val backendChangesView = BackendChangesView(panel, BackendLocalCommitChangesViewModel(panel)) - val id = storeValueGlobally(scope, backendChangesView.viewModel, BackendChangesViewValueIdType) + val viewModel = BackendLocalCommitChangesViewModel(panel) + val backendChangesView = BackendChangesView(panel, viewModel) + val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType) panel.id = id return backendChangesView } diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt index 058c0e84ffd7..46c5548adf83 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/BackendChangesViewValueIdType.kt @@ -1,8 +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.BackendCommitChangesViewModel 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 index d21aff6e5480..ff0535956cda 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendCommitChangesViewModel.kt @@ -1,8 +1,10 @@ // 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 +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 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 index 05927bdee37f..c860cf1f89e7 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendLocalCommitChangesViewModel.kt @@ -1,15 +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 +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.util.ui.tree.TreeUtil.* import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler import kotlinx.coroutines.flow.MutableSharedFlow @@ -97,7 +97,7 @@ internal class BackendLocalCommitChangesViewModel(private val panel: CommitChang for (change in changes) { panel.changesView.findNodePathInTree(change)?.let { paths.add(it) } } - TreeUtil.selectPaths(panel.changesView, paths) + selectPaths(panel.changesView, paths) } override fun getTree(): ChangesListView = panel.changesView 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 index 76ea446397c4..283eb59320a2 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/viewModel/BackendRemoteCommitChangesViewModel.kt @@ -4,7 +4,6 @@ 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.BackendCommitChangesViewModel import com.intellij.openapi.vcs.changes.Change import com.intellij.openapi.vcs.changes.InclusionModel import com.intellij.openapi.vcs.changes.LocalChangesListView 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 63e606fdf05c..b8371a5cc490 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitPanel.kt @@ -21,7 +21,7 @@ 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.BackendChangesView +import com.intellij.vcs.changes.viewModel.BackendCommitChangesViewModel import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn @@ -32,7 +32,7 @@ import kotlin.properties.Delegates.observable class ChangesViewCommitPanel internal constructor( project: Project, - private val changesView: BackendChangesView, + private val viewModel: BackendCommitChangesViewModel, ) : NonModalCommitPanel(project), ChangesViewCommitWorkflowUi { private var isHideToolWindowOnCommit = false @@ -54,7 +54,7 @@ class ChangesViewCommitPanel internal constructor( val scope = VcsDisposable.getInstance(project).coroutineScope.childScope("ChangesViewCommitPanel") Disposer.register(this) { scope.cancel() } - changesView.viewModel.inclusionChanged.onEach { writeIntentReadAction { fireInclusionChanged() } }.launchIn(scope) + viewModel.inclusionChanged.onEach { writeIntentReadAction { fireInclusionChanged() } }.launchIn(scope) commitActionsPanel.isCommitButtonDefault = { !progressPanel.isDumbMode && UIUtil.isFocusAncestor(rootComponent ?: component) @@ -79,32 +79,32 @@ class ChangesViewCommitPanel internal constructor( } override fun expand(item: Any) { - changesView.viewModel.expand(item) + viewModel.expand(item) } override fun select(item: Any) { - changesView.viewModel.select(item) + viewModel.select(item) } override fun selectFirst(items: Collection) { - changesView.viewModel.selectFirst(items) + viewModel.selectFirst(items) } override fun setCompletionContext(changeLists: List) { commitMessage.setChangesSupplier(ChangeListChangesSupplier(changeLists)) } - override fun getDisplayedChanges(): List = changesView.viewModel.getDisplayedChanges() - override fun getIncludedChanges(): List = changesView.viewModel.getIncludedChanges() + override fun getDisplayedChanges(): List = viewModel.getDisplayedChanges() + override fun getIncludedChanges(): List = viewModel.getIncludedChanges() override fun getDisplayedUnversionedFiles(): List = - changesView.viewModel.getDisplayedUnversionedFiles() + viewModel.getDisplayedUnversionedFiles() override fun getIncludedUnversionedFiles(): List = - changesView.viewModel.getIncludedUnversionedFiles() + viewModel.getIncludedUnversionedFiles() override fun setInclusionModel(model: InclusionModel?) { - changesView.viewModel.setInclusionModel(model) + viewModel.setInclusionModel(model) } override val commitProgressUi: CommitProgressUi get() = progressPanel @@ -122,7 +122,7 @@ class ChangesViewCommitPanel internal constructor( override fun dispose() { super.dispose() - changesView.viewModel.setShowCheckboxes(false) + viewModel.setShowCheckboxes(false) } override fun activate(): Boolean { @@ -130,7 +130,7 @@ class ChangesViewCommitPanel internal constructor( val contentManager = ChangesViewContentManager.getInstance(project) saveToolWindowState() - changesView.viewModel.setShowCheckboxes(true) + viewModel.setShowCheckboxes(true) component.isVisible = true commitActionsPanel.isActive = true @@ -147,7 +147,7 @@ class ChangesViewCommitPanel internal constructor( } clearToolWindowState() - changesView.viewModel.setShowCheckboxes(false) + viewModel.setShowCheckboxes(false) component.isVisible = false commitActionsPanel.isActive = false From c95b1dc67ed8833bfd704336d25f14fae2652bf3 Mon Sep 17 00:00:00 2001 From: Ilia Kirianovskii Date: Mon, 20 Oct 2025 17:37:49 +0200 Subject: [PATCH 102/105] [build-scripts] Upgrade plugin verifier to v1.396 in PluginVerifierKt GitOrigin-RevId: c1d7d2fa9aaedbd712c05282830bbfa6286fb938 --- .../src/org/jetbrains/intellij/build/PluginVerifier.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/PluginVerifier.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/PluginVerifier.kt index 9d5de8eff767..7b3c664cda3f 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/PluginVerifier.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/PluginVerifier.kt @@ -12,7 +12,7 @@ import kotlin.io.path.pathString import kotlin.io.path.readText -private const val DEFAULT_PLUGIN_VERIFIER_VERSION = "1.381" +private const val DEFAULT_PLUGIN_VERIFIER_VERSION = "1.396" suspend fun createPluginVerifier( pluginVerifierVersion: String = DEFAULT_PLUGIN_VERIFIER_VERSION, From fdf497cce6b67b6244731d78131ff92929932274 Mon Sep 17 00:00:00 2001 From: Vladimir Lagunov Date: Mon, 20 Oct 2025 11:32:21 +0200 Subject: [PATCH 103/105] [devkit][eel] IJPL-212892 UseOptimizedEelFunctions: i18n, cleanup GitOrigin-RevId: 3ce89de234a9bba2d47d057cf2088f34a72e4d07 --- .../devkit-core/resources/intellij.devkit.core.xml | 3 ++- .../resources/messages/DevKitBundle.properties | 4 ++++ .../inspections/eel/UseOptimizedEelFunctions.kt | 14 +++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml index 4d326eaa151f..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"/> Date: Mon, 20 Oct 2025 16:07:25 +0200 Subject: [PATCH 104/105] [run-targets][console, threading] IJPL-172039 Proper modality state for alarm in console in SshTargetIntrospectionStep GitOrigin-RevId: dadc696c5c7268238c4b478fcedeeb757dd05e87 --- .../intellij/execution/filters/TextConsoleBuilderImpl.java | 4 ++-- .../src/com/intellij/execution/impl/ConsoleViewImpl.kt | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) 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 From 589309f4ae56042d009367f51c0d2c5fa85e7d6b Mon Sep 17 00:00:00 2001 From: Vladimir Lagunov Date: Mon, 20 Oct 2025 16:27:03 +0200 Subject: [PATCH 105/105] [eel] IJPL-213853 Minimal environment variables on local Eel must not include login-interactive variables GitOrigin-RevId: 10134ef0c5214b43fc7e6988ba841d91f3e2fc1e --- .../intellij/platform/eel/impl/local/EelLocalExecApi.kt | 8 ++------ platform/util/src/com/intellij/util/EnvironmentUtil.java | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/platform/eel-impl/src/com/intellij/platform/eel/impl/local/EelLocalExecApi.kt b/platform/eel-impl/src/com/intellij/platform/eel/impl/local/EelLocalExecApi.kt index 286897f93dd9..30af75a17168 100644 --- a/platform/eel-impl/src/com/intellij/platform/eel/impl/local/EelLocalExecApi.kt +++ b/platform/eel-impl/src/com/intellij/platform/eel/impl/local/EelLocalExecApi.kt @@ -64,9 +64,7 @@ class EelLocalExecPosixApi( val (cache, interactive) = when (opts.mode) { EelExecPosixApi.PosixEnvironmentVariablesOptions.Mode.MINIMAL -> { - return service().coroutineScope.async { - EnvironmentUtil.getEnvironmentMap() - } + return CompletableDeferred(EnvironmentUtil.getSystemEnv()) } EelExecPosixApi.PosixEnvironmentVariablesOptions.Mode.LOGIN_NON_INTERACTIVE -> { @@ -200,9 +198,7 @@ class EelLocalExecWindowsApi : EelExecWindowsApi, LocalEelExecApi { override val descriptor: EelDescriptor = LocalEelDescriptor override fun environmentVariables(opts: EelExecApi.EnvironmentVariablesOptions): Deferred> = - service().coroutineScope.async { - EnvironmentUtil.getEnvironmentMap() - } + CompletableDeferred(EnvironmentUtil.getEnvironmentMap()) override suspend fun findExeFilesInPath(binaryName: String): List = findExeFilesInPath(binaryName, LOG) 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())); }