mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge roman.golyshev/kt-master-sync-20-10-2025 into master
GitOrigin-RevId: f3e302cf2fbb2cd9261a7f999e2e8277827d5a9d
This commit is contained in:
+5
-5
@@ -1,18 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="kotlinx-serialization-cbor" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-cbor-jvm:1.8.1">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-cbor-jvm:1.9.0">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.8.1/kotlinx-serialization-cbor-jvm-1.8.1.jar">
|
||||
<sha256sum>604c4130ca7a0e979a449cf550087a5c2c586485e8af01dcf24b03b7b287306d</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.9.0/kotlinx-serialization-cbor-jvm-1.9.0.jar">
|
||||
<sha256sum>e45f32f0d80e1164593cfcf9f1907ae39871084c6aa08e49acc2e8e22e1d93ce</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.8.1/kotlinx-serialization-cbor-jvm-1.8.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.9.0/kotlinx-serialization-cbor-jvm-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.8.1/kotlinx-serialization-cbor-jvm-1.8.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-cbor-jvm/1.9.0/kotlinx-serialization-cbor-jvm-1.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+5
-5
@@ -1,18 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="kotlinx-serialization-core" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.8.1">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1.jar">
|
||||
<sha256sum>3565b6d4d789bf70683c45566944287fc1d8dc75c23d98bd87d01059cc76f2b3</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.9.0/kotlinx-serialization-core-jvm-1.9.0.jar">
|
||||
<sha256sum>1f0afa172110e45a7231ef1b44ae8fd84c1ebaff96f3fc3ad68ef8c48120b59c</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.9.0/kotlinx-serialization-core-jvm-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.9.0/kotlinx-serialization-core-jvm-1.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+5
-5
@@ -1,18 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="kotlinx-serialization-json" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.8.1">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.8.1/kotlinx-serialization-json-jvm-1.8.1.jar">
|
||||
<sha256sum>8769e5647557e3700919c32d508f5c5dad53c5d8234cd10846354fbcff14aa24</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.9.0/kotlinx-serialization-json-jvm-1.9.0.jar">
|
||||
<sha256sum>d94cc34cae39246a1af74fda63f9c4812ce12216ef641d5fa3bbbb539a6922d8</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.8.1/kotlinx-serialization-json-jvm-1.8.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.9.0/kotlinx-serialization-json-jvm-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.8.1/kotlinx-serialization-json-jvm-1.8.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.9.0/kotlinx-serialization-json-jvm-1.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+5
-5
@@ -1,18 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="kotlinx-serialization-json-io" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-json-io-jvm:1.8.1">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-json-io-jvm:1.9.0">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.8.1/kotlinx-serialization-json-io-jvm-1.8.1.jar">
|
||||
<sha256sum>ab5ec8ea48815197c46a7df9f418a62791fe91cda5023b941ce4de872d810cc1</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.9.0/kotlinx-serialization-json-io-jvm-1.9.0.jar">
|
||||
<sha256sum>98abb132b03e11b80318d45350ec9ebbb5453d26505e81331bf2eea7143b6fbe</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.8.1/kotlinx-serialization-json-io-jvm-1.8.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.9.0/kotlinx-serialization-json-io-jvm-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.8.1/kotlinx-serialization-json-io-jvm-1.8.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-json-io-jvm/1.9.0/kotlinx-serialization-json-io-jvm-1.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+5
-5
@@ -1,18 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="kotlinx-serialization-protobuf" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-protobuf-jvm:1.8.1">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-serialization-protobuf-jvm:1.9.0">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.8.1/kotlinx-serialization-protobuf-jvm-1.8.1.jar">
|
||||
<sha256sum>fbd174f72ea82364baa112069f2d7b361758f6b68bd81d2ac51b02c66a48e924</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.9.0/kotlinx-serialization-protobuf-jvm-1.9.0.jar">
|
||||
<sha256sum>555035205ad1a21254b49fd6b5ddbe6ad1abb5c7baba244258bd758f80687bca</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.8.1/kotlinx-serialization-protobuf-jvm-1.8.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.9.0/kotlinx-serialization-protobuf-jvm-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.8.1/kotlinx-serialization-protobuf-jvm-1.8.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-protobuf-jvm/1.9.0/kotlinx-serialization-protobuf-jvm-1.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+2
-6
@@ -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<NodeSource> myBaseSources;
|
||||
private final Set<NodeSource> myDeletedSources;
|
||||
|
||||
public DeltaImpl(Iterable<NodeSource> baseSources, Iterable<NodeSource> 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<NodeSource> baseSources, Iterable<NodeSource> deletedSources, IndexFactory indexFactory) {
|
||||
super(Containers.MEMORY_CONTAINER_FACTORY, indexFactory);
|
||||
myBaseSources = Collections.unmodifiableSet(baseSources instanceof Set? (Set<? extends NodeSource>)baseSources : Iterators.collect(baseSources, new HashSet<>()));
|
||||
myDeletedSources = Collections.unmodifiableSet(deletedSources instanceof Set? (Set<? extends NodeSource>)deletedSources : Iterators.collect(deletedSources, new HashSet<>()));
|
||||
}
|
||||
|
||||
+11
-20
@@ -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<DifferentiateStrategy> ourDifferentiateStrategies = List.of(new KotlinSourceOnlyDifferentiateStrategy(), new GeneralJvmDifferentiateStrategy());
|
||||
private final Set<String> 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<NodeSource> compiledSources, Iterable<NodeSource> deletedSources, boolean isSourceOnly) {
|
||||
Delta delta = isSourceOnly? new SourceOnlyDelta(myRegisteredIndices, compiledSources, deletedSources) : new DeltaImpl(compiledSources, deletedSources);
|
||||
|
||||
Set<String> 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
|
||||
|
||||
+57
-6
@@ -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<ReferenceID, NodeSource> myNodeToSourcesMap;
|
||||
protected final MultiMaplet<NodeSource, Node<?, ?>> mySourceToNodesMap;
|
||||
private final MapletFactory myContainerFactory;
|
||||
private final IndexFactory myIndexFactory;
|
||||
private static final Set<String> ourMandatoryIndexNames = Set.of(
|
||||
NodeDependenciesIndex.NAME, SubclassesIndex.NAME, TypealiasesIndex.NAME
|
||||
);
|
||||
|
||||
protected GraphImpl(@NotNull MapletFactory cFactory) {
|
||||
public interface IndexFactory {
|
||||
Iterable<BackDependencyIndex> 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<MapletFactory, BackDependencyIndex>... 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<String> 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<NodeSource> 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
|
||||
|
||||
+35
@@ -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<ReferenceID> 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
+3
@@ -36,6 +36,9 @@ public interface BuildContext extends DiagnosticSink {
|
||||
@Nullable
|
||||
Path getAbiOutputZip();
|
||||
|
||||
@Nullable
|
||||
Path getKotlinCriStoragePath();
|
||||
|
||||
NodeSourceSnapshot getSources();
|
||||
|
||||
NodeSourceSnapshot getBinaryDependencies();
|
||||
|
||||
+1
@@ -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";
|
||||
|
||||
|
||||
|
||||
+51
-9
@@ -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;
|
||||
@@ -15,6 +13,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 +33,7 @@ public class StorageManager implements CloseableExt {
|
||||
private CompositeZipOutputBuilder myComposite;
|
||||
private InstrumentationClassFinder myInstrumentationClassFinder;
|
||||
private FormBinding myFormBinding;
|
||||
private boolean isKotlinCriDataGenerationEnabled;
|
||||
|
||||
private final MVStore myDataSwapStore;
|
||||
|
||||
@@ -45,6 +45,7 @@ public class StorageManager implements CloseableExt {
|
||||
.cacheSize(8)
|
||||
.open();
|
||||
myDataSwapStore.setVersionsToKeep(0);
|
||||
isKotlinCriDataGenerationEnabled = myContext.getKotlinCriStoragePath() != null;
|
||||
}
|
||||
|
||||
public void cleanBuildState() throws IOException {
|
||||
@@ -106,14 +107,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
|
||||
@@ -190,6 +206,7 @@ public class StorageManager implements CloseableExt {
|
||||
GraphConfiguration config = myGraphConfig;
|
||||
if (config != null) {
|
||||
myGraphConfig = null;
|
||||
writeKotlinCriData(config.getGraph(), saveChanges);
|
||||
safeClose(config.getGraph(), saveChanges);
|
||||
}
|
||||
|
||||
@@ -208,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) {
|
||||
|
||||
+8
@@ -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;
|
||||
|
||||
+100
@@ -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<String, ByteArray> {
|
||||
val serializedDataResult = mutableMapOf<String, ByteArray>()
|
||||
|
||||
// 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<String, Int>()
|
||||
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<String, Int>): Int {
|
||||
return fileIdToPathEntryAccumulator.computeIfAbsent(this) { fileIdToPathEntryAccumulator.size + 1 }
|
||||
}
|
||||
|
||||
private fun DataOutputStream.writeVersion() = writeInt(VERSION)
|
||||
+2
-1
@@ -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<NodeSourceSnapshot, Graph> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
out/
|
||||
build/
|
||||
.idea/
|
||||
.gradle/
|
||||
@@ -108,6 +108,7 @@
|
||||
<keymapExtension implementation="com.intellij.debugger.actions.DebuggerKeymapExtension"/>
|
||||
|
||||
<xdebugger.settings implementation="com.intellij.debugger.settings.JavaDebuggerSettings"/>
|
||||
<registryKey key="execution.java.always.debug" defaultValue="false" description="Always run java processes with the debug agent"/>
|
||||
|
||||
<xdebugger.breakpointGroupingRule implementation="com.intellij.debugger.ui.XBreakpointGroupingByPackageRule"/>
|
||||
<xdebugger.breakpointGroupingRule implementation="com.intellij.debugger.ui.XBreakpointGroupingByClassRule"/>
|
||||
@@ -219,6 +220,8 @@
|
||||
description="The timeout between the hit run-to-cursor breakpoint in another thread/coroutine and pause the debugger"/>
|
||||
<registryKey key="debugger.run.to.cursor.waited.threads.limit" defaultValue="1"
|
||||
description="How many of threads allow to wait on the hit run-to-cursor breakpoint for the desired hit"/>"/>
|
||||
<registryKey key="debugger.log.jdi.in.unit.tests" defaultValue="false"
|
||||
description="Enable full JDI logging under DebugProcessImpl.LOG.debug for unit tests"/>
|
||||
</extensions>
|
||||
|
||||
<actions>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -365,5 +365,8 @@
|
||||
<referenceInjector implementation="com.intellij.microservices.jvm.url.HttpUrlReferenceInjector"/>
|
||||
<referenceInjector implementation="com.intellij.microservices.jvm.url.WSUrlReferenceInjector"/>
|
||||
<referenceInjector implementation="com.intellij.microservices.jvm.url.HttpMethodReferenceInjector"/>
|
||||
|
||||
<registryKey key="batch.inspections.ignored.jvm.languages" defaultValue="Groovy,Scala"
|
||||
description="Disable UAST processing for JVM languages; a comma separated list of UAST capable language ids."/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
+9
-37
@@ -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<PsiElement, DataflowResult> fileMap =
|
||||
|
||||
ConcurrentMap<PsiElement, DataflowResult> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -678,10 +678,6 @@
|
||||
implementationClass="com.intellij.codeInsight.hint.NextParameterAfterCompletionHandler"/>
|
||||
<compiler.updateResourcesBuildContributor implementation="com.intellij.compiler.impl.JavaResourcesBuildContributor"/>
|
||||
|
||||
<!--<expectedTypesProvider implementation="com.intellij.codeInsight.JavaExpectedTypesProvider"/>-->
|
||||
|
||||
|
||||
|
||||
<projectService serviceImplementation="com.intellij.execution.testDiscovery.JavaAutoRunManager"/>
|
||||
<registryKey defaultValue="true" description="Trigger autotest on delay instead of build events" key="trigger.autotest.on.delay"/>
|
||||
|
||||
@@ -811,6 +807,10 @@
|
||||
<applicationService serviceInterface="com.intellij.execution.vmOptions.VMOptionsService"
|
||||
serviceImplementation="com.intellij.execution.vmOptions.VMOptionsServiceImpl"/>
|
||||
|
||||
<registryKey key="ide.jvm.run.marker" defaultValue="false" description="Enable JVM-lang-based run line markers"/>
|
||||
<registryKey key="ide.jvm.command.line.clear.all.environments" defaultValue="true"
|
||||
description="Clear all environments when running a JVM command line application"/>
|
||||
|
||||
<runDashboardCustomizer implementation="com.intellij.execution.CommonJavaRunDashboardCustomizer" id="commonJavaCustomizer"/>
|
||||
|
||||
<warmupConfigurator implementation="com.intellij.ide.warmup.JdkWarmupConfigurator"/>
|
||||
@@ -2785,12 +2785,19 @@
|
||||
<registryKey key="java.completion.methods.use.tags"
|
||||
defaultValue="true"
|
||||
description="Use additional tags for methods for completion"/>
|
||||
<registryKey key="ide.java.completion.suggest.static.after.instance"
|
||||
defaultValue="false"
|
||||
description="Suggest static methods/fields after instance qualifier in Java completion"/>
|
||||
|
||||
<registryKey key="java.create.compact.source.file.separately"
|
||||
defaultValue="true"
|
||||
description="Show 'Create java compact file' separately from all other classes"/>
|
||||
|
||||
<virtualFileCustomDataProvider implementation="com.intellij.ide.JavaCodeStyleSettingsCustomDataSynchronizer"/>
|
||||
|
||||
<registryKey key="compiler.external.javac.keep.alive.timeout" defaultValue="300000"
|
||||
description="If not used for the specified period of time or longer, the IDE will shut down the cached javac compilation process"/>
|
||||
<registryKey key="compiler.build.can.use.eel" defaultValue="true" description="Allow using Eel API to run build processes"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -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<List<Ou
|
||||
myContext.sendDescriptionStatusMessage(JpsBuildBundle.message("progress.text.extracting.downloaded.results"));
|
||||
List<Future<?>> 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;
|
||||
|
||||
+2
-1
@@ -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<Future<?>> futures = ContainerUtil.map(libs, lib -> executorService.submit(() -> resolveAction.accept(lib)));
|
||||
for (Future<?> future : futures) future.get();
|
||||
ConcurrencyUtil.getAll(futures);
|
||||
}
|
||||
finally {
|
||||
executorService.shutdown();
|
||||
|
||||
+10
-10
@@ -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"]
|
||||
)
|
||||
|
||||
|
||||
+54
-40
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,63 @@ 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<String> = 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<String>, 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) ->
|
||||
val langRootDir = resourceRoot.resolve(descriptor.resPath)
|
||||
if (langRootDir.exists()) {
|
||||
Span.current().addEvent("adding \"${lang}\" to the resulting ZIP.")
|
||||
descriptor.resList.forEach { resDir ->
|
||||
archiveDir(
|
||||
startDir = langRootDir.resolve(resDir),
|
||||
addFile = { archiver.addFile(it, zipCreator) })
|
||||
}
|
||||
}
|
||||
else
|
||||
Span.current().addEvent("skip adding \"${lang}\" to the resulting ZIP because it was not supplied.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
@@ -348,11 +357,15 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin
|
||||
}
|
||||
|
||||
private var cachedVisibleChildren: MutableList<MyNode>? = null
|
||||
private var cachedIndex = -1
|
||||
private var cachedFilteringState: BuildTreeFilteringState? = null
|
||||
|
||||
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
|
||||
@@ -363,7 +376,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 +407,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 +429,7 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin
|
||||
|
||||
private fun clearCache() {
|
||||
cachedVisibleChildren = null
|
||||
cachedFilteringState = null
|
||||
}
|
||||
|
||||
private fun clearCacheIfInvalid() {
|
||||
@@ -431,17 +447,26 @@ internal class BuildTreeView(private val project: Project, parentScope: Coroutin
|
||||
}
|
||||
|
||||
fun getVisibleChildren(): List<MyNode> {
|
||||
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<MyNode> {
|
||||
cachedFilteringState = filteringState
|
||||
val result = mutableListOf<MyNode>()
|
||||
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 +486,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<TreeNode> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<? extends @NotNull VFileEvent> 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
|
||||
|
||||
@@ -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<SimpleDiffChange> 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) {
|
||||
|
||||
@@ -64,9 +64,7 @@ class EelLocalExecPosixApi(
|
||||
|
||||
val (cache, interactive) = when (opts.mode) {
|
||||
EelExecPosixApi.PosixEnvironmentVariablesOptions.Mode.MINIMAL -> {
|
||||
return service<CoroutineScopeService>().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<Map<String, String>> =
|
||||
service<CoroutineScopeService>().coroutineScope.async {
|
||||
EnvironmentUtil.getEnvironmentMap()
|
||||
}
|
||||
CompletableDeferred(EnvironmentUtil.getEnvironmentMap())
|
||||
|
||||
override suspend fun findExeFilesInPath(binaryName: String): List<EelPath> =
|
||||
findExeFilesInPath(binaryName, LOG)
|
||||
|
||||
@@ -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<ServiceStatusDto> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
- <init>(java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,java.lang.String):V
|
||||
- b:<init>(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
|
||||
|
||||
@@ -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
|
||||
- <init>(java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,java.lang.String,Z):V
|
||||
- b:<init>(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
|
||||
|
||||
+3
-1
@@ -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<UnnamedConfigurable>(
|
||||
@ApiStatus.Internal
|
||||
class EditorAppearanceConfigurable : BoundCompositeSearchableConfigurable<UnnamedConfigurable>(
|
||||
ApplicationBundle.message("tab.editor.settings.appearance"),
|
||||
"reference.settingsdialog.IDE.editor.appearance",
|
||||
"editor.preferences.appearance"
|
||||
|
||||
+3
-1
@@ -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;
|
||||
* <p>
|
||||
* A new instance of the specified class will be created each time then the Settings dialog is opened
|
||||
*/
|
||||
final class EditorAppearanceConfigurableEP extends ConfigurableEP<UnnamedConfigurable> {
|
||||
@ApiStatus.Internal
|
||||
public final class EditorAppearanceConfigurableEP extends ConfigurableEP<UnnamedConfigurable> {
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.codeVision.ui.model
|
||||
|
||||
import com.intellij.codeInsight.codeVision.CodeVisionEntry
|
||||
import com.intellij.lang.LangBundle
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
class ZeroWidthCodeVisionEntry(
|
||||
providerId: String
|
||||
) : CodeVisionEntry(
|
||||
providerId,
|
||||
icon = null,
|
||||
longPresentation = LangBundle.message("show.a.code.vision.inlay.inlay.when.no.results.are.available"),
|
||||
tooltip = "",
|
||||
extraActions = emptyList()
|
||||
) {
|
||||
init {
|
||||
showInMorePopup = false
|
||||
}
|
||||
}
|
||||
+2
@@ -3,6 +3,7 @@ package com.intellij.codeInsight.codeVision.ui.model
|
||||
|
||||
import com.intellij.codeInsight.codeVision.CodeVisionEntry
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.Nls
|
||||
import javax.swing.Icon
|
||||
|
||||
@@ -13,6 +14,7 @@ class ZombieCodeVisionEntry(
|
||||
icon: Icon?,
|
||||
val count: Int?,
|
||||
val text: String = longPresentation,
|
||||
val shouldBeDelimited: Boolean = true
|
||||
) : CodeVisionEntry(providerId, icon, longPresentation, tooltip, emptyList()) {
|
||||
|
||||
override fun toString() = "ZombieCodeVisionEntry('$text')"
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,5 +26,5 @@ interface ICodeVisionEntryBasePainter<T> : ICodeVisionPainter {
|
||||
): Dimension
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun shouldBeDelimited(): Boolean = true
|
||||
fun shouldBeDelimited(entry: T): Boolean = true
|
||||
}
|
||||
+36
-1
@@ -4,15 +4,22 @@ package com.intellij.codeInsight.codeVision.ui.renderers.providers
|
||||
import com.intellij.codeInsight.codeVision.CodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.AdditionalCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.CounterCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.RangeCodeVisionModel
|
||||
import com.intellij.codeInsight.codeVision.ui.model.RichTextCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.TextCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.ZeroWidthCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.renderers.painters.CodeVisionRichTextPainter
|
||||
import com.intellij.codeInsight.codeVision.ui.renderers.painters.CodeVisionVisionTextPainter
|
||||
import com.intellij.codeInsight.codeVision.ui.renderers.painters.DefaultCodeVisionPainter
|
||||
import com.intellij.codeInsight.codeVision.ui.renderers.painters.ICodeVisionEntryBasePainter
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.util.ClassExtension
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import java.awt.Dimension
|
||||
import java.awt.Graphics
|
||||
import java.awt.Point
|
||||
|
||||
private val INSTANCE = CodeVisionPainterProviders()
|
||||
|
||||
@@ -50,7 +57,35 @@ private class ZombieCodeVisionEntryPainter : DefaultCodeVisionPainter<ZombieCode
|
||||
val debug = Registry.`is`("cache.markup.debug")
|
||||
if (debug) "${result}?" else result
|
||||
})
|
||||
)
|
||||
) {
|
||||
override fun shouldBeDelimited(entry: ZombieCodeVisionEntry): Boolean {
|
||||
return entry.shouldBeDelimited
|
||||
}
|
||||
}
|
||||
|
||||
private class ZeroWidthCodeVisionEntryPainter : ICodeVisionEntryBasePainter<ZeroWidthCodeVisionEntry> {
|
||||
|
||||
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<CodeVisionEntry> {
|
||||
return INSTANCE.getPainter(this@painter)
|
||||
|
||||
@@ -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<Language, FileHighlightingSetting>(languages.size)
|
||||
val settingMap = LinkedHashMap<Language, FileHighlightingSetting>(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 {
|
||||
|
||||
+7
-2
@@ -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, CodeVi
|
||||
writeUTF(grave, limb.providerId)
|
||||
writeGutterIcon(grave, limb.icon)
|
||||
writeCount(grave, limb)
|
||||
if (limb.shouldBeDelimited) writeINT(grave, 1) else writeINT(grave, 0)
|
||||
}
|
||||
|
||||
override fun exhumeLimb(grave: DataInput): CodeVisionLimb {
|
||||
@@ -45,7 +47,8 @@ internal object CodeVisionNecromancy : LimbedNecromancy<CodeVisionZombie, CodeVi
|
||||
val provId: String = readUTF(grave)
|
||||
val icon: Icon? = readGutterIcon(grave)
|
||||
val count: Int? = readCount(grave)
|
||||
return CodeVisionLimb(startOffset, endOffset, provId, presentation, tooltip, icon, count)
|
||||
val shouldBeDelimited = readINT(grave) == 1
|
||||
return CodeVisionLimb(startOffset, endOffset, provId, presentation, tooltip, icon, count, shouldBeDelimited)
|
||||
}
|
||||
|
||||
override fun formZombie(limbs: List<CodeVisionLimb>): CodeVisionZombie {
|
||||
@@ -74,6 +77,7 @@ internal data class CodeVisionLimb(
|
||||
val tooltip: String,
|
||||
val icon: Icon?,
|
||||
val count: Int?,
|
||||
val shouldBeDelimited: Boolean
|
||||
) {
|
||||
constructor(pair: Pair<TextRange, CodeVisionEntry>) : 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+120
@@ -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<ScreenReader>("name", "Name of the screen reader tool"),
|
||||
"Screen reader is running in the system")
|
||||
private val SCREEN_MAGNIFIER = GROUP.registerEvent(
|
||||
"screen.magnifier",
|
||||
EventFields.Enum<ScreenMagnifier>("name", "Name of the screen magnifier tool"),
|
||||
"Screen magnifier is running in the system")
|
||||
private val VOICE_CONTROL = GROUP.registerEvent(
|
||||
"voice.control",
|
||||
EventFields.Enum<VoiceControl>("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<MetricEvent> {
|
||||
val set = mutableSetOf<MetricEvent>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -259,6 +259,12 @@ public final class PluginManagerConfigurable
|
||||
}
|
||||
}
|
||||
|
||||
public void scheduleApply() {
|
||||
if (myPanel != null) {
|
||||
myPanel.scheduleApply();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (myPanel != null) {
|
||||
|
||||
+15
@@ -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();
|
||||
|
||||
+13
-21
@@ -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<List<IdeCompatibleUpdate>>() {})
|
||||
}.firstOrNull()
|
||||
?.pluginId
|
||||
@@ -966,23 +975,6 @@ private data class CompatibleUpdateRequest(
|
||||
)
|
||||
}
|
||||
|
||||
private data class CompatibleUpdateForModuleRequest(
|
||||
val module: String,
|
||||
val build: String,
|
||||
val os: String = OS.CURRENT.name,
|
||||
val arch: String = CpuArch.CURRENT.name,
|
||||
) {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
module: String,
|
||||
buildNumber: BuildNumber? = null,
|
||||
) : this(
|
||||
module,
|
||||
ApplicationInfoImpl.orFromPluginCompatibleBuild(buildNumber),
|
||||
)
|
||||
}
|
||||
|
||||
private fun Logger.infoOrDebug(
|
||||
message: String,
|
||||
throwable: Throwable,
|
||||
|
||||
-3
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+1
-3
@@ -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)
|
||||
}
|
||||
|
||||
+1
-2
@@ -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
|
||||
|
||||
|
||||
@@ -1136,7 +1136,7 @@ fun CoroutineScope.runInitProjectActivities(project: Project) {
|
||||
(project.serviceAsync<StartupManager>() 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)
|
||||
|
||||
+7
-1
@@ -414,9 +414,14 @@ class InfoAndProgressPanel internal constructor(
|
||||
): BalloonHandler {
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
val htmlContent = htmlBody.replace("\n", "<br>")
|
||||
val icon = icon ?: if (type == MessageType.INFO) {
|
||||
null
|
||||
} else {
|
||||
type.defaultIcon
|
||||
}
|
||||
val balloon = JBPopupFactory.getInstance()
|
||||
.createHtmlTextBalloonBuilder(htmlContent,
|
||||
null,
|
||||
icon,
|
||||
type.titleForeground,
|
||||
type.popupBackground,
|
||||
listener)
|
||||
@@ -1252,6 +1257,7 @@ private class CounterLabel : JPanel() {
|
||||
label = createLabel()
|
||||
layout = BorderLayout()
|
||||
add(label, BorderLayout.CENTER)
|
||||
isOpaque = false
|
||||
setNumber(0, isProgressVisible =false, isPopupShowing = false)
|
||||
}
|
||||
|
||||
|
||||
+1
-10
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -7,28 +7,64 @@ import com.jetbrains.JBR;
|
||||
import org.intellij.lang.annotations.JdkConstants;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import static java.awt.Cursor.*;
|
||||
import static com.intellij.ui.WindowMouseListenerSupportKt.createWindowMouseListenerSupport;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public abstract class WindowMouseListener extends MouseAdapter implements MouseInputListener {
|
||||
private static final boolean moveAfterMouseRelease = StartupUiUtil.isWaylandToolkit();
|
||||
protected final Component myContent;
|
||||
@JdkConstants.CursorType int myCursorType;
|
||||
private Point myLocation;
|
||||
private Rectangle viewBounds;
|
||||
private int mouseButton;
|
||||
private boolean wasDragged;
|
||||
private boolean leftMouseButtonOnly = false;
|
||||
protected final WindowMouseListenerSupport support = createWindowMouseListenerSupport(new SourceAdapter());
|
||||
|
||||
private class SourceAdapter implements WindowMouseListenerSource {
|
||||
@Override
|
||||
public @NotNull Component getContent(@NotNull MouseEvent event) {
|
||||
return WindowMouseListener.this.getContent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component getView(@NotNull Component event) {
|
||||
return WindowMouseListener.this.getView(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled(@NotNull Component view) {
|
||||
return WindowMouseListener.this.isDisabled(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCursorType(@Nullable Component view, @Nullable Point location) {
|
||||
return WindowMouseListener.this.getCursorType(view, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursor(@NotNull Component content, @Nullable Cursor cursor) {
|
||||
WindowMouseListener.this.setCursor(content, cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBounds(@NotNull Rectangle bounds, @NotNull Component view, int dx, int dy) {
|
||||
WindowMouseListener.this.updateBounds(bounds, view, dx, dy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyMoved() {
|
||||
WindowMouseListener.this.notifyMoved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyResized() {
|
||||
WindowMouseListener.this.notifyResized();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param content the window content to find a window, or {@code null} to use a component from a mouse event eeeqweq3r
|
||||
* @param content the window content to find a window, or {@code null} to use a component from a mouse event
|
||||
*/
|
||||
WindowMouseListener(Component content) {
|
||||
myContent = content;
|
||||
@@ -42,6 +78,11 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
|
||||
@JdkConstants.CursorType
|
||||
abstract int getCursorType(Component view, Point location);
|
||||
|
||||
@JdkConstants.CursorType
|
||||
int getCursorType() {
|
||||
return support.getCursorType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bounds the component bounds, which should be updated
|
||||
* @param view the component to move/resize
|
||||
@@ -51,32 +92,32 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
|
||||
abstract void updateBounds(Rectangle bounds, Component view, int dx, int dy);
|
||||
|
||||
public void setLeftMouseButtonOnly(boolean leftMouseButtonOnly) {
|
||||
this.leftMouseButtonOnly = leftMouseButtonOnly;
|
||||
support.setLeftMouseButtonOnly(leftMouseButtonOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent event) {
|
||||
update(event, false);
|
||||
support.update(event, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent event) {
|
||||
update(event, true);
|
||||
support.update(event, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent event) {
|
||||
process(event, true);
|
||||
support.process(event, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent event) {
|
||||
process(event, false);
|
||||
support.process(event, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent event) {
|
||||
process(event, false);
|
||||
support.process(event, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,88 +133,6 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a cursor and starts moving/resizing if the {@code start} is specified.
|
||||
*/
|
||||
private void update(MouseEvent event, boolean start) {
|
||||
if (event.isConsumed() || (start && leftMouseButtonOnly && !SwingUtilities.isLeftMouseButton(event))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (start) wasDragged = false; // reset dragged state when mouse pressed
|
||||
if (myLocation == null) {
|
||||
Component content = getContent(event);
|
||||
Component view = getView(content);
|
||||
if (view != null) {
|
||||
myCursorType = isDisabled(view) ? CUSTOM_CURSOR : getCursorType(view, event.getLocationOnScreen());
|
||||
setCursor(content, getPredefinedCursor(myCursorType == CUSTOM_CURSOR ? DEFAULT_CURSOR : myCursorType));
|
||||
if (start && myCursorType != CUSTOM_CURSOR) {
|
||||
mouseButton = event.getButton();
|
||||
myLocation = event.getLocationOnScreen();
|
||||
viewBounds = view.getBounds();
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes moving/resizing and stops it if not {@code mouseMove}.
|
||||
*/
|
||||
private void process(MouseEvent event, boolean mouseMove) {
|
||||
if (event.isConsumed()) return;
|
||||
if (mouseMove) wasDragged = true; // set dragged state when mouse dragged
|
||||
if (myLocation != null && viewBounds != null) {
|
||||
Component content = getContent(event);
|
||||
Component view = getView(content);
|
||||
if (mouseMove && myCursorType == DEFAULT_CURSOR && jbrMoveSupported(view)) {
|
||||
// Enter in move mode only after mouse move, so double click is supported
|
||||
JBR.getWindowMove().startMovingTogetherWithMouse((Window)view, mouseButton);
|
||||
myLocation = null;
|
||||
viewBounds = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
Rectangle bounds = new Rectangle(viewBounds);
|
||||
int dx = event.getXOnScreen() - myLocation.x;
|
||||
int dy = event.getYOnScreen() - myLocation.y;
|
||||
if (myCursorType == DEFAULT_CURSOR && view instanceof Frame) {
|
||||
int state = ((Frame)view).getExtendedState();
|
||||
if (isStateSet(Frame.MAXIMIZED_HORIZ, state)) dx = 0;
|
||||
if (isStateSet(Frame.MAXIMIZED_VERT, state)) dy = 0;
|
||||
}
|
||||
updateBounds(bounds, view, dx, dy);
|
||||
Rectangle viewBounds = view.getBounds();
|
||||
if (!bounds.equals(viewBounds)) {
|
||||
boolean moved = bounds.x != viewBounds.x || bounds.y != viewBounds.y;
|
||||
boolean resized = bounds.width != viewBounds.width || bounds.height != viewBounds.height;
|
||||
boolean reallyMoveWindow = !moveAfterMouseRelease || !mouseMove;
|
||||
if ((moved && reallyMoveWindow) || resized) {
|
||||
view.reshape(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
view.invalidate();
|
||||
view.validate();
|
||||
view.repaint();
|
||||
if (moved) notifyMoved();
|
||||
if (resized) notifyResized();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mouseMove) {
|
||||
setCursor(content, getPredefinedCursor(DEFAULT_CURSOR));
|
||||
myLocation = null;
|
||||
if (wasDragged) {
|
||||
viewBounds = null; // no mouse clicked when mouse released after mouse dragged
|
||||
}
|
||||
}
|
||||
event.consume();
|
||||
}
|
||||
else if (!mouseMove && viewBounds != null) {
|
||||
viewBounds = null; // consume mouse clicked for consumed mouse released if no mouse dragged
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a window content which is used to find corresponding window and to set a cursor.
|
||||
* By default, it returns a component from the specified mouse event if the content is not specified.
|
||||
@@ -206,7 +165,7 @@ public abstract class WindowMouseListener extends MouseAdapter implements MouseI
|
||||
* Returns {@code true} if a window is now moving/resizing.
|
||||
*/
|
||||
public boolean isBusy() {
|
||||
return myLocation != null;
|
||||
return support.isBusy();
|
||||
}
|
||||
|
||||
protected void notifyMoved() { }
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ui
|
||||
|
||||
import com.intellij.util.ui.StartupUiUtil
|
||||
import com.jetbrains.JBR
|
||||
import org.intellij.lang.annotations.JdkConstants
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal interface WindowMouseListenerSource {
|
||||
fun getContent(event: MouseEvent): Component
|
||||
|
||||
fun getView(event: Component): Component?
|
||||
|
||||
fun isDisabled(view: Component): Boolean
|
||||
|
||||
@JdkConstants.CursorType
|
||||
fun getCursorType(view: Component?, location: Point?): Int
|
||||
|
||||
fun setCursor(content: Component, cursor: Cursor?)
|
||||
|
||||
fun updateBounds(bounds: Rectangle, view: Component, dx: Int, dy: Int)
|
||||
|
||||
fun notifyMoved()
|
||||
|
||||
fun notifyResized()
|
||||
}
|
||||
|
||||
internal fun createWindowMouseListenerSupport(source: WindowMouseListenerSource): WindowMouseListenerSupport {
|
||||
return if (StartupUiUtil.isWaylandToolkit()) {
|
||||
WaylandWindowMouseListenerSupport(source)
|
||||
}
|
||||
else {
|
||||
RegularWindowMouseListenerSupport(source)
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class WindowMouseListenerSupport(private val source: WindowMouseListenerSource) {
|
||||
@get:JdkConstants.CursorType
|
||||
var cursorType: Int = 0
|
||||
private set
|
||||
private var currentSession: WindowListenerSession? = null
|
||||
|
||||
var leftMouseButtonOnly = false
|
||||
|
||||
val isBusy: Boolean
|
||||
get() = currentSession != null
|
||||
|
||||
protected abstract fun moveAfterMouseRelease(): Boolean
|
||||
|
||||
protected abstract fun jbrMoveSupported(component: Component?): Boolean
|
||||
|
||||
/**
|
||||
* Updates a cursor and starts moving/resizing if the `start` is specified.
|
||||
*/
|
||||
fun update(event: MouseEvent, start: Boolean) {
|
||||
if (event.isConsumed || (start && leftMouseButtonOnly && !SwingUtilities.isLeftMouseButton(event))) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isBusy) {
|
||||
val content = source.getContent(event)
|
||||
val view = source.getView(content)
|
||||
if (view != null) {
|
||||
beforeUpdate(event, view)
|
||||
updateCursor(event, content, view)
|
||||
if (start && cursorType != Cursor.CUSTOM_CURSOR) {
|
||||
start(event, view)
|
||||
event.consume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun beforeUpdate(event: MouseEvent, view: Component) { }
|
||||
|
||||
private fun updateCursor(event: MouseEvent, content: Component, view: Component) {
|
||||
cursorType = if (source.isDisabled(view)) Cursor.CUSTOM_CURSOR else source.getCursorType(view, event.locationOnScreen)
|
||||
source.setCursor(content, Cursor.getPredefinedCursor(if (cursorType == Cursor.CUSTOM_CURSOR) Cursor.DEFAULT_CURSOR else cursorType))
|
||||
}
|
||||
|
||||
private fun start(event: MouseEvent, view: Component) {
|
||||
currentSession = WindowListenerSession(
|
||||
event.locationOnScreen,
|
||||
view.bounds,
|
||||
event.getButton(),
|
||||
)
|
||||
onStarted(event, view)
|
||||
}
|
||||
|
||||
protected open fun onStarted(event: MouseEvent, view: Component) { }
|
||||
|
||||
/**
|
||||
* Processes moving/resizing and stops it if not `mouseMove`.
|
||||
*/
|
||||
fun process(event: MouseEvent, mouseMove: Boolean) {
|
||||
// Some points:
|
||||
// 1. Stop events should be handled even if they're already consumed, so the session finishes correctly.
|
||||
// 2. Non-drag unconsumed events should be handled as drag events anyway to support the "move on mouse release" mode.
|
||||
if (!event.isConsumed) {
|
||||
handleDragEvent(event, mouseMove)
|
||||
}
|
||||
if (!mouseMove) {
|
||||
handleStopEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
@JdkConstants.CursorType
|
||||
open fun getResizeCursor(top: Int, left: Int, bottom: Int, right: Int, resizeArea: Insets): Int {
|
||||
// Wayland doesn't allow to change window's location programmatically,
|
||||
// so resizing from top/left shall be forbidden for now.
|
||||
if (top < resizeArea.top) {
|
||||
if (left < resizeArea.left * 2) return Cursor.NW_RESIZE_CURSOR
|
||||
if (right < resizeArea.right * 2) return Cursor.NE_RESIZE_CURSOR
|
||||
return Cursor.N_RESIZE_CURSOR
|
||||
}
|
||||
if (bottom < resizeArea.bottom) {
|
||||
if (left < resizeArea.left * 2) return Cursor.SW_RESIZE_CURSOR
|
||||
if (right < resizeArea.right * 2) return Cursor.SE_RESIZE_CURSOR
|
||||
return Cursor.S_RESIZE_CURSOR
|
||||
}
|
||||
if (left < resizeArea.left) {
|
||||
if (top < resizeArea.top * 2) return Cursor.NW_RESIZE_CURSOR
|
||||
if (bottom < resizeArea.bottom * 2) return Cursor.SW_RESIZE_CURSOR
|
||||
return Cursor.W_RESIZE_CURSOR
|
||||
}
|
||||
if (right < resizeArea.right) {
|
||||
if (top < resizeArea.top * 2) return Cursor.NE_RESIZE_CURSOR
|
||||
if (bottom < resizeArea.bottom * 2) return Cursor.SE_RESIZE_CURSOR
|
||||
return Cursor.E_RESIZE_CURSOR
|
||||
}
|
||||
return Cursor.CUSTOM_CURSOR
|
||||
}
|
||||
|
||||
private fun handleDragEvent(event: MouseEvent, mouseMove: Boolean) {
|
||||
val session = currentSession ?: return
|
||||
if (mouseMove) {
|
||||
onDraggingStarted(session)
|
||||
}
|
||||
|
||||
val content = source.getContent(event)
|
||||
val view = source.getView(content)
|
||||
// Enter in move mode only after mouse move, so double click is supported
|
||||
if (mouseMove && cursorType == Cursor.DEFAULT_CURSOR && jbrMoveSupported(view)) {
|
||||
enterJbrMoveMode(session, view)
|
||||
return
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
val bounds = Rectangle(session.viewBounds)
|
||||
val delta = computeOffsetFromInitialLocation(session, event)
|
||||
if (cursorType == Cursor.DEFAULT_CURSOR && view is Frame) {
|
||||
val state = view.extendedState
|
||||
if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_HORIZ, state)) delta.x = 0
|
||||
if (WindowMouseListener.isStateSet(Frame.MAXIMIZED_VERT, state)) delta.y = 0
|
||||
}
|
||||
source.updateBounds(bounds, view, delta.x, delta.y)
|
||||
val currentViewBounds = view.bounds
|
||||
if (bounds != currentViewBounds) {
|
||||
val moved = bounds.x != currentViewBounds.x || bounds.y != currentViewBounds.y
|
||||
val resized = bounds.width != currentViewBounds.width || bounds.height != currentViewBounds.height
|
||||
val reallyMoveWindow = !moveAfterMouseRelease() || !mouseMove
|
||||
if ((moved && reallyMoveWindow) || resized) {
|
||||
setViewBounds(view, bounds, moved, resized)
|
||||
}
|
||||
}
|
||||
}
|
||||
event.consume()
|
||||
}
|
||||
|
||||
protected abstract fun onDraggingStarted(session: WindowListenerSession)
|
||||
|
||||
protected open fun computeOffsetFromInitialLocation(session: WindowListenerSession, event: MouseEvent): Point {
|
||||
val location = session.location
|
||||
val dx: Int = event.xOnScreen - location.x
|
||||
val dy: Int = event.yOnScreen - location.y
|
||||
return Point(dx, dy)
|
||||
}
|
||||
|
||||
private fun setViewBounds(view: Component, bounds: Rectangle, moved: Boolean, resized: Boolean) {
|
||||
view.reshape(bounds.x, bounds.y, bounds.width, bounds.height)
|
||||
view.invalidate()
|
||||
view.validate()
|
||||
view.repaint()
|
||||
if (moved) source.notifyMoved()
|
||||
if (resized) source.notifyResized()
|
||||
}
|
||||
|
||||
private fun handleStopEvent(event: MouseEvent) {
|
||||
val session = currentSession ?: return
|
||||
|
||||
resetCursor(source.getContent(event))
|
||||
|
||||
if (session.expectMouseReleased && event.id == MouseEvent.MOUSE_RELEASED) {
|
||||
session.expectMouseReleased = false
|
||||
event.consume()
|
||||
}
|
||||
if (session.expectMouseClicked && event.id == MouseEvent.MOUSE_CLICKED) {
|
||||
session.expectMouseClicked = false
|
||||
event.consume()
|
||||
}
|
||||
|
||||
if (!session.expectMouseReleased && !session.expectMouseClicked) {
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetCursor(content: Component) {
|
||||
source.setCursor(content, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR))
|
||||
}
|
||||
|
||||
private fun stop() {
|
||||
currentSession = null
|
||||
}
|
||||
|
||||
private fun enterJbrMoveMode(session: WindowListenerSession, view: Component?) {
|
||||
JBR.getWindowMove().startMovingTogetherWithMouse(view as Window, session.mouseButton)
|
||||
stop()
|
||||
}
|
||||
|
||||
protected data class WindowListenerSession(
|
||||
val location: Point,
|
||||
val viewBounds: Rectangle,
|
||||
val mouseButton: Int,
|
||||
var expectMouseReleased: Boolean = true,
|
||||
var expectMouseClicked: Boolean = true,
|
||||
)
|
||||
}
|
||||
|
||||
private class RegularWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) {
|
||||
override fun moveAfterMouseRelease(): Boolean = false
|
||||
|
||||
override fun onDraggingStarted(session: WindowListenerSession) {
|
||||
session.expectMouseClicked = false // after dragging starts, there will be only one "released" event
|
||||
}
|
||||
|
||||
override fun jbrMoveSupported(component: Component?): Boolean {
|
||||
return (component is Frame || component is Dialog) && JBR.isWindowMoveSupported()
|
||||
}
|
||||
}
|
||||
|
||||
private class WaylandWindowMouseListenerSupport(source: WindowMouseListenerSource) : WindowMouseListenerSupport(source) {
|
||||
private var isTruePopup = false
|
||||
private var dx = 0
|
||||
private var dy = 0
|
||||
|
||||
override fun beforeUpdate(event: MouseEvent, view: Component) {
|
||||
isTruePopup = view is Window && view.type == Window.Type.POPUP
|
||||
}
|
||||
|
||||
override fun onStarted(event: MouseEvent, view: Component) {
|
||||
dx = 0
|
||||
dy = 0
|
||||
if (isRelativeMovementMode()) {
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraggingStarted(session: WindowListenerSession) { } // on Wayland, whether dragging has started or not, both "released" and "clicked" events will arrive
|
||||
|
||||
override fun getResizeCursor(top: Int, left: Int, bottom: Int, right: Int, resizeArea: Insets): Int {
|
||||
if (isRelativeMovementMode()) return super.getResizeCursor(top, left, bottom, right, resizeArea)
|
||||
// Wayland doesn't allow to change window's location programmatically,
|
||||
// so resizing from top/left shall be forbidden for now.
|
||||
if (bottom < resizeArea.bottom) {
|
||||
if (right < resizeArea.right * 2) return Cursor.SE_RESIZE_CURSOR
|
||||
return Cursor.S_RESIZE_CURSOR
|
||||
}
|
||||
if (right < resizeArea.right) {
|
||||
if (bottom < resizeArea.bottom * 2) return Cursor.SE_RESIZE_CURSOR
|
||||
return Cursor.E_RESIZE_CURSOR
|
||||
}
|
||||
return Cursor.CUSTOM_CURSOR
|
||||
}
|
||||
|
||||
override fun computeOffsetFromInitialLocation(session: WindowListenerSession, event: MouseEvent): Point {
|
||||
if (isRelativeMovementMode()) {
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
val delta = JBR.getRelativePointerMovement().getAccumulatedMouseDeltaAndReset()
|
||||
dx += delta.x
|
||||
dy += delta.y
|
||||
return Point(dx, dy)
|
||||
}
|
||||
else {
|
||||
return super.computeOffsetFromInitialLocation(session, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isRelativeMovementMode(): Boolean = isTruePopup && JBR.isRelativePointerMovementSupported()
|
||||
|
||||
override fun moveAfterMouseRelease(): Boolean = !isRelativeMovementMode()
|
||||
|
||||
override fun jbrMoveSupported(component: Component?): Boolean {
|
||||
return component is Window && component.type != Window.Type.POPUP
|
||||
}
|
||||
}
|
||||
@@ -87,8 +87,8 @@ public class WindowResizeListener extends WindowMouseListener {
|
||||
if (myCorner != null && right < myCorner.getIconWidth() && bottom < myCorner.getIconHeight()) {
|
||||
return DEFAULT_CURSOR;
|
||||
}
|
||||
Insets expected = getResizeBorder(view);
|
||||
if (expected != null) {
|
||||
Insets resizeArea = getResizeBorder(view);
|
||||
if (resizeArea != null) {
|
||||
if (view instanceof Frame) {
|
||||
int state = ((Frame)view).getExtendedState();
|
||||
if (isStateSet(Frame.MAXIMIZED_HORIZ, state)) {
|
||||
@@ -101,28 +101,7 @@ public class WindowResizeListener extends WindowMouseListener {
|
||||
}
|
||||
}
|
||||
|
||||
// Wayland doesn't allow to change window's location programmatically,
|
||||
// so resizing from top/left shall be forbidden for now.
|
||||
if (!isWayland && top < expected.top) {
|
||||
if (left < expected.left * 2) return NW_RESIZE_CURSOR;
|
||||
if (right < expected.right * 2) return NE_RESIZE_CURSOR;
|
||||
return N_RESIZE_CURSOR;
|
||||
}
|
||||
if (bottom < expected.bottom) {
|
||||
if (!isWayland && left < expected.left * 2) return SW_RESIZE_CURSOR;
|
||||
if (right < expected.right * 2) return SE_RESIZE_CURSOR;
|
||||
return S_RESIZE_CURSOR;
|
||||
}
|
||||
if (!isWayland && left < expected.left) {
|
||||
if (top < expected.top * 2 ) return NW_RESIZE_CURSOR;
|
||||
if (bottom < expected.bottom * 2) return SW_RESIZE_CURSOR;
|
||||
return W_RESIZE_CURSOR;
|
||||
}
|
||||
if (right < expected.right) {
|
||||
if (!isWayland && top < expected.top * 2) return NE_RESIZE_CURSOR;
|
||||
if (bottom < expected.bottom * 2) return SE_RESIZE_CURSOR;
|
||||
return E_RESIZE_CURSOR;
|
||||
}
|
||||
return support.getResizeCursor(top, left, bottom, right, resizeArea);
|
||||
}
|
||||
return CUSTOM_CURSOR;
|
||||
}
|
||||
@@ -130,6 +109,7 @@ public class WindowResizeListener extends WindowMouseListener {
|
||||
@Override
|
||||
void updateBounds(Rectangle bounds, Component view, int dx, int dy) {
|
||||
Dimension minimum = view.getMinimumSize();
|
||||
var myCursorType = getCursorType();
|
||||
if (myCursorType == NE_RESIZE_CURSOR || myCursorType == E_RESIZE_CURSOR || myCursorType == SE_RESIZE_CURSOR || myCursorType == DEFAULT_CURSOR) {
|
||||
bounds.width += fixMinSize(dx, bounds.width, minimum.width);
|
||||
}
|
||||
|
||||
@@ -1401,7 +1401,10 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
|
||||
WindowRoundedCornersManager.setRoundedCorners(window, roundedCornerParams);
|
||||
}
|
||||
|
||||
if (myNormalWindowLevel && !window.isDisplayable()) {
|
||||
if (shouldUseTrueWaylandPopups()) {
|
||||
window.setType(Window.Type.POPUP);
|
||||
}
|
||||
else if (myNormalWindowLevel && !window.isDisplayable()) {
|
||||
window.setType(Window.Type.NORMAL);
|
||||
}
|
||||
myWindow = window;
|
||||
@@ -1526,19 +1529,33 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
|
||||
}
|
||||
|
||||
private static Point getLocationRelativeToParent(Rectangle bounds, Window popupParent) {
|
||||
Rectangle newBounds = new Rectangle(bounds);
|
||||
// The Wayland server may refuse to show a popup whose top-left corner is located outside the parent toplevel's bounds.
|
||||
var toplevelParent = ComponentUtil.findUltimateParent(popupParent);
|
||||
var toplevelParentLocation = toplevelParent.getLocationOnScreen();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"The top-level parent is located at " + toplevelParentLocation +
|
||||
" and has the size " + toplevelParent.getSize()
|
||||
);
|
||||
}
|
||||
Rectangle okBounds = new Rectangle(toplevelParentLocation.x, toplevelParentLocation.y,
|
||||
toplevelParent.getWidth() + newBounds.width, toplevelParent.getHeight() + newBounds.height);
|
||||
ScreenUtil.moveToFit(newBounds, okBounds, new Insets(0, 0, 1, 1));
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The bounds after fitting into the top-level parent: " + newBounds);
|
||||
}
|
||||
// The "bounds" are "screen" coordinates, which in Wayland means that they
|
||||
// are relative to the nearest toplevel (Window) in the hierarchy.
|
||||
// But popups in Wayland are expected to be located relative to popup's "parent" (a toplevel or another popup).
|
||||
// We need to adjust "bounds" to be relative to the parent.
|
||||
Rectangle newBounds = new Rectangle(bounds);
|
||||
Point parentLocation = popupParent.getLocationOnScreen();
|
||||
newBounds.x -= parentLocation.x;
|
||||
newBounds.y -= parentLocation.y;
|
||||
// The Wayland server may refuse to show a popup whose top-left corner is located outside the parent toplevel's bounds.
|
||||
// TODO: Need to fit into the nearest toplevel, not popupParent that may be another popup.
|
||||
Rectangle okBounds = new Rectangle(0, 0,
|
||||
popupParent.getWidth() + newBounds.width, popupParent.getHeight() + newBounds.height);
|
||||
ScreenUtil.moveToFit(newBounds, okBounds, new Insets(0, 0, 1, 1));
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The direct popup parent is located at " + parentLocation);
|
||||
LOG.debug("The bounds after converting to the parent coordinate system: " + newBounds);
|
||||
}
|
||||
return newBounds.getLocation();
|
||||
}
|
||||
|
||||
@@ -2833,6 +2850,7 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
|
||||
}
|
||||
|
||||
private @Nullable Point getStoredLocation() {
|
||||
if (shouldUseTrueWaylandPopups()) return null; // not supported at the moment
|
||||
if (myDimensionServiceKey == null) return null;
|
||||
return getWindowStateService(getProjectDependingOnKey(myDimensionServiceKey)).getLocation(myDimensionServiceKey);
|
||||
}
|
||||
@@ -2909,4 +2927,8 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup
|
||||
}
|
||||
return event.isConsumed();
|
||||
}
|
||||
|
||||
private static boolean shouldUseTrueWaylandPopups() {
|
||||
return StartupUiUtil.isWaylandToolkit() && Registry.is("wayland.true.popups", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,6 +546,13 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
if (rootPane != null) {
|
||||
return rootPane;
|
||||
}
|
||||
|
||||
// https://youtrack.jetbrains.com/issue/DPA-3312 Unexpected component for dataContext: org.jetbrains.skiko.SkiaLayer$1
|
||||
// The focus owner may be a non-JComponent, but it resides inside a wrapping JComponent parent
|
||||
Container parent = component == null ? null : component.getParent();
|
||||
if (parent instanceof JComponent c) {
|
||||
return c;
|
||||
}
|
||||
else {
|
||||
String componentClass = component == null ? null : component.getClass().getName();
|
||||
throw new AssertionError("Unexpected component for " + errorInfoSupplier.get() + ": " + componentClass);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -793,6 +793,8 @@
|
||||
implementationClass="com.intellij.codeInsight.codeVision.ui.renderers.providers.RichTextCodeVisionEntryPainter"/>
|
||||
<codeVisionPainterProvider forClass="com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry"
|
||||
implementationClass="com.intellij.codeInsight.codeVision.ui.renderers.providers.ZombieCodeVisionEntryPainter"/>
|
||||
<codeVisionPainterProvider forClass="com.intellij.codeInsight.codeVision.ui.model.ZeroWidthCodeVisionEntry"
|
||||
implementationClass="com.intellij.codeInsight.codeVision.ui.renderers.providers.ZeroWidthCodeVisionEntryPainter"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.codeInsight.codeVision.ui.renderers.CodeVisionListPainterFactory"
|
||||
serviceImplementation="com.intellij.codeInsight.codeVision.ui.renderers.DefaultCodeVisionListPainterFactory"/>
|
||||
|
||||
@@ -1027,6 +1027,7 @@
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.codeInsight.navigation.actions.GTDUCollector"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.codeInsight.navigation.actions.GoToDeclarationCollector"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.accessibility.AccessibilityUsageTrackerCollector"/>
|
||||
<statistics.applicationUsagesCollector implementation="com.intellij.accessibility.AccessibilityToolsStateCollector"/>
|
||||
<statistics.counterUsagesCollector
|
||||
implementationClass="com.intellij.ide.actions.searcheverywhere.SearchEverywhereFiltersStatisticsCollector"/>
|
||||
<statistics.counterUsagesCollector
|
||||
@@ -1804,6 +1805,7 @@
|
||||
provider="com.intellij.ide.minimap.settings.MinimapConfigurableProvider"
|
||||
key="settings.name" bundle="messages.MinimapBundle"/>
|
||||
<registryKey key="popup.advertiser.speed.search" defaultValue="false" description="Shows speed search advertiser in popups"/>
|
||||
<registryKey key="wayland.true.popups" defaultValue="false" description="Show popups in Wayland as true popups (allows remembering the location, but limits them to the IDE window)"/>
|
||||
<actionPromoter implementation="com.intellij.ide.actions.speedSearch.SpeedSearchActionPromoter"/>
|
||||
|
||||
<fileEditorProvider id="learnIde" implementation="com.intellij.openapi.wm.impl.welcomeScreen.learnIde.editorTab.LearnIdeFileEditorProvider"/>
|
||||
|
||||
+19
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
|
||||
import com.intellij.testFramework.junit5.TestApplication;
|
||||
import com.intellij.testFramework.utils.vfs.CheckVFSHealthExtension;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -312,9 +313,7 @@ public class PersistentFS_FindFilesTest {
|
||||
try {
|
||||
List<? extends Future<?>> futures = ContainerUtil.map(tasks, task -> pool.submit(task));
|
||||
|
||||
for (Future<?> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
ConcurrencyUtil.getAll(futures);
|
||||
}
|
||||
finally {
|
||||
pool.shutdown();
|
||||
|
||||
+2
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -112,7 +112,8 @@ public final class EnvironmentUtil {
|
||||
ourEnvGetter.set(loader);
|
||||
}
|
||||
|
||||
private static Map<String, String> getSystemEnv() {
|
||||
@ApiStatus.Internal
|
||||
public static Map<String, String> getSystemEnv() {
|
||||
if (OS.CURRENT == OS.Windows) {
|
||||
return Collections.unmodifiableMap(CollectionFactory.createCaseInsensitiveStringMap(System.getenv()));
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
@@ -755,22 +755,6 @@ a:com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor$Wrapper
|
||||
f:com.intellij.openapi.vcs.changes.ChangesPreprocess
|
||||
- <init>():V
|
||||
- s:preprocessChangesRemoveDeletedForDuplicateMoved(java.util.List):java.util.List
|
||||
a:com.intellij.openapi.vcs.changes.ChangesTreeEditorDiffPreview
|
||||
- com.intellij.openapi.vcs.changes.EditorTabDiffPreview
|
||||
- <init>(com.intellij.openapi.vcs.changes.ui.ChangesTree,javax.swing.JComponent):V
|
||||
- b:<init>(com.intellij.openapi.vcs.changes.ui.ChangesTree,javax.swing.JComponent,I,kotlin.jvm.internal.DefaultConstructorMarker):V
|
||||
- pf:getTree():com.intellij.openapi.vcs.changes.ui.ChangesTree
|
||||
- handleDoubleClick(java.awt.event.MouseEvent):Z
|
||||
- handleEnterKey():Z
|
||||
- p:handleEscapeKey():V
|
||||
- p:handleNextDiffShortcut():V
|
||||
- p:handleSingleClick():V
|
||||
- p:isOpenPreviewWithNextDiffShortcut():Z
|
||||
- p:isOpenPreviewWithSingleClick():Z
|
||||
- p:isOpenPreviewWithSingleClickEnabled():Z
|
||||
- p:isPreviewOnDoubleClick():Z
|
||||
- p:isPreviewOnEnter():Z
|
||||
- p:returnFocusToTree():V
|
||||
com.intellij.openapi.vcs.changes.ChangesViewEx
|
||||
- com.intellij.openapi.vcs.changes.ChangesViewI
|
||||
- a:getCommitWorkflowHandler():com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler
|
||||
@@ -781,13 +765,12 @@ com.intellij.openapi.vcs.changes.ChangesViewI
|
||||
- a:scheduleRefresh():V
|
||||
- a:selectChanges(java.util.List):V
|
||||
- a:selectFile(com.intellij.openapi.vfs.VirtualFile):V
|
||||
- a:setBusy(Z):V
|
||||
- a:setGrouping(java.lang.String):V
|
||||
- a:updateProgressComponent(java.util.List):V
|
||||
c:com.intellij.openapi.vcs.changes.ChangesViewManager
|
||||
- com.intellij.openapi.Disposable
|
||||
- com.intellij.openapi.vcs.changes.ChangesViewEx
|
||||
- <init>(com.intellij.openapi.project.Project):V
|
||||
- <init>(com.intellij.openapi.project.Project,kotlinx.coroutines.CoroutineScope):V
|
||||
- closeEditorPreview(Z):V
|
||||
- s:createTextStatusFactory(java.lang.String,Z):com.intellij.openapi.util.Factory
|
||||
- diffPreviewChanged(Z):V
|
||||
@@ -802,28 +785,8 @@ c:com.intellij.openapi.vcs.changes.ChangesViewManager
|
||||
- scheduleRefresh(java.lang.Runnable):V
|
||||
- selectChanges(java.util.List):V
|
||||
- selectFile(com.intellij.openapi.vfs.VirtualFile):V
|
||||
- setBusy(Z):V
|
||||
- setGrouping(java.lang.String):V
|
||||
- updateProgressComponent(java.util.List):V
|
||||
f:com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel
|
||||
- com.intellij.openapi.ui.SimpleToolWindowPanel
|
||||
- com.intellij.openapi.Disposable
|
||||
- addListener(com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel$Listener,com.intellij.openapi.Disposable):V
|
||||
- dispose():V
|
||||
- getActions(Z):java.util.List
|
||||
- isAllowExcludeFromCommit():Z
|
||||
- scheduleRefresh():V
|
||||
- selectChanges(java.util.List):V
|
||||
- selectFile(com.intellij.openapi.vfs.VirtualFile):V
|
||||
- uiDataSnapshot(com.intellij.openapi.actionSystem.DataSink):V
|
||||
- updateCommitWorkflow():V
|
||||
com.intellij.openapi.vcs.changes.ChangesViewManager$ChangesViewToolWindowPanel$Listener
|
||||
- java.util.EventListener
|
||||
- a:allowExcludeFromCommitChanged():V
|
||||
c:com.intellij.openapi.vcs.changes.ChangesViewManager$ContentPreloader
|
||||
- com.intellij.openapi.vcs.changes.ui.ChangesViewContentProvider$Preloader
|
||||
- <init>(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
|
||||
- <init>(com.intellij.openapi.project.Project):V
|
||||
@@ -2644,7 +2607,7 @@ c:com.intellij.openapi.vcs.changes.ui.TreeHandlerDiffRequestProcessor
|
||||
- pf:selectChange(com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor$Wrapper):V
|
||||
- p:showAllChangesForEmptySelection():Z
|
||||
a:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview
|
||||
- com.intellij.openapi.vcs.changes.ChangesTreeEditorDiffPreview
|
||||
- com.intellij.openapi.vcs.changes.EditorTabDiffPreview
|
||||
- sf:Companion:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview$Companion
|
||||
- <init>(com.intellij.openapi.vcs.changes.ui.ChangesTree,com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler):V
|
||||
- <init>(com.intellij.openapi.vcs.changes.ui.ChangesTree,javax.swing.JComponent,com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler):V
|
||||
@@ -2655,7 +2618,19 @@ a:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview
|
||||
- pf:getEditorTabName(com.intellij.diff.impl.DiffEditorViewer):java.lang.String
|
||||
- a:getEditorTabName(com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor$Wrapper):java.lang.String
|
||||
- pf:getHandler():com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler
|
||||
- pf:getTree():com.intellij.openapi.vcs.changes.ui.ChangesTree
|
||||
- handleDoubleClick(java.awt.event.MouseEvent):Z
|
||||
- handleEnterKey():Z
|
||||
- p:handleEscapeKey():V
|
||||
- p:handleNextDiffShortcut():V
|
||||
- p:handleSingleClick():V
|
||||
- f:hasContent():Z
|
||||
- p:isOpenPreviewWithNextDiffShortcut():Z
|
||||
- p:isOpenPreviewWithSingleClick():Z
|
||||
- p:isOpenPreviewWithSingleClickEnabled():Z
|
||||
- p:isPreviewOnDoubleClick():Z
|
||||
- p:isPreviewOnEnter():Z
|
||||
- p:returnFocusToTree():V
|
||||
f:com.intellij.openapi.vcs.changes.ui.TreeHandlerEditorDiffPreview$Companion
|
||||
- f:createDefaultViewer(com.intellij.openapi.vcs.changes.ui.ChangesTree,com.intellij.openapi.vcs.changes.ui.ChangesTreeDiffPreviewHandler,java.lang.String):com.intellij.diff.impl.DiffEditorViewer
|
||||
a:com.intellij.openapi.vcs.changes.ui.VcsToolWindowFactory
|
||||
@@ -3969,24 +3944,37 @@ a:com.intellij.vcs.commit.ChangeListViewCommitPanel
|
||||
- f:getEditedCommit():com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation
|
||||
- f:getIncludedChanges():java.util.List
|
||||
- f:getIncludedUnversionedFiles():java.util.List
|
||||
- f:getInclusionModel():com.intellij.openapi.vcs.changes.InclusionModel
|
||||
- f:select(java.lang.Object):V
|
||||
- f:selectFirst(java.util.Collection):V
|
||||
- f:setCompletionContext(java.util.List):V
|
||||
- f:setEditedCommit(com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation):V
|
||||
- f:setInclusionModel(com.intellij.openapi.vcs.changes.InclusionModel):V
|
||||
- setInclusionModel(com.intellij.openapi.vcs.changes.InclusionModel):V
|
||||
f:com.intellij.vcs.commit.ChangesViewCommitPanel
|
||||
- com.intellij.vcs.commit.ChangeListViewCommitPanel
|
||||
- com.intellij.vcs.commit.NonModalCommitPanel
|
||||
- com.intellij.vcs.commit.ChangesViewCommitWorkflowUi
|
||||
- activate():Z
|
||||
- deactivate(Z):V
|
||||
- dispose():V
|
||||
- endExecution():V
|
||||
- expand(java.lang.Object):V
|
||||
- getCommitProgressUi():com.intellij.vcs.commit.CommitProgressUi
|
||||
- getDisplayedChanges():java.util.List
|
||||
- getDisplayedUnversionedFiles():java.util.List
|
||||
- getEditedCommit():com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation
|
||||
- getIncludedChanges():java.util.List
|
||||
- getIncludedUnversionedFiles():java.util.List
|
||||
- isActive():Z
|
||||
- refreshChangesViewBeforeCommit(kotlin.coroutines.Continuation):java.lang.Object
|
||||
- select(java.lang.Object):V
|
||||
- selectFirst(java.util.Collection):V
|
||||
- setCompletionContext(java.util.List):V
|
||||
- setEditedCommit(com.intellij.platform.vcs.impl.shared.commit.EditedCommitPresentation):V
|
||||
- setInclusionModel(com.intellij.openapi.vcs.changes.InclusionModel):V
|
||||
com.intellij.vcs.commit.ChangesViewCommitWorkflowUi
|
||||
- com.intellij.vcs.commit.NonModalCommitWorkflowUi
|
||||
- a:deactivate(Z):V
|
||||
- a:endExecution():V
|
||||
- a:expand(java.lang.Object):V
|
||||
- a:getInclusionModel():com.intellij.openapi.vcs.changes.InclusionModel
|
||||
- a:isActive():Z
|
||||
- a:refreshChangesViewBeforeCommit(kotlin.coroutines.Continuation):java.lang.Object
|
||||
- a:select(java.lang.Object):V
|
||||
@@ -4093,6 +4081,7 @@ c:com.intellij.vcs.commit.CommitProgressPanel
|
||||
- com.intellij.openapi.vcs.changes.InclusionListener
|
||||
- com.intellij.vcs.commit.CommitProgressUi
|
||||
- <init>(com.intellij.openapi.project.Project):V
|
||||
- <init>(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
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
<ui.content.impl.toolWindowContentPostprocessor implementation="com.intellij.platform.vcs.impl.frontend.toolwindow.VcsToolWindowContentReplacer"/>
|
||||
|
||||
<frontendChangesViewContentProvider implementation="com.intellij.platform.vcs.impl.frontend.shelf.ShelveChangesViewContentProvider"/>
|
||||
<frontendChangesViewContentProvider implementation="com.intellij.platform.vcs.impl.frontend.changes.LocalChangesViewContentProvider"/>
|
||||
|
||||
<frontend.splitComponentProvider implementation="com.intellij.platform.vcs.impl.frontend.changes.ChangesViewSplitComponentProvider"/>
|
||||
|
||||
<recentFiles.navigator implementation="com.intellij.platform.vcs.impl.frontend.diff.DiffRecentFilesNavigator"/>
|
||||
<recentFiles.excluder implementation="com.intellij.platform.vcs.impl.frontend.diff.DiffRecentFilesExcluder"/>
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.vcs.impl.frontend.changes
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.FilePath
|
||||
import com.intellij.openapi.vcs.changes.*
|
||||
import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
|
||||
import com.intellij.ui.split.SplitComponentBinding
|
||||
import com.intellij.ui.split.SplitComponentProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.swing.JComponent
|
||||
|
||||
internal class ChangesViewSplitComponentProvider : SplitComponentProvider<ChangesViewId> {
|
||||
override val binding: SplitComponentBinding<ChangesViewId> = ChangesViewSplitComponentBinding
|
||||
|
||||
override fun createComponent(project: Project, scope: CoroutineScope, modelId: ChangesViewId): JComponent {
|
||||
val tree = LocalChangesListView(project)
|
||||
val panel = CommitChangesViewWithToolbarPanel(tree, scope)
|
||||
panel.id = modelId
|
||||
panel.initPanel(object : CommitChangesViewWithToolbarPanel.ModelProvider {
|
||||
override fun getModelData(): CommitChangesViewWithToolbarPanel.ModelProvider.ModelData {
|
||||
val changeLists = ChangeListsViewModel.getInstance(project).changeLists.value
|
||||
return CommitChangesViewWithToolbarPanel.ModelProvider.ModelData(changeLists.lists,
|
||||
emptyList(),
|
||||
emptyList()) { true }
|
||||
}
|
||||
|
||||
override fun synchronizeInclusion(changeLists: List<LocalChangeList>, unversionedFiles: List<FilePath>) {
|
||||
}
|
||||
})
|
||||
|
||||
scope.launch {
|
||||
ChangeListsViewModel.getInstance(project).changeLists.collect {
|
||||
panel.scheduleRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
return panel
|
||||
}
|
||||
}
|
||||
-56
@@ -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<LocalChangeList>, unversionedFiles: List<FilePath>) {
|
||||
}
|
||||
})
|
||||
|
||||
content.component = panel
|
||||
|
||||
project.service<ScopeProvider>().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)
|
||||
}
|
||||
@@ -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<ChangesViewId> = SplitComponentBinding("ChangesView", ::ChangesViewId)
|
||||
|
||||
@Serializable
|
||||
@ApiStatus.Internal
|
||||
data class ChangesViewId(override val uid: UID) : Id
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+9
-22
@@ -1,54 +1,41 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.vcs.changes
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.project.InitialVfsRefreshService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.FilePath
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesBrowserUnversionedLoadingPendingNode
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory
|
||||
import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder
|
||||
import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
|
||||
import com.intellij.platform.vcs.impl.shared.changes.TreeModelBuilderEx
|
||||
import com.intellij.platform.vcs.impl.shared.commit.PartialCommitChangeNodeDecorator
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import javax.swing.tree.DefaultTreeModel
|
||||
|
||||
// TODO make shared
|
||||
private typealias DecoratorProvider = (ChangeNodeDecorator) -> ChangeNodeDecorator
|
||||
|
||||
@ApiStatus.Internal
|
||||
object ChangesViewUtil {
|
||||
private fun getChangeDecoratorProvider(project: Project, isAllowExcludeFromCommit: () -> Boolean): (ChangeNodeDecorator?) -> PartialCommitChangeNodeDecorator {
|
||||
return { baseDecorator: ChangeNodeDecorator? -> PartialCommitChangeNodeDecorator(project, baseDecorator, isAllowExcludeFromCommit) }
|
||||
}
|
||||
private fun getChangeDecoratorProvider(project: Project, isAllowExcludeFromCommit: () -> Boolean): DecoratorProvider =
|
||||
{ baseDecorator -> PartialCommitChangeNodeDecorator(project, baseDecorator, isAllowExcludeFromCommit) }
|
||||
|
||||
fun createTreeModel(
|
||||
project: Project,
|
||||
grouping: ChangesGroupingPolicyFactory,
|
||||
changeLists: List<LocalChangeList>,
|
||||
unversionedFiles: List<FilePath>,
|
||||
showIgnoredFiles: Boolean,
|
||||
ignoredFiles: List<FilePath>,
|
||||
isAllowExcludeFromCommit: () -> Boolean
|
||||
): DefaultTreeModel {
|
||||
val changeListManager = ChangeListManagerImpl.getInstanceImpl(project)
|
||||
|
||||
val shouldShowUntrackedLoading = unversionedFiles.isEmpty() &&
|
||||
!project.getService(InitialVfsRefreshService::class.java).isInitialVfsRefreshFinished() &&
|
||||
changeListManager.isUnversionedInUpdateMode
|
||||
val skipSingleDefaultChangeList = !changeListManager.areChangeListsEnabled()
|
||||
|
||||
val skipSingleDefaultChangeList = !ChangeListsViewModel.getInstance(project).areChangeListsEnabled.value
|
||||
val treeModelBuilder = TreeModelBuilder(project, grouping)
|
||||
.setChangeLists(changeLists, skipSingleDefaultChangeList, getChangeDecoratorProvider(project, isAllowExcludeFromCommit))
|
||||
.also { TreeModelBuilderEx.getInstanceOrNull(project)?.modifyTreeModelBuilder(it) }
|
||||
.setUnversioned(unversionedFiles)
|
||||
|
||||
if (showIgnoredFiles) {
|
||||
val ignoredFilePaths: List<FilePath> = changeListManager.getIgnoredFilePaths()
|
||||
treeModelBuilder.setIgnored(ignoredFilePaths)
|
||||
}
|
||||
if (shouldShowUntrackedLoading) {
|
||||
treeModelBuilder.insertSubtreeRoot(ChangesBrowserUnversionedLoadingPendingNode())
|
||||
}
|
||||
treeModelBuilder.setIgnored(ignoredFiles)
|
||||
|
||||
for (extension in ChangesViewModifier.KEY.getExtensions(project)) {
|
||||
try {
|
||||
+53
-47
@@ -3,25 +3,19 @@ package com.intellij.openapi.vcs.changes
|
||||
|
||||
import com.intellij.ide.CommonActionsManager
|
||||
import com.intellij.ide.ui.customization.CustomActionsSchema
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.UI
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.progress.checkCanceled
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vcs.FilePath
|
||||
import com.intellij.openapi.vcs.changes.ChangesViewModifier.ChangesViewModifierListener
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesGroupingPolicyFactory
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesListView
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesTree
|
||||
import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder
|
||||
import com.intellij.platform.diagnostic.telemetry.TelemetryManager
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.use
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.platform.vcs.impl.shared.SingleTaskRunner
|
||||
import com.intellij.platform.vcs.impl.shared.changes.ChangeListsViewModel
|
||||
import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings
|
||||
@@ -30,57 +24,66 @@ import com.intellij.platform.vcs.impl.shared.telemetry.ChangesView
|
||||
import com.intellij.platform.vcs.impl.shared.telemetry.VcsScope
|
||||
import com.intellij.ui.ExperimentalUI.Companion.isNewUI
|
||||
import com.intellij.util.application
|
||||
import com.intellij.util.asDisposable
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.ui.EdtInvocationManager.invokeLaterIfNeeded
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.CalledInAny
|
||||
import java.lang.Runnable
|
||||
import javax.swing.tree.DefaultTreeModel
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
private val REFRESH_DELAY = 100.milliseconds
|
||||
|
||||
@ApiStatus.Internal
|
||||
class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisposable: Disposable) : ChangesViewPanel(changesView), Disposable {
|
||||
class CommitChangesViewWithToolbarPanel(
|
||||
changesView: ChangesListView,
|
||||
private val cs: CoroutineScope,
|
||||
) : ChangesViewPanel(changesView) {
|
||||
val project: Project get() = changesView.project
|
||||
private val settings get() = ChangesViewSettings.getInstance(project)
|
||||
|
||||
private val scope = project.service<ScopeProvider>().cs.childScope("CommitChangesListWithToolbarPanel")
|
||||
private val refresher = SingleTaskRunner(scope, 100.milliseconds) {
|
||||
private val refresher = SingleTaskRunner(cs) {
|
||||
refreshView()
|
||||
}
|
||||
|
||||
private var modelProvider: ModelProvider? = null
|
||||
|
||||
private var disposed = false
|
||||
var id: ChangesViewId? = null
|
||||
set(value) {
|
||||
if (field == null) field = value
|
||||
else error("Id is already assigned")
|
||||
}
|
||||
|
||||
init {
|
||||
refresher.start()
|
||||
Disposer.register(parentDisposable, this)
|
||||
}
|
||||
|
||||
@CalledInAny
|
||||
fun setBusy(busy: Boolean) {
|
||||
invokeLaterIfNeeded { changesView.setPaintBusy(busy) }
|
||||
cs.launch(Dispatchers.UI) {
|
||||
refresher.getIdleFlow().collect { idle ->
|
||||
changesView.setPaintBusy(!idle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
fun initPanel(modelProvider: ModelProvider) {
|
||||
checkNotNull(id)
|
||||
|
||||
this.modelProvider = modelProvider
|
||||
|
||||
scope.launch(Dispatchers.UI) {
|
||||
cs.launch(Dispatchers.UI) {
|
||||
ChangeListsViewModel.getInstance(project).changeListManagerState.collectLatest {
|
||||
changesView.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch(Dispatchers.UI) {
|
||||
cs.launch(Dispatchers.UI) {
|
||||
PartialChangesHolder.getInstance(project).updates.collectLatest {
|
||||
changesView.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
changesView.isShowCheckboxes = true
|
||||
changesView.installPopupHandler(
|
||||
ActionManager.getInstance().getAction("ChangesViewPopupMenuShared") as ActionGroup
|
||||
)
|
||||
@@ -92,16 +95,16 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
|
||||
|
||||
toolbarActionGroup.addAll(createChangesToolbarActions(changesView))
|
||||
|
||||
Initializer.EP_NAME.forEachExtensionSafe { it.init(scope, this) }
|
||||
Initializer.EP_NAME.forEachExtensionSafe { it.init(cs, this) }
|
||||
|
||||
ChangesViewModifier.KEY.addChangeListener(project, { resetViewImmediatelyAndRefreshLater() }, this)
|
||||
project.messageBus.connect(scope).subscribe(ChangesViewModifier.TOPIC, ChangesViewModifierListener { scheduleRefresh() })
|
||||
ChangesViewModifier.KEY.addChangeListener(project, { resetViewImmediatelyAndRefreshLater() }, cs.asDisposable())
|
||||
project.messageBus.connect(cs).subscribe(ChangesViewModifier.TOPIC, ChangesViewModifierListener { scheduleRefresh() })
|
||||
|
||||
scheduleRefresh()
|
||||
}
|
||||
|
||||
@CalledInAny
|
||||
fun scheduleRefresh() {
|
||||
fun scheduleRefresh(){
|
||||
scheduleRefresh(withDelay = true)
|
||||
}
|
||||
|
||||
@@ -118,35 +121,43 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
|
||||
|
||||
@CalledInAny
|
||||
private fun scheduleRefresh(withDelay: Boolean, @RequiresBackgroundThread callback: Runnable? = null) {
|
||||
setBusy(true)
|
||||
if (withDelay) {
|
||||
if (!withDelay && callback == null) {
|
||||
refresher.request()
|
||||
return
|
||||
}
|
||||
else {
|
||||
refresher.requestNow()
|
||||
}
|
||||
scope.launch {
|
||||
|
||||
cs.launch {
|
||||
if (withDelay) delay(REFRESH_DELAY)
|
||||
refresher.request()
|
||||
refresher.awaitNotBusy()
|
||||
callback?.run()
|
||||
setBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresBackgroundThread
|
||||
private suspend fun refreshView() {
|
||||
if (disposed || !project.isInitialized || application.isUnitTestMode) return
|
||||
if (!cs.isActive || !project.isInitialized || application.isUnitTestMode) return
|
||||
val modelProvider = modelProvider ?: return
|
||||
|
||||
val model = TRACER.spanBuilder(ChangesView.ChangesViewRefreshBackground.name).use {
|
||||
modelProvider.getModel(changesView.grouping)
|
||||
val (modelData, model) = TRACER.spanBuilder(ChangesView.ChangesViewRefreshBackground.name).use {
|
||||
val modelData = modelProvider.getModelData()
|
||||
|
||||
modelData to ChangesViewUtil.createTreeModel(
|
||||
project,
|
||||
changesView.grouping,
|
||||
modelData.changeLists,
|
||||
modelData.unversionedFiles,
|
||||
modelData.ignoredFiles,
|
||||
modelData.isAllowExcludeFromCommit,
|
||||
)
|
||||
}
|
||||
|
||||
checkCanceled()
|
||||
withContext(Dispatchers.EDT) {
|
||||
TRACER.spanBuilder(ChangesView.ChangesViewRefreshEdt.getName()).use {
|
||||
changesView.updateTreeModel(model.treeModel, ChangesViewTreeStateStrategy())
|
||||
changesView.updateTreeModel(model, ChangesViewTreeStateStrategy())
|
||||
checkCanceled()
|
||||
modelProvider.synchronizeInclusion(model.changeLists, model.unversionedFiles)
|
||||
modelProvider.synchronizeInclusion(modelData.changeLists, modelData.unversionedFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,11 +173,6 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
|
||||
scheduleRefreshNow()
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
scope.cancel()
|
||||
disposed = true
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val TRACER
|
||||
get() = TelemetryManager.getInstance().getTracer(VcsScope)
|
||||
@@ -192,11 +198,14 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
|
||||
}
|
||||
|
||||
interface ModelProvider {
|
||||
fun getModel(grouping: ChangesGroupingPolicyFactory): ExtendedTreeModel
|
||||
fun getModelData(): ModelData
|
||||
|
||||
fun synchronizeInclusion(changeLists: List<LocalChangeList>, unversionedFiles: List<FilePath>)
|
||||
|
||||
class ExtendedTreeModel(val changeLists: List<LocalChangeList>, val unversionedFiles: List<FilePath>, val treeModel: DefaultTreeModel)
|
||||
class ModelData(val changeLists: List<LocalChangeList>,
|
||||
val unversionedFiles: List<FilePath>,
|
||||
val ignoredFiles: List<FilePath>,
|
||||
val isAllowExcludeFromCommit: () -> Boolean)
|
||||
}
|
||||
|
||||
interface Initializer {
|
||||
@@ -206,7 +215,4 @@ class CommitChangesViewWithToolbarPanel(changesView: ChangesListView, parentDisp
|
||||
internal val EP_NAME = ExtensionPointName<Initializer>("com.intellij.vcs.commitChangesViewInitializer")
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class ScopeProvider(val cs: CoroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
+19
-25
@@ -6,10 +6,7 @@ import com.intellij.openapi.diagnostic.getOrHandleException
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.checkCanceled
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import kotlin.time.Duration
|
||||
|
||||
@@ -21,47 +18,39 @@ import kotlin.time.Duration
|
||||
@ApiStatus.Internal
|
||||
class SingleTaskRunner(
|
||||
cs: CoroutineScope,
|
||||
private val delay: Duration = Duration.Companion.ZERO,
|
||||
private val task: suspend () -> Unit,
|
||||
) {
|
||||
private val requested = MutableStateFlow(false)
|
||||
private val busy = MutableStateFlow(false)
|
||||
/**
|
||||
* Represents the state of the runner:
|
||||
* 0 - idle,
|
||||
* 1 - a single task is either queued or running,
|
||||
* 2 - has one running task and one queued task.
|
||||
*/
|
||||
private val requestCounter = MutableStateFlow(0)
|
||||
|
||||
private val runNow = Channel<Unit>(capacity = Channel.CONFLATED)
|
||||
fun getIdleFlow(): Flow<Boolean> = requestCounter.map { it == 0 }
|
||||
|
||||
private val processorJob = cs.launch(Dispatchers.Default, CoroutineStart.LAZY) {
|
||||
try {
|
||||
while (true) {
|
||||
checkCanceled()
|
||||
requested.first { it }
|
||||
if (delay.isPositive()) {
|
||||
withTimeoutOrNull(delay) { runNow.receive() }
|
||||
}
|
||||
busy.value = true
|
||||
requested.value = false
|
||||
requestCounter.first { it > 0 }
|
||||
checkCanceled()
|
||||
runCatching {
|
||||
task()
|
||||
}.getOrHandleException { LOG.error("Task failed", it) }
|
||||
busy.value = false
|
||||
requestCounter.update { it.dec() }
|
||||
}
|
||||
}
|
||||
finally {
|
||||
requested.value = false
|
||||
busy.value = false
|
||||
requestCounter.value = 0
|
||||
LOG.debug { "Task processing finished" }
|
||||
}
|
||||
}
|
||||
|
||||
fun request() {
|
||||
if (processorJob.isCancelled) return
|
||||
requested.value = true
|
||||
}
|
||||
|
||||
fun requestNow() {
|
||||
if (processorJob.isCancelled) return
|
||||
runNow.trySend(Unit)
|
||||
requested.value = true
|
||||
requestCounter.update { it.inc().coerceAtMost(2) }
|
||||
}
|
||||
|
||||
fun start() {
|
||||
@@ -73,10 +62,15 @@ class SingleTaskRunner(
|
||||
* Await the state where the task is not executed and there are no requests to do so.
|
||||
*/
|
||||
suspend fun awaitNotBusy() {
|
||||
requested.combine(busy) { requested, busy -> !requested && !busy }.first { it }
|
||||
getIdleFlow().first { it }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<SingleTaskRunner>()
|
||||
|
||||
fun delayedTaskRunner(cs: CoroutineScope, delay: Duration, task: suspend () -> Unit): SingleTaskRunner = SingleTaskRunner(cs) {
|
||||
delay(delay)
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -22,7 +22,7 @@ class ChangeListsViewModel(
|
||||
) {
|
||||
val areChangeListsEnabled: StateFlow<Boolean> = changeListsApiFlow(checkRegistry = false) { api, projectId ->
|
||||
emitAll(api.areChangeListsEnabled(projectId))
|
||||
}.stateIn(cs, SharingStarted.Eagerly, false)
|
||||
}.stateIn(cs, SharingStarted.Eagerly, true)
|
||||
|
||||
val changeListManagerState: StateFlow<ChangeListManagerState> = changeListsApiFlow(checkRegistry = false) { api, projectId ->
|
||||
emitAll(api.getChangeListManagerState(projectId))
|
||||
|
||||
@@ -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());
|
||||
|
||||
-93
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ public interface ChangesViewI {
|
||||
void selectFile(VirtualFile vFile);
|
||||
void selectChanges(@NotNull List<? extends Change> changes);
|
||||
void updateProgressComponent(@NotNull List<Supplier<JComponent>> progress);
|
||||
void setBusy(boolean b);
|
||||
|
||||
void setGrouping(@NotNull String groupingKey);
|
||||
}
|
||||
|
||||
@@ -23,23 +23,23 @@ import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.registry.RegistryValue;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.*;
|
||||
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
|
||||
import com.intellij.openapi.vcs.VcsApplicationSettings;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vcs.VcsConfiguration;
|
||||
import com.intellij.openapi.vcs.changes.actions.diff.ShowDiffFromLocalChangesActionProvider;
|
||||
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
|
||||
import com.intellij.openapi.vcs.changes.ui.*;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.platform.vcs.impl.shared.RdLocalChanges;
|
||||
import com.intellij.platform.vcs.impl.shared.changes.ChangesViewDataKeys;
|
||||
import com.intellij.platform.vcs.impl.shared.changes.ChangesViewSettings;
|
||||
import com.intellij.platform.vcs.impl.shared.changes.PreviewDiffSplitterComponent;
|
||||
import com.intellij.ui.ComponentUtil;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.ui.components.panels.Wrapper;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
@@ -47,23 +47,18 @@ import com.intellij.util.ui.JBDimension;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import com.intellij.vcs.changes.BackendChangesView;
|
||||
import com.intellij.vcs.commit.*;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
import kotlinx.coroutines.CoroutineScope;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -71,26 +66,25 @@ import static com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager.*;
|
||||
import static com.intellij.openapi.vcs.changes.ui.ChangesViewContentManagerKt.isCommitToolWindowShown;
|
||||
import static com.intellij.openapi.vcs.changes.ui.ChangesViewContentManagerKt.subscribeOnVcsToolWindowLayoutChanges;
|
||||
import static com.intellij.util.ui.JBUI.Panels.simplePanel;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
private static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION";
|
||||
|
||||
private final @NotNull CoroutineScope myScope;
|
||||
private final @NotNull Project myProject;
|
||||
|
||||
private @Nullable CommitChangesViewWithToolbarPanel myChangesPanel;
|
||||
private @Nullable BackendChangesView myChangesView;
|
||||
private @Nullable ChangesViewToolWindowPanel myToolWindowPanel;
|
||||
|
||||
@NotNull
|
||||
@RequiresEdt
|
||||
CommitChangesViewWithToolbarPanel initChangesPanel() {
|
||||
if (myChangesPanel == null) {
|
||||
BackendChangesView initChangesView() {
|
||||
if (myChangesView == null) {
|
||||
Activity activity = StartUpMeasurer.startActivity("ChangesViewPanel initialization");
|
||||
ChangesListView tree = new LocalChangesListView(myProject);
|
||||
myChangesPanel = new CommitChangesViewWithToolbarPanel(tree, this);
|
||||
myChangesView = BackendChangesView.create(myProject, myScope);
|
||||
activity.end();
|
||||
}
|
||||
return myChangesPanel;
|
||||
return myChangesView;
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
@@ -98,9 +92,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
if (myToolWindowPanel == null) {
|
||||
Activity activity = StartUpMeasurer.startActivity("ChangesViewToolWindowPanel initialization");
|
||||
|
||||
// ChangesViewPanel is used for a singular ChangesViewToolWindowPanel instance. Cleanup is not needed.
|
||||
CommitChangesViewWithToolbarPanel changesViewPanel = initChangesPanel();
|
||||
ChangesViewToolWindowPanel panel = new ChangesViewToolWindowPanel(myProject, changesViewPanel);
|
||||
BackendChangesView changesView = initChangesView();
|
||||
ChangesViewToolWindowPanel panel = new ChangesViewToolWindowPanel(myProject, changesView);
|
||||
Disposer.register(this, panel);
|
||||
|
||||
panel.updateCommitWorkflow();
|
||||
@@ -108,7 +101,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
myToolWindowPanel = panel;
|
||||
Disposer.register(panel, () -> {
|
||||
// Content is removed from TW
|
||||
myChangesPanel = null;
|
||||
myChangesView = null;
|
||||
myToolWindowPanel = null;
|
||||
});
|
||||
|
||||
@@ -117,7 +110,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
return myToolWindowPanel;
|
||||
}
|
||||
|
||||
public ChangesViewManager(@NotNull Project project) {
|
||||
public ChangesViewManager(@NotNull Project project, @NotNull CoroutineScope coroutineScope) {
|
||||
myScope = coroutineScope;
|
||||
myProject = project;
|
||||
|
||||
MessageBusConnection busConnection = project.getMessageBus().connect(this);
|
||||
@@ -159,10 +153,10 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public static class ContentPreloader implements ChangesViewContentProvider.Preloader {
|
||||
static class ContentPreloader implements ChangesViewContentProvider.Preloader {
|
||||
private final @NotNull Project myProject;
|
||||
|
||||
public ContentPreloader(@NotNull Project project) {
|
||||
ContentPreloader(@NotNull Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@@ -182,27 +176,26 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
@Override
|
||||
public void scheduleRefresh(@NotNull Runnable callback) {
|
||||
if (myToolWindowPanel != null) {
|
||||
myToolWindowPanel.scheduleRefreshNow(callback);
|
||||
}
|
||||
if (myChangesView == null) return;
|
||||
myChangesView.getViewModel().scheduleRefreshNow(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleRefresh() {
|
||||
if (myToolWindowPanel == null) return;
|
||||
myToolWindowPanel.scheduleRefresh();
|
||||
if (myChangesView == null) return;
|
||||
myChangesView.getViewModel().scheduleDelayedRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectFile(VirtualFile vFile) {
|
||||
if (myToolWindowPanel == null) return;
|
||||
myToolWindowPanel.selectFile(vFile);
|
||||
if (myChangesView == null) return;
|
||||
myChangesView.getViewModel().selectFile(vFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectChanges(@NotNull List<? extends Change> changes) {
|
||||
if (myToolWindowPanel == null) return;
|
||||
myToolWindowPanel.selectChanges(changes);
|
||||
if (myChangesView == null) return;
|
||||
myChangesView.getViewModel().selectChanges(new ArrayList<>(changes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -211,16 +204,10 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
myToolWindowPanel.updateProgressComponent(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBusy(boolean b) {
|
||||
if (myToolWindowPanel == null) return;
|
||||
myToolWindowPanel.myChangesPanel.setBusy(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGrouping(@NotNull String groupingKey) {
|
||||
if (myToolWindowPanel == null) return;
|
||||
myToolWindowPanel.myChangesPanel.setGrouping(groupingKey);
|
||||
if (myChangesView == null) return;
|
||||
myChangesView.getViewModel().setGrouping(groupingKey);
|
||||
}
|
||||
|
||||
private void updateCommitWorkflow() {
|
||||
@@ -241,8 +228,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
@Override
|
||||
public void resetViewImmediatelyAndRefreshLater() {
|
||||
if (myToolWindowPanel != null) {
|
||||
myToolWindowPanel.myChangesPanel.resetViewImmediatelyAndRefreshLater();
|
||||
if (myChangesView != null) {
|
||||
myChangesView.getViewModel().resetViewImmediatelyAndRefreshLater();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,25 +242,12 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
@Override
|
||||
public void initTabContent(@NotNull Content content) {
|
||||
if (RdLocalChanges.isEnabled()) {
|
||||
// Action isn't executed unless a context component is showing,
|
||||
// which makes things complicated in RD scenario because:
|
||||
// * For Commit TW we still use PROJECTOR_STEALING hacking content of the particular tab.
|
||||
// * The current content component in a backend TW is selected as the context component.
|
||||
// (see `ToolWindowComponentIdProvider` and `ComponentIdOwner` for more insights).
|
||||
// * An empty panel is used for this tab content on the backend.
|
||||
//
|
||||
// As a workaround, it's explicitly marked as shown.
|
||||
ComponentUtil.markAsShowing(content.getComponent(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
ChangesViewManager viewManager = (ChangesViewManager)getInstance(myProject);
|
||||
ChangesViewToolWindowPanel panel = viewManager.initToolWindowPanel();
|
||||
|
||||
content.setHelpId(ChangesListView.HELP_ID);
|
||||
content.setComponent(panel);
|
||||
content.setPreferredFocusableComponent(panel.myView);
|
||||
content.setPreferredFocusableComponent(panel.myChangesView.getViewModel().getPreferredFocusableComponent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +292,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ChangesViewToolWindowPanel extends SimpleToolWindowPanel implements Disposable {
|
||||
private static final class ChangesViewToolWindowPanel extends SimpleToolWindowPanel implements Disposable {
|
||||
private static final @NotNull RegistryValue isOpenEditorDiffPreviewWithSingleClick =
|
||||
Registry.get("show.diff.preview.as.editor.tab.with.single.click");
|
||||
|
||||
@@ -328,8 +302,8 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
private final @NotNull Wrapper myMainPanelContent;
|
||||
private final @NotNull BorderLayoutPanel myContentPanel;
|
||||
private final @NotNull CommitChangesViewWithToolbarPanel myChangesPanel;
|
||||
private final @NotNull ChangesListView myView;
|
||||
|
||||
private final @NotNull BackendChangesView myChangesView;
|
||||
|
||||
private final @NotNull ChangesViewCommitPanelSplitter myCommitPanelSplitter;
|
||||
private final @NotNull ChangesViewEditorDiffPreview myEditorDiffPreview;
|
||||
@@ -345,18 +319,16 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
private boolean myDisposed = false;
|
||||
|
||||
private ChangesViewToolWindowPanel(@NotNull Project project,
|
||||
@NotNull CommitChangesViewWithToolbarPanel changesViewPanel) {
|
||||
@NotNull BackendChangesView backendChangesView) {
|
||||
super(false, true);
|
||||
myProject = project;
|
||||
myChangesViewSettings = ChangesViewSettings.getInstance(project);
|
||||
myChangesPanel = changesViewPanel;
|
||||
changesViewPanel.initPanel(new ModelProvider(project, () -> myCommitWorkflowHandler));
|
||||
myChangesView = backendChangesView;
|
||||
myChangesView.getViewModel().initPanel();
|
||||
|
||||
MessageBusConnection busConnection = myProject.getMessageBus().connect(this);
|
||||
myVcsConfiguration = VcsConfiguration.getInstance(myProject);
|
||||
|
||||
// ChangesViewPanel is used for a singular ChangesViewToolWindowPanel instance. Cleanup is not needed.
|
||||
myView = myChangesPanel.getChangesView();
|
||||
registerShortcuts(this);
|
||||
|
||||
ApplicationManager.getApplication().getMessageBus().connect(project)
|
||||
@@ -372,16 +344,9 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
myCommitPanelSplitter = new ChangesViewCommitPanelSplitter(myProject);
|
||||
Disposer.register(this, myCommitPanelSplitter);
|
||||
myCommitPanelSplitter.setFirstComponent(myChangesPanel);
|
||||
myCommitPanelSplitter.setFirstComponent(backendChangesView.getChangesPanel());
|
||||
|
||||
myContentPanel = new BorderLayoutPanel() {
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return isMinimumSizeSet() || myChangesPanel.isToolbarHorizontal()
|
||||
? super.getMinimumSize()
|
||||
: myChangesPanel.getToolbar().getComponent().getPreferredSize();
|
||||
}
|
||||
};
|
||||
myContentPanel = new BorderLayoutPanel();
|
||||
myContentPanel.addToCenter(myCommitPanelSplitter);
|
||||
myMainPanelContent = new Wrapper(myContentPanel);
|
||||
JPanel mainPanel = simplePanel(myMainPanelContent)
|
||||
@@ -391,17 +356,17 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
Disposer.register(this, myEditorDiffPreview);
|
||||
|
||||
// Override the handlers registered by editorDiffPreview
|
||||
myView.setDoubleClickHandler(e -> {
|
||||
if (EditSourceOnDoubleClickHandler.isToggleEvent(myView, e)) return false;
|
||||
myChangesView.getViewModel().getTree().setDoubleClickHandler(e -> {
|
||||
if (EditSourceOnDoubleClickHandler.isToggleEvent(myChangesView.getViewModel().getTree(), e)) return false;
|
||||
if (performHoverAction()) return true;
|
||||
if (myEditorDiffPreview.handleDoubleClick(e)) return true;
|
||||
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myView), true);
|
||||
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myChangesView.getViewModel().getTree()), true);
|
||||
return true;
|
||||
});
|
||||
myView.setEnterKeyHandler(e -> {
|
||||
myChangesView.getViewModel().getTree().setEnterKeyHandler(e -> {
|
||||
if (performHoverAction()) return true;
|
||||
if (myEditorDiffPreview.handleEnterKey()) return true;
|
||||
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myView), false);
|
||||
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(myChangesView.getViewModel().getTree()), false);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -412,7 +377,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
}
|
||||
|
||||
private boolean performHoverAction() {
|
||||
ChangesBrowserNode<?> selected = VcsTreeModelData.selected(myView).iterateNodes().single();
|
||||
ChangesBrowserNode<?> selected = VcsTreeModelData.selected(myChangesView.getViewModel().getTree()).iterateNodes().single();
|
||||
if (selected == null) return false;
|
||||
|
||||
for (ChangesViewNodeAction extension : ChangesViewNodeAction.EP_NAME.getExtensions(myProject)) {
|
||||
@@ -453,7 +418,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
private class ChangesViewEditorDiffPreview extends TreeHandlerEditorDiffPreview {
|
||||
private ChangesViewEditorDiffPreview() {
|
||||
super(myView, myContentPanel, ChangesViewDiffPreviewHandler.INSTANCE);
|
||||
super(myChangesView.getViewModel().getTree(), myContentPanel, ChangesViewDiffPreviewHandler.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -491,7 +456,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
@Override
|
||||
protected boolean isOpenPreviewWithSingleClick() {
|
||||
if (myChangesPanel.getChangesView().isModelUpdateInProgress()) return false;
|
||||
if (myChangesView.getViewModel().isModelUpdateInProgress()) return false;
|
||||
if (mySplitterDiffPreview != null && myVcsConfiguration.LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN) return false;
|
||||
return super.isOpenPreviewWithSingleClick();
|
||||
}
|
||||
@@ -544,8 +509,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
}
|
||||
|
||||
private ChangesViewDiffPreviewProcessor createDiffPreviewProcessor(boolean isInEditor) {
|
||||
|
||||
ChangesViewDiffPreviewProcessor processor = new ChangesViewDiffPreviewProcessor(myView, isInEditor);
|
||||
ChangesViewDiffPreviewProcessor processor = new ChangesViewDiffPreviewProcessor(myChangesView.getViewModel().getTree(), isInEditor);
|
||||
this.addListener(new Listener() {
|
||||
@Override
|
||||
public void allowExcludeFromCommitChanged() {
|
||||
@@ -583,6 +547,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
myCommitWorkflowHandler = null;
|
||||
myCommitPanel = null;
|
||||
}
|
||||
myChangesView.getViewModel().setCommitWorkflowHandler(myCommitWorkflowHandler);
|
||||
|
||||
myDispatcher.getMulticaster().allowExcludeFromCommitChanged();
|
||||
configureToolbars();
|
||||
@@ -598,12 +563,12 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
|
||||
private void configureToolbars() {
|
||||
boolean isToolbarHorizontal = CommitModeManager.getInstance(myProject).getCurrentCommitMode().useCommitToolWindow();
|
||||
myChangesPanel.setToolbarHorizontal(isToolbarHorizontal);
|
||||
myChangesView.getViewModel().setToolbarHorizontal(isToolbarHorizontal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<AnAction> getActions(boolean originalProvider) {
|
||||
return asList(myChangesPanel.getToolbarActionGroup().getChildren(ActionManager.getInstance()));
|
||||
return myChangesView.getViewModel().getActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -611,7 +576,7 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
super.uiDataSnapshot(sink);
|
||||
sink.set(DiffDataKeys.EDITOR_TAB_DIFF_PREVIEW, myEditorDiffPreview);
|
||||
sink.set(ChangesViewDataKeys.SETTINGS, myChangesViewSettings);
|
||||
sink.set(ChangesViewDataKeys.REFRESHER, () -> scheduleRefreshNow(null));
|
||||
sink.set(ChangesViewDataKeys.REFRESHER, () -> myChangesView.getViewModel().scheduleRefreshNow(null));
|
||||
|
||||
// This makes COMMIT_WORKFLOW_HANDLER available anywhere in "Local Changes" - so commit executor actions are enabled.
|
||||
DataSink.uiDataSnapshot(sink, myCommitPanel);
|
||||
@@ -638,42 +603,6 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
public void scheduleRefresh() {
|
||||
myChangesPanel.scheduleRefresh();
|
||||
}
|
||||
|
||||
private void scheduleRefreshNow(@Nullable @RequiresBackgroundThread Runnable callback) {
|
||||
myChangesPanel.scheduleRefreshNow(callback);
|
||||
}
|
||||
|
||||
public void selectFile(@Nullable VirtualFile vFile) {
|
||||
if (vFile == null) return;
|
||||
|
||||
ChangesBrowserNode<?> node = findNodeForFile(vFile);
|
||||
if (node == null) return;
|
||||
|
||||
TreeUtil.selectNode(myView, node);
|
||||
}
|
||||
|
||||
public void selectChanges(@NotNull List<? extends Change> changes) {
|
||||
List<TreePath> paths = new ArrayList<>();
|
||||
|
||||
for (Change change : changes) {
|
||||
ContainerUtil.addIfNotNull(paths, myView.findNodePathInTree(change));
|
||||
}
|
||||
|
||||
TreeUtil.selectPaths(myView, paths);
|
||||
}
|
||||
|
||||
private @Nullable ChangesBrowserNode<?> findNodeForFile(@NotNull VirtualFile file) {
|
||||
FilePath filePath = VcsUtil.getFilePath(file);
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode)myView.getModel().getRoot();
|
||||
return (ChangesBrowserNode<?>)TreeUtil.findNode(root, node -> {
|
||||
FilePath nodeFilePath = VcsTreeModelData.mapUserObjectToFilePath(node.getUserObject());
|
||||
return Objects.equals(filePath, nodeFilePath);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeLaterIfNeeded(Runnable runnable) {
|
||||
ModalityUiUtil.invokeLaterIfNeeded(ModalityState.nonModal(), myProject.getDisposed(), runnable);
|
||||
}
|
||||
@@ -693,43 +622,6 @@ public class ChangesViewManager implements ChangesViewEx, Disposable {
|
||||
return ChangesViewWorkflowManager.getInstance(myProject).getCommitWorkflowHandler();
|
||||
}
|
||||
|
||||
private static class ModelProvider implements CommitChangesViewWithToolbarPanel.ModelProvider {
|
||||
private final @NotNull Project project;
|
||||
private final @NotNull Supplier<@Nullable ChangesViewCommitWorkflowHandler> commitWorkflowHandlerSupplier;
|
||||
|
||||
private ModelProvider(@NotNull Project project,
|
||||
@NotNull Supplier<@Nullable ChangesViewCommitWorkflowHandler> commitWorkflowHandlerSupplier) {
|
||||
this.project = project;
|
||||
this.commitWorkflowHandlerSupplier = commitWorkflowHandlerSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ExtendedTreeModel getModel(@NotNull ChangesGroupingPolicyFactory grouping) {
|
||||
ChangeListManagerImpl changeListManager = ChangeListManagerImpl.getInstanceImpl(project);
|
||||
List<LocalChangeList> changeLists = changeListManager.getChangeLists();
|
||||
List<FilePath> unversionedFiles = changeListManager.getUnversionedFilesPaths();
|
||||
|
||||
DefaultTreeModel treeModel = ChangesViewUtil.INSTANCE.createTreeModel(
|
||||
project,
|
||||
grouping,
|
||||
changeLists,
|
||||
unversionedFiles,
|
||||
ChangesViewSettings.getInstance(project).getShowIgnored(),
|
||||
() -> isAllowExcludeFromCommit(commitWorkflowHandlerSupplier.get())
|
||||
);
|
||||
return new ExtendedTreeModel(changeLists, unversionedFiles, treeModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeInclusion(@NotNull List<? extends @NotNull LocalChangeList> changeLists,
|
||||
@NotNull List<? extends @NotNull FilePath> unversionedFiles) {
|
||||
ChangesViewCommitWorkflowHandler commitWorkflowHandler = commitWorkflowHandlerSupplier.get();
|
||||
if (commitWorkflowHandler != null) {
|
||||
commitWorkflowHandler.synchronizeInclusion(changeLists, unversionedFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAllowExcludeFromCommit(@Nullable ChangesViewCommitWorkflowHandler handler) {
|
||||
return handler != null && handler.isActive();
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,9 +40,9 @@ internal class ChangesViewWorkflowManagerImpl(
|
||||
val activity = StartUpMeasurer.startActivity("ChangesViewWorkflowManager initialization")
|
||||
|
||||
// ChangesViewPanel can be reused between workflow instances -> should clean up after ourselves
|
||||
val changesPanel = (ChangesViewManager.getInstance(project) as ChangesViewManager).initChangesPanel()
|
||||
val changesView = (ChangesViewManager.getInstance(project) as ChangesViewManager).initChangesView()
|
||||
val workflow = ChangesViewCommitWorkflow(project)
|
||||
val commitPanel = ChangesViewCommitPanel(project, changesPanel.changesView)
|
||||
val commitPanel = ChangesViewCommitPanel(project, changesView.viewModel)
|
||||
_commitWorkflowHandler = ChangesViewCommitWorkflowHandler(workflow, commitPanel)
|
||||
|
||||
project.messageBus.syncPublisher(TOPIC).commitWorkflowChanged()
|
||||
|
||||
@@ -48,10 +48,6 @@ class DummyChangesView implements ChangesViewEx {
|
||||
public void updateProgressComponent(@NotNull List<Supplier<JComponent>> progress) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBusy(boolean b) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGrouping(@NotNull String groupingKey) {
|
||||
}
|
||||
|
||||
+7
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.vcs.changes.ui
|
||||
|
||||
import com.intellij.openapi.project.InitialVfsRefreshService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.FileStatus
|
||||
import com.intellij.openapi.vcs.changes.*
|
||||
@@ -24,6 +25,12 @@ internal class BackendChangesViewModelBuilderService(private val project: Projec
|
||||
|
||||
override fun modifyTreeModelBuilder(modelBuilder: TreeModelBuilder) {
|
||||
val changeListManager = ChangeListManagerImpl.getInstanceImpl(project)
|
||||
val shouldShowUntrackedLoading = changeListManager.unversionedFilesPaths.isEmpty() &&
|
||||
!project.getService(InitialVfsRefreshService::class.java).isInitialVfsRefreshFinished() &&
|
||||
changeListManager.isUnversionedInUpdateMode
|
||||
if (shouldShowUntrackedLoading) {
|
||||
modelBuilder.insertSubtreeRoot(ChangesBrowserUnversionedLoadingPendingNode())
|
||||
}
|
||||
|
||||
modelBuilder.setLocallyDeletedPaths(changeListManager.deletedFiles)
|
||||
.setModifiedWithoutEditing(changeListManager.modifiedWithoutEditing)
|
||||
|
||||
-1
@@ -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())
|
||||
|
||||
+82
-3
@@ -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()) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.vcs.changes
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.changes.ChangesViewId
|
||||
import com.intellij.openapi.vcs.changes.ChangesViewSplitComponentBinding
|
||||
import com.intellij.openapi.vcs.changes.CommitChangesViewWithToolbarPanel
|
||||
import com.intellij.openapi.vcs.changes.LocalChangesListView
|
||||
import com.intellij.platform.kernel.ids.storeValueGlobally
|
||||
import com.intellij.platform.vcs.impl.shared.RdLocalChanges
|
||||
import com.intellij.ui.split.createComponent
|
||||
import com.intellij.vcs.changes.viewModel.BackendCommitChangesViewModel
|
||||
import com.intellij.vcs.changes.viewModel.BackendLocalCommitChangesViewModel
|
||||
import com.intellij.vcs.changes.viewModel.BackendRemoteCommitChangesViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import javax.swing.JComponent
|
||||
|
||||
internal class BackendChangesView private constructor(
|
||||
val changesPanel: JComponent,
|
||||
val viewModel: BackendCommitChangesViewModel,
|
||||
) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(project: Project, scope: CoroutineScope): BackendChangesView {
|
||||
if (RdLocalChanges.isEnabled()) {
|
||||
val viewModel = BackendRemoteCommitChangesViewModel(project)
|
||||
val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType)
|
||||
val panel = ChangesViewSplitComponentBinding.createComponent(project, scope, id)
|
||||
return BackendChangesView(panel, viewModel)
|
||||
}
|
||||
else {
|
||||
val panel = CommitChangesViewWithToolbarPanel(LocalChangesListView(project), scope)
|
||||
val viewModel = BackendLocalCommitChangesViewModel(panel)
|
||||
val backendChangesView = BackendChangesView(panel, viewModel)
|
||||
val id = storeValueGlobally(scope, viewModel, BackendChangesViewValueIdType)
|
||||
panel.id = id
|
||||
return backendChangesView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class ScopeProvider(val cs: CoroutineScope)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user