mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[java, compilation-charts] support compilation charts for JPS and Maven IDEA-346294
GitOrigin-RevId: 1a81a298541f02ae165dfb8327b5899614f1d195
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1d1c6f0f38
commit
2edba4709f
2
.idea/modules.xml
generated
2
.idea/modules.xml
generated
@@ -449,6 +449,8 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/ByteCodeViewer/intellij.java.byteCodeViewer.iml" filepath="$PROJECT_DIR$/plugins/ByteCodeViewer/intellij.java.byteCodeViewer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/openapi/intellij.java.compiler.iml" filepath="$PROJECT_DIR$/java/compiler/openapi/intellij.java.compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/javac2/intellij.java.compiler.antTasks.iml" filepath="$PROJECT_DIR$/java/compiler/javac2/intellij.java.compiler.antTasks.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/charts/intellij.java.compiler.charts.iml" filepath="$PROJECT_DIR$/java/compiler/charts/intellij.java.compiler.charts.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/charts/jps-plugin/intellij.java.compiler.charts.jps.iml" filepath="$PROJECT_DIR$/java/compiler/charts/jps-plugin/intellij.java.compiler.charts.jps.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/impl/intellij.java.compiler.impl.iml" filepath="$PROJECT_DIR$/java/compiler/impl/intellij.java.compiler.impl.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/instrumentation-util/intellij.java.compiler.instrumentationUtil.iml" filepath="$PROJECT_DIR$/java/compiler/instrumentation-util/intellij.java.compiler.instrumentationUtil.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/instrumentation-util-8/intellij.java.compiler.instrumentationUtil.java8.iml" filepath="$PROJECT_DIR$/java/compiler/instrumentation-util-8/intellij.java.compiler.instrumentationUtil.java8.iml" />
|
||||
|
||||
@@ -63,6 +63,7 @@ suspend fun buildCommunityStandaloneJpsBuilder(targetDir: Path,
|
||||
|
||||
|
||||
layout.withModule("intellij.maven.jps", "maven-jps.jar")
|
||||
layout.withModule("intellij.java.compiler.charts.jps", "java-compiler-charts-jps.jar")
|
||||
layout.withModule("intellij.java.aetherDependencyResolver", "aether-dependency-resolver.jar")
|
||||
layout.withModule("intellij.gradle.jps", "gradle-jps.jar")
|
||||
|
||||
|
||||
@@ -218,5 +218,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework.junit5Jimfs" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.yaml.editing" scope="RUNTIME" />
|
||||
<orderEntry type="module" module-name="intellij.platform.whatsNew" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.java.compiler.charts" scope="RUNTIME" />
|
||||
</component>
|
||||
</module>
|
||||
43
java/compiler/charts/intellij.java.compiler.charts.iml
Normal file
43
java/compiler/charts/intellij.java.compiler.charts.iml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module relativePaths="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="5" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false">
|
||||
<compilerSettings>
|
||||
<option name="additionalArguments" value="-Xjvm-default=all -opt-in=com.intellij.openapi.util.IntellijInternalApi" />
|
||||
</compilerSettings>
|
||||
<compilerArguments>
|
||||
<stringArguments>
|
||||
<stringArg name="jvmTarget" arg="11" />
|
||||
<stringArg name="apiVersion" arg="1.9" />
|
||||
<stringArg name="languageVersion" arg="1.9" />
|
||||
</stringArguments>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="intellij.java.compiler.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.build" />
|
||||
<orderEntry type="module" module-name="intellij.platform.extensions" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
|
||||
<orderEntry type="library" name="jackson" level="project" />
|
||||
<orderEntry type="library" name="jackson-databind" level="project" />
|
||||
<orderEntry type="library" name="rd-core" level="project" />
|
||||
<orderEntry type="library" name="rd-swing" level="project" />
|
||||
<orderEntry type="library" name="kotlinx-coroutines-core" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.rd.community" />
|
||||
<orderEntry type="library" name="kotlinx-collections-immutable" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.java.compiler.charts.jps" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core.ui" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module relativePaths="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.build" />
|
||||
<orderEntry type="library" name="gson" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.model" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,2 @@
|
||||
# Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
com.intellij.compiler.charts.jps.ChartsBuilderService
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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.compiler.charts.jps;
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.ModuleChunk;
|
||||
import org.jetbrains.jps.builders.DirtyFilesHolder;
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
|
||||
import org.jetbrains.jps.incremental.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ChartsBuilderService extends BuilderService {
|
||||
public static String COMPILATION_STATISTIC_BUILDER_ID = "jps.compile.statistic";
|
||||
|
||||
@Override
|
||||
public @NotNull List<? extends ModuleLevelBuilder> createModuleLevelBuilders() {
|
||||
return List.of(new ChartsModuleLevelBuilder());
|
||||
}
|
||||
|
||||
private static class ChartsModuleLevelBuilder extends ModuleLevelBuilder {
|
||||
private ScheduledFuture<?> myStatisticsReporter = null;
|
||||
private Runnable myStatisticsRunnable = null;
|
||||
|
||||
protected ChartsModuleLevelBuilder() {
|
||||
super(BuilderCategory.TRANSLATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitCode build(@NotNull CompileContext context,
|
||||
@NotNull ModuleChunk chunk,
|
||||
@NotNull DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
|
||||
@NotNull OutputConsumer outputConsumer) throws ProjectBuildException, IOException {
|
||||
return ExitCode.NOTHING_DONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> getCompilableFileExtensions() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@Override
|
||||
public @NotNull String getPresentableName() {
|
||||
return StringUtil.capitalize(getBuilderName());
|
||||
}
|
||||
|
||||
public static @NotNull @NlsSafe String getBuilderName() {
|
||||
return "charts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildStarted(@NotNull CompileContext context) {
|
||||
final MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
|
||||
final OperatingSystemMXBean os = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
|
||||
|
||||
myStatisticsRunnable = () -> context.processMessage(CompileStatisticBuilderMessage.create(memory, os));
|
||||
myStatisticsReporter = AppExecutorUtil.createBoundedScheduledExecutorService("IncProjectBuilder metrics reporter", 1)
|
||||
.scheduleWithFixedDelay(myStatisticsRunnable, 0, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildFinished(@NotNull CompileContext context) {
|
||||
if (myStatisticsRunnable != null) {
|
||||
myStatisticsRunnable.run();
|
||||
myStatisticsRunnable = null;
|
||||
}
|
||||
if (myStatisticsReporter != null) {
|
||||
myStatisticsReporter.cancel(true);
|
||||
myStatisticsReporter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkBuildStarted(@NotNull CompileContext context, @NotNull ModuleChunk chunk) {
|
||||
context.processMessage(CompileStatisticBuilderMessage.create(chunk.getTargets(), "STARTED"));
|
||||
if (myStatisticsRunnable != null) myStatisticsRunnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkBuildFinished(@NotNull CompileContext context, @NotNull ModuleChunk chunk) {
|
||||
context.processMessage(CompileStatisticBuilderMessage.create(chunk.getTargets(), "FINISHED"));
|
||||
if (myStatisticsRunnable != null) myStatisticsRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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.compiler.charts.jps;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.builders.BuildTarget;
|
||||
import org.jetbrains.jps.builders.ModuleBasedTarget;
|
||||
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CustomBuilderMessage;
|
||||
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.intellij.compiler.charts.jps.ChartsBuilderService.COMPILATION_STATISTIC_BUILDER_ID;
|
||||
|
||||
public class CompileStatisticBuilderMessage extends CustomBuilderMessage {
|
||||
private static final Gson JSON = new Gson();
|
||||
|
||||
private CompileStatisticBuilderMessage(@NotNull String messageType, @NotNull String data) {
|
||||
super(COMPILATION_STATISTIC_BUILDER_ID, messageType, data);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CompileStatisticBuilderMessage create(@NotNull Set<? extends BuildTarget<?>> targets,
|
||||
@NotNull String event) {
|
||||
List<CompileStatisticBuilderMessage.TargetEvent>
|
||||
events = ContainerUtil.map(targets, target -> map(target, event.equals("STARTED")
|
||||
? CompileStatisticBuilderMessage.StartTarget::new
|
||||
: CompileStatisticBuilderMessage.FinishTarget::new));
|
||||
return new CompileStatisticBuilderMessage(event, JSON.toJson(events));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <T extends CompileStatisticBuilderMessage.TargetEvent> T map(@NotNull BuildTarget<?> target,
|
||||
@NotNull Supplier<T> event) {
|
||||
T data = event.get();
|
||||
data.name = target instanceof ModuleBasedTarget
|
||||
? ((ModuleBasedTarget<?>)target).getModule().getName() :
|
||||
target.getId();
|
||||
data.type = target.getTargetType().getTypeId();
|
||||
data.isFileBased = target.getTargetType().isFileBased();
|
||||
data.isTest = target.getTargetType() instanceof JavaModuleBuildTargetType &&
|
||||
((JavaModuleBuildTargetType)target.getTargetType()).isTests();
|
||||
return data;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static BuildMessage create(@NotNull MemoryMXBean memory, @NotNull OperatingSystemMXBean os) {
|
||||
CompileStatisticBuilderMessage.CpuMemoryStatistics
|
||||
statistics = new CompileStatisticBuilderMessage.CpuMemoryStatistics();
|
||||
statistics.heapUsed = Math.max(memory.getHeapMemoryUsage().getUsed(), 0);
|
||||
statistics.heapMax = Math.max(memory.getHeapMemoryUsage().getMax(), 0);
|
||||
statistics.nonHeapUsed = Math.max(memory.getNonHeapMemoryUsage().getUsed(), 0);
|
||||
statistics.nonHeapMax = Math.max(memory.getNonHeapMemoryUsage().getMax(), 0);
|
||||
statistics.cpu = Math.max(os.getProcessCpuLoad() * 100, 0);
|
||||
return new CompileStatisticBuilderMessage("STATISTIC", JSON.toJson(statistics));
|
||||
}
|
||||
|
||||
public static abstract class TargetEvent {
|
||||
public String name;
|
||||
public String type;
|
||||
public boolean isTest;
|
||||
public boolean isFileBased;
|
||||
public long time = System.nanoTime();
|
||||
public long thread = Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public static class StartTarget extends CompileStatisticBuilderMessage.TargetEvent {
|
||||
}
|
||||
|
||||
public static class FinishTarget extends CompileStatisticBuilderMessage.TargetEvent {
|
||||
}
|
||||
|
||||
public static class CpuMemoryStatistics {
|
||||
public long heapUsed;
|
||||
public long heapMax;
|
||||
public long nonHeapUsed;
|
||||
public long nonHeapMax;
|
||||
public double cpu;
|
||||
public long time = System.nanoTime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<idea-plugin package="com.intellij.compiler.charts">
|
||||
<module value="intellij.java.compiler.charts" />
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<postStartupActivity implementation="com.intellij.compiler.charts.CompilationChartsProjectActivity"/>
|
||||
<compileServer.plugin classpath="jps/java-compiler-charts-jps.jar"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,8 @@
|
||||
charts.module.info=<html> <body style='padding: 5px;'> <b>{0}</b> <br /> <b>duration</b> {1} </body> </html>
|
||||
charts.tab.name=Chart\u2026
|
||||
charts.module=Module
|
||||
charts.reset=Reset
|
||||
charts.production.type=Production
|
||||
charts.test.type=Test
|
||||
charts.memory.type=Memory
|
||||
charts.cpu.type=CPU
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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.compiler.charts;
|
||||
|
||||
import com.intellij.DynamicBundle;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
public final class CompilationChartsBundle {
|
||||
public static final @NonNls String BUNDLE = "messages.CompilationChartsBundle";
|
||||
private static final DynamicBundle INSTANCE = new DynamicBundle(CompilationChartsBundle.class, BUNDLE);
|
||||
|
||||
private CompilationChartsBundle() {}
|
||||
|
||||
public static @NotNull @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
|
||||
return INSTANCE.getMessage(key, params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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.compiler.charts
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.intellij.build.BuildProgressListener
|
||||
import com.intellij.build.BuildViewManager
|
||||
import com.intellij.build.events.FinishBuildEvent
|
||||
import com.intellij.build.events.StartBuildEvent
|
||||
import com.intellij.compiler.charts.jps.ChartsBuilderService.COMPILATION_STATISTIC_BUILDER_ID
|
||||
import com.intellij.compiler.charts.jps.CompileStatisticBuilderMessage.*
|
||||
import com.intellij.compiler.charts.ui.CompilationChartsBuildEvent
|
||||
import com.intellij.compiler.server.CustomBuilderMessageHandler
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.util.messages.MessageBusConnection
|
||||
import java.util.*
|
||||
|
||||
class CompilationChartsProjectActivity : ProjectActivity {
|
||||
companion object {
|
||||
private val LOG: Logger = Logger.getInstance(CompilationChartsProjectActivity::class.java)
|
||||
}
|
||||
|
||||
override suspend fun execute(project: Project) {
|
||||
|
||||
val view = project.getService(BuildViewManager::class.java)
|
||||
val disposable = Disposer.newDisposable(view, "Compilation charts event listener disposable")
|
||||
|
||||
val connection: MessageBusConnection = project.messageBus.connect()
|
||||
val handler = CompilationChartsMessageHandler()
|
||||
connection.subscribe(CustomBuilderMessageHandler.TOPIC, handler)
|
||||
|
||||
view.addListener(BuildProgressListener { buildId, event ->
|
||||
when (event) {
|
||||
is StartBuildEvent -> {
|
||||
val title = event.buildDescriptor.title.lowercase()
|
||||
if (title.contains("up-to-date") || title.startsWith("worksheet")) return@BuildProgressListener
|
||||
|
||||
val chartEvent = CompilationChartsBuildEvent(buildId)
|
||||
view.onEvent(buildId, chartEvent)
|
||||
handler.addState(chartEvent.vm());
|
||||
}
|
||||
is FinishBuildEvent -> handler.removeState()
|
||||
}
|
||||
}, disposable)
|
||||
}
|
||||
|
||||
private class CompilationChartsMessageHandler : CustomBuilderMessageHandler {
|
||||
private val json = ObjectMapper(JsonFactory())
|
||||
private val states: Queue<CompilationChartsViewModel> = ArrayDeque()
|
||||
private var currentState: CompilationChartsViewModel? = null
|
||||
|
||||
fun addState(vm: CompilationChartsViewModel) {
|
||||
states.add(vm)
|
||||
if (currentState == null) removeState()
|
||||
}
|
||||
|
||||
fun removeState() {
|
||||
currentState = states.poll()
|
||||
}
|
||||
|
||||
override fun messageReceived(builderId: String?, messageType: String?, messageText: String?) {
|
||||
if (builderId != COMPILATION_STATISTIC_BUILDER_ID) return
|
||||
try {
|
||||
when (messageType) {
|
||||
"STARTED" -> {
|
||||
val values = json.readValue(messageText, object : TypeReference<List<StartTarget>>() {})
|
||||
currentState?.started(values)
|
||||
}
|
||||
"FINISHED" -> {
|
||||
val values = json.readValue(messageText, object : TypeReference<List<FinishTarget>>() {})
|
||||
currentState?.finished(values)
|
||||
}
|
||||
"STATISTIC" -> {
|
||||
val value = json.readValue(messageText, CpuMemoryStatistics::class.java)
|
||||
currentState?.statistic(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: JsonProcessingException) {
|
||||
LOG.warn("Failed to parse message: $messageText", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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.compiler.charts
|
||||
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel.Modules.EventKey
|
||||
import com.intellij.compiler.charts.jps.CompileStatisticBuilderMessage.*
|
||||
import com.jetbrains.rd.framework.impl.RdList
|
||||
import com.jetbrains.rd.framework.impl.RdMap
|
||||
import com.jetbrains.rd.framework.impl.RdProperty
|
||||
import com.jetbrains.rd.util.lifetime.Lifetime
|
||||
import com.jetbrains.rd.util.reactive.IViewableMap
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.Predicate
|
||||
|
||||
class CompilationChartsViewModel(val lifetime: Lifetime) {
|
||||
val modules: Modules = Modules(Long.MAX_VALUE, 0, 0, RdMap())
|
||||
val statistics: Statistics = Statistics()
|
||||
val cpuMemory: RdProperty<CpuMemoryStatisticsType> = RdProperty(CpuMemoryStatisticsType.MEMORY)
|
||||
val filter: RdProperty<Filter> = RdProperty(Filter())
|
||||
|
||||
private val threadIndexes: MutableMap<Long, Int> = ConcurrentHashMap()
|
||||
fun started(values: List<StartTarget>) {
|
||||
values.forEach { value ->
|
||||
modules.add(Modules.StartEvent(value, index(value.thread)))
|
||||
}
|
||||
}
|
||||
|
||||
fun finished(values: List<FinishTarget>) {
|
||||
values.forEach { value ->
|
||||
modules.add(Modules.FinishEvent(value, index(value.thread)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun index(threadId: Long): Int {
|
||||
var index: Int? = threadIndexes[threadId]
|
||||
if (index != null) return index
|
||||
synchronized(threadIndexes) {
|
||||
index = threadIndexes[threadId]
|
||||
if (index != null) return index!!
|
||||
|
||||
index = threadIndexes.size
|
||||
threadIndexes[threadId] = index!!
|
||||
return index!!
|
||||
}
|
||||
}
|
||||
|
||||
fun statistic(value: CpuMemoryStatistics) {
|
||||
if (statistics.maxMemory < value.heapUsed) statistics.maxMemory = value.heapUsed
|
||||
if (statistics.start > value.time) statistics.start = value.time
|
||||
if (statistics.end < value.time) statistics.end = value.time
|
||||
|
||||
statistics.cpu.add(StatisticData(value.time, value.cpu.toLong()))
|
||||
statistics.memoryMax.add(StatisticData(value.time, value.heapMax))
|
||||
statistics.memoryUsed.add(StatisticData(value.time, value.heapUsed))
|
||||
}
|
||||
|
||||
data class Modules(var start: Long, var end: Long, var threadCount: Int, private val events: RdMap<EventKey, PersistentList<Event>>) {
|
||||
fun add(event: Event) {
|
||||
if (start > event.target.time) start = event.target.time
|
||||
if (end < event.target.time) end = event.target.time
|
||||
if (threadCount <= event.threadNumber) threadCount = event.threadNumber + 1
|
||||
|
||||
events.compute(event.key) { _, list ->
|
||||
list?.add(event) ?: persistentListOf(event)
|
||||
}
|
||||
}
|
||||
|
||||
fun get(): IViewableMap<EventKey, List<Event>> = events
|
||||
|
||||
interface Event {
|
||||
val target: TargetEvent
|
||||
val threadNumber: Int
|
||||
|
||||
val key: EventKey
|
||||
get() = EventKey(target.name, target.type, target.isTest)
|
||||
}
|
||||
|
||||
data class EventKey(val name: String, val type: String, val test: Boolean)
|
||||
data class StartEvent(override val target: StartTarget, override val threadNumber: Int) : Event
|
||||
data class FinishEvent(override val target: FinishTarget, override val threadNumber: Int) : Event
|
||||
}
|
||||
|
||||
data class StatisticData(val time: Long, val data: Long) : Comparable<StatisticData> {
|
||||
override fun compareTo(other: StatisticData): Int = time.compareTo(other.time)
|
||||
}
|
||||
|
||||
data class Statistics(val memoryUsed: RdList<StatisticData> = RdList(),
|
||||
val memoryMax: RdList<StatisticData> = RdList(),
|
||||
val cpu: RdList<StatisticData> = RdList(),
|
||||
var maxMemory: Long = 0,
|
||||
var start: Long = Long.MAX_VALUE,
|
||||
var end: Long = 0)
|
||||
|
||||
data class ViewModules(var filter: Predicate<EventKey> = Predicate<EventKey> { _ -> true },
|
||||
val data: MutableMap<EventKey, List<Modules.Event>> = ConcurrentHashMap()) {
|
||||
fun data(): Map<EventKey, List<Modules.Event>> = data.filter { filter.test(it.key) }
|
||||
}
|
||||
|
||||
data class Filter(val text: List<String> = listOf(), val production: Boolean = true, val test: Boolean = true) : Predicate<EventKey> {
|
||||
fun setText(text: List<String>): Filter = Filter(text, production, test)
|
||||
fun setProduction(production: Boolean): Filter = Filter(text, production, test)
|
||||
fun setTest(test: Boolean): Filter = Filter(text, production, test)
|
||||
|
||||
override fun test(key: EventKey): Boolean {
|
||||
if (text.isNotEmpty()) {
|
||||
if (!text.all { key.name.contains(it) }) return false
|
||||
}
|
||||
|
||||
if (key.test) {
|
||||
if (!test) return false
|
||||
}
|
||||
else {
|
||||
if (!production) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
enum class CpuMemoryStatisticsType {
|
||||
CPU, MEMORY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import com.intellij.compiler.charts.CompilationChartsBundle
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.ui.popup.IconButton
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBPanel
|
||||
import com.intellij.ui.components.JBTextField
|
||||
import com.intellij.ui.components.fields.ExtendableTextComponent
|
||||
import com.intellij.ui.components.fields.ExtendableTextField
|
||||
import com.intellij.ui.scale.JBUIScale.scale
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Dimension
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JPanel
|
||||
|
||||
|
||||
class ActionPanel(private val vm: CompilationChartsViewModel) : BorderLayoutPanel() {
|
||||
private val searchField: JBTextField = object : ExtendableTextField() {
|
||||
val reset = Runnable {
|
||||
vm.filter.set(vm.filter.value.setText(listOf()))
|
||||
text = ""
|
||||
}
|
||||
init {
|
||||
setExtensions(object : ExtendableTextComponent.Extension {
|
||||
override fun getIcon(hovered: Boolean) = AllIcons.Actions.Search
|
||||
override fun isIconBeforeText() = true
|
||||
override fun getIconGap() = scale(6)
|
||||
}, object : ExtendableTextComponent.Extension {
|
||||
override fun getIcon(hovered: Boolean) = IconButton(
|
||||
CompilationChartsBundle.message("charts.reset"),
|
||||
AllIcons.Actions.Close,
|
||||
AllIcons.Actions.CloseHovered
|
||||
)
|
||||
|
||||
override fun isIconBeforeText() = false
|
||||
override fun getIconGap() = scale(6)
|
||||
override fun getActionOnClick() = reset
|
||||
})
|
||||
|
||||
addActionListener { _ ->
|
||||
val words = this.text.split(" ").filter { it.isNotBlank() }.map { it.trim() }
|
||||
if (words.isEmpty()) {
|
||||
vm.filter.set(vm.filter.value.setText(listOf()))
|
||||
}
|
||||
else {
|
||||
vm.filter.set(vm.filter.value.setText(words))
|
||||
}
|
||||
}
|
||||
addKeyListener(object : KeyAdapter() {
|
||||
override fun keyPressed(e: KeyEvent) {
|
||||
if (e.keyCode == KeyEvent.VK_ESCAPE) reset.run()
|
||||
}
|
||||
})
|
||||
border = JBUI.Borders.empty(1)
|
||||
BoxLayout(this, BoxLayout.LINE_AXIS)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
border = JBUI.Borders.emptyBottom(2)
|
||||
layout = BorderLayout()
|
||||
|
||||
// module name
|
||||
addToLeft(JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.LINE_AXIS)
|
||||
border = JBUI.Borders.empty(2)
|
||||
add(JBLabel(CompilationChartsBundle.message("charts.module")))
|
||||
add(searchField)
|
||||
})
|
||||
|
||||
// legend
|
||||
addToRight(JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.LINE_AXIS)
|
||||
border = JBUI.Borders.empty(2)
|
||||
|
||||
add(JBPanel<JBPanel<*>>(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
val block = JBLabel().apply {
|
||||
preferredSize = Dimension(10, 10)
|
||||
isOpaque = true
|
||||
background = COLOR_PRODUCTION_BLOCK
|
||||
border = BorderFactory.createLineBorder(COLOR_PRODUCTION_BORDER, 1)
|
||||
}
|
||||
add(block)
|
||||
add(JBLabel(CompilationChartsBundle.message("charts.production.type")))
|
||||
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseEntered(e: MouseEvent) {
|
||||
block.border = BorderFactory.createLineBorder(COLOR_PRODUCTION_BORDER_SELECTED, 1)
|
||||
}
|
||||
|
||||
override fun mouseExited(e: MouseEvent) {
|
||||
block.border = BorderFactory.createLineBorder(COLOR_PRODUCTION_BORDER, 1)
|
||||
}
|
||||
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
vm.filter.set(vm.filter.value.setProduction(!vm.filter.value.production))
|
||||
if (vm.filter.value.production)
|
||||
block.background = COLOR_PRODUCTION_BLOCK
|
||||
else
|
||||
block.background = COLOR_PRODUCTION_BLOCK_DISABLED
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
add(JBPanel<JBPanel<*>>(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
val block = JBLabel().apply {
|
||||
preferredSize = Dimension(10, 10)
|
||||
isOpaque = true
|
||||
background = COLOR_TEST_BLOCK
|
||||
border = BorderFactory.createLineBorder(COLOR_TEST_BORDER)
|
||||
}
|
||||
add(block)
|
||||
add(JBLabel(CompilationChartsBundle.message("charts.test.type")))
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseEntered(e: MouseEvent) {
|
||||
block.border = BorderFactory.createLineBorder(COLOR_TEST_BORDER_SELECTED, 1)
|
||||
}
|
||||
|
||||
override fun mouseExited(e: MouseEvent) {
|
||||
block.border = BorderFactory.createLineBorder(COLOR_TEST_BORDER, 1)
|
||||
}
|
||||
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
vm.filter.set(vm.filter.value.setTest(!vm.filter.value.test))
|
||||
if (vm.filter.value.test)
|
||||
block.background = COLOR_TEST_BLOCK
|
||||
else
|
||||
block.background = COLOR_TEST_BLOCK_DISABLED
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
add(JBPanel<JBPanel<*>>(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
val block = JBLabel().apply {
|
||||
preferredSize = Dimension(10, 2)
|
||||
isOpaque = true
|
||||
background = COLOR_MEMORY_BORDER
|
||||
}
|
||||
val label = JBLabel(CompilationChartsBundle.message("charts.memory.type"))
|
||||
|
||||
add(block)
|
||||
add(label)
|
||||
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
when(vm.cpuMemory.value) {
|
||||
CompilationChartsViewModel.CpuMemoryStatisticsType.MEMORY -> {
|
||||
label.text = CompilationChartsBundle.message("charts.cpu.type")
|
||||
block.background = COLOR_CPU_BORDER
|
||||
vm.cpuMemory.set(CompilationChartsViewModel.CpuMemoryStatisticsType.CPU)
|
||||
}
|
||||
CompilationChartsViewModel.CpuMemoryStatisticsType.CPU -> {
|
||||
label.text = CompilationChartsBundle.message("charts.memory.type")
|
||||
block.background = COLOR_MEMORY_BORDER
|
||||
vm.cpuMemory.set(CompilationChartsViewModel.CpuMemoryStatisticsType.MEMORY)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import com.intellij.build.events.BuildEventPresentationData
|
||||
import com.intellij.build.events.PresentableBuildEvent
|
||||
import com.intellij.build.events.impl.AbstractBuildEvent
|
||||
import com.intellij.compiler.charts.CompilationChartsBundle
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel
|
||||
import com.intellij.execution.ui.ExecutionConsole
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.ActionGroup
|
||||
import com.intellij.openapi.rd.createLifetime
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JComponent
|
||||
|
||||
class CompilationChartsBuildEvent(buildId: Any) :
|
||||
AbstractBuildEvent(Any(), buildId, System.currentTimeMillis(), CompilationChartsBundle.message("charts.tab.name")),
|
||||
PresentableBuildEvent {
|
||||
|
||||
private val console: CompilationChartsExecutionConsole by lazy { CompilationChartsExecutionConsole() }
|
||||
|
||||
override fun getPresentationData(): BuildEventPresentationData = CompilationChartsPresentationData(console)
|
||||
|
||||
fun vm(): CompilationChartsViewModel = console.vm
|
||||
|
||||
private class CompilationChartsPresentationData(private val component: ExecutionConsole) : BuildEventPresentationData {
|
||||
override fun getNodeIcon(): Icon = AllIcons.Actions.Profile
|
||||
|
||||
override fun getExecutionConsole(): ExecutionConsole = component
|
||||
|
||||
override fun consoleToolbarActions(): ActionGroup? = null
|
||||
}
|
||||
|
||||
private class CompilationChartsExecutionConsole : ExecutionConsole {
|
||||
val vm: CompilationChartsViewModel = CompilationChartsViewModel(this.createLifetime())
|
||||
private val _component: JComponent by lazy {
|
||||
CompilationChartsView(vm)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
override fun getComponent(): JComponent = _component
|
||||
override fun getPreferredFocusableComponent(): JComponent = _component
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel.CpuMemoryStatisticsType
|
||||
import com.intellij.ide.ui.UISettings.Companion.setupAntialiasing
|
||||
import com.intellij.ui.components.JBPanelWithEmptyText
|
||||
import com.intellij.ui.table.JBTable
|
||||
import java.awt.Dimension
|
||||
import java.awt.Graphics
|
||||
import java.awt.Graphics2D
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
import javax.swing.JViewport
|
||||
import kotlin.math.exp
|
||||
|
||||
|
||||
class CompilationChartsDiagramsComponent(private val vm: CompilationChartsViewModel,
|
||||
private val getViewport: () -> JViewport) : JBPanelWithEmptyText() {
|
||||
companion object {
|
||||
val ROW_HEIGHT = JBTable().rowHeight * 1.5
|
||||
}
|
||||
|
||||
var modules: CompilationChartsViewModel.ViewModules = CompilationChartsViewModel.ViewModules()
|
||||
var cpu: MutableSet<CompilationChartsViewModel.StatisticData> = ConcurrentSkipListSet()
|
||||
var memory: MutableSet<CompilationChartsViewModel.StatisticData> = ConcurrentSkipListSet()
|
||||
var statistic: Statistic = Statistic()
|
||||
var cpuMemory: CpuMemoryStatisticsType = CpuMemoryStatisticsType.MEMORY
|
||||
private val mouseAdapter: CompilationChartsMouseAdapter
|
||||
|
||||
private var zoom: Zoom = Zoom()
|
||||
|
||||
init {
|
||||
addMouseWheelListener { e ->
|
||||
if (e.isControlDown) {
|
||||
zoom.adjustUser(getViewport(), e.x, exp(e.preciseWheelRotation * -0.05))
|
||||
this@CompilationChartsDiagramsComponent.repaint()
|
||||
}
|
||||
else {
|
||||
e.component.parent.dispatchEvent(e)
|
||||
}
|
||||
}
|
||||
|
||||
mouseAdapter = CompilationChartsMouseAdapter(vm, this)
|
||||
addMouseListener(mouseAdapter)
|
||||
}
|
||||
|
||||
override fun paintComponent(g2d: Graphics) {
|
||||
if (g2d !is Graphics2D) return
|
||||
|
||||
charts(vm, zoom, getViewport()) {
|
||||
progress {
|
||||
model = modules.data()
|
||||
height = ROW_HEIGHT
|
||||
threads = statistic.threadCount
|
||||
|
||||
block {
|
||||
border = MODULE_BLOCK_BORDER
|
||||
padding = MODULE_BLOCK_PADDING
|
||||
color = { m -> if (m.target.isTest) COLOR_TEST_BLOCK else COLOR_PRODUCTION_BLOCK }
|
||||
outline = { m -> if (m.target.isTest) COLOR_TEST_BORDER else COLOR_PRODUCTION_BORDER }
|
||||
selected = { m -> if (m.target.isTest) COLOR_TEST_BORDER_SELECTED else COLOR_PRODUCTION_BORDER_SELECTED }
|
||||
}
|
||||
background {
|
||||
color = { row -> if (row % 2 == 0) COLOR_BACKGROUND_EVEN else COLOR_BACKGROUND_ODD }
|
||||
}
|
||||
}
|
||||
usage {
|
||||
when (cpuMemory) {
|
||||
CpuMemoryStatisticsType.MEMORY -> {
|
||||
model = memory
|
||||
maximum = statistic.maxMemory
|
||||
unit = "MB"
|
||||
color {
|
||||
background = COLOR_MEMORY
|
||||
border = COLOR_MEMORY_BORDER
|
||||
}
|
||||
}
|
||||
CpuMemoryStatisticsType.CPU -> {
|
||||
model = cpu
|
||||
maximum = statistic.maxCpu
|
||||
unit = "%"
|
||||
color {
|
||||
background = COLOR_CPU
|
||||
border = COLOR_CPU_BORDER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
axis {
|
||||
stroke = floatArrayOf(5f, 5f)
|
||||
distance = AXIS_DISTANCE_PX
|
||||
count = AXIS_MARKERS_COUNT
|
||||
height = ROW_HEIGHT
|
||||
padding = AXIS_TEXT_PADDING
|
||||
}
|
||||
settings {
|
||||
font {
|
||||
size = FONT_SIZE
|
||||
color = COLOR_TEXT
|
||||
}
|
||||
duration {
|
||||
from = statistic.start
|
||||
to = statistic.end
|
||||
}
|
||||
background = COLOR_BACKGROUND
|
||||
line {
|
||||
color = COLOR_LINE
|
||||
}
|
||||
mouse = mouseAdapter
|
||||
}
|
||||
}.draw(g2d) {
|
||||
setupAntialiasing(g2d) // ??
|
||||
val size = Dimension(width().toInt(), height().toInt())
|
||||
if (size != this@CompilationChartsDiagramsComponent.preferredSize) {
|
||||
this@CompilationChartsDiagramsComponent.preferredSize = size
|
||||
this@CompilationChartsDiagramsComponent.revalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel
|
||||
import com.intellij.ui.components.JBScrollPane
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import com.jetbrains.rd.util.reactive.IViewableList
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.swing.JViewport
|
||||
import javax.swing.ScrollPaneConstants
|
||||
|
||||
class CompilationChartsView(private val vm: CompilationChartsViewModel) : BorderLayoutPanel() {
|
||||
init {
|
||||
val init = Initialization()
|
||||
val diagrams = CompilationChartsDiagramsComponent(vm, init.init())
|
||||
val scroll = JBScrollPane(diagrams).apply {
|
||||
horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
|
||||
verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
|
||||
border = JBUI.Borders.empty()
|
||||
viewport.scrollMode = JViewport.SIMPLE_SCROLL_MODE
|
||||
name = "compilation-charts-scroll-pane"
|
||||
init.set(this)
|
||||
}
|
||||
|
||||
val actionPanel = ActionPanel(vm)
|
||||
addToTop(actionPanel)
|
||||
addToCenter(scroll)
|
||||
|
||||
vm.modules.get().advise(vm.lifetime) { module ->
|
||||
module.newValueOpt?.let { data ->
|
||||
diagrams.modules.data[module.key] = data
|
||||
} ?: diagrams.modules.data.remove(module.key)
|
||||
|
||||
diagrams.statistic.time(vm.modules.start)
|
||||
diagrams.statistic.time(vm.modules.end)
|
||||
diagrams.statistic.thread(vm.modules.threadCount)
|
||||
}
|
||||
|
||||
vm.statistics.cpu.advise(vm.lifetime) { statistics ->
|
||||
if (statistics !is IViewableList.Event.Add) return@advise
|
||||
diagrams.cpu.add(statistics.newValue)
|
||||
diagrams.statistic.time(statistics.newValueOpt?.time)
|
||||
diagrams.statistic.cpu(statistics.newValueOpt?.data)
|
||||
|
||||
if (vm.cpuMemory.value == CompilationChartsViewModel.CpuMemoryStatisticsType.CPU) {
|
||||
scroll.revalidate()
|
||||
scroll.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
vm.statistics.memoryUsed.advise(vm.lifetime) { statistics ->
|
||||
if (statistics !is IViewableList.Event.Add) return@advise
|
||||
diagrams.memory.add(statistics.newValue)
|
||||
diagrams.statistic.memory(statistics.newValueOpt?.data)
|
||||
diagrams.statistic.time(statistics.newValueOpt?.time)
|
||||
|
||||
if (vm.cpuMemory.value == CompilationChartsViewModel.CpuMemoryStatisticsType.MEMORY) {
|
||||
scroll.revalidate()
|
||||
scroll.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
vm.filter.advise(vm.lifetime) { filter ->
|
||||
diagrams.modules.filter = filter
|
||||
|
||||
scroll.revalidate()
|
||||
scroll.repaint()
|
||||
}
|
||||
|
||||
vm.cpuMemory.advise(vm.lifetime) { filter ->
|
||||
diagrams.cpuMemory = filter
|
||||
|
||||
scroll.revalidate()
|
||||
scroll.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private class Initialization {
|
||||
val scroll: AtomicReference<JBScrollPane> = AtomicReference<JBScrollPane>()
|
||||
fun init(): () -> JViewport = { scroll.get().viewport }
|
||||
fun set(scroll: JBScrollPane) = this.scroll.set(scroll)
|
||||
}
|
||||
}
|
||||
|
||||
data class Statistic(var start: Long, var end: Long, var maxMemory: Long, var threadCount: Int, var maxCpu: Long) {
|
||||
constructor() : this(Long.MAX_VALUE, 0, 0, 0, 0)
|
||||
|
||||
fun time(time: Long?) {
|
||||
if (time == null) return
|
||||
if (start > time) start = time
|
||||
if (end < time) end = time
|
||||
}
|
||||
|
||||
fun memory(memory: Long?) {
|
||||
if (memory == null) return
|
||||
if (maxMemory < memory) maxMemory = memory
|
||||
}
|
||||
|
||||
fun cpu(cpu: Long?) {
|
||||
if (cpu == null) return
|
||||
if (maxCpu < cpu) maxCpu = cpu
|
||||
}
|
||||
|
||||
fun thread(count: Int) {
|
||||
if (threadCount < count) threadCount = count
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import java.awt.Point
|
||||
import javax.swing.JViewport
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class Zoom {
|
||||
private var userScale = -1.0
|
||||
private var dynamicScale = 0.0
|
||||
|
||||
private var correctionDuration: Long = 0
|
||||
private var lastCorrectionTime: Long = System.currentTimeMillis()
|
||||
|
||||
constructor() {
|
||||
this.dynamicScale = 5.5e7 / NANOS
|
||||
}
|
||||
|
||||
constructor(seconds: Double) {
|
||||
this.userScale = 5.5e7 / (seconds * NANOS)
|
||||
}
|
||||
|
||||
fun toPixels(duration: Long): Double = toPixels(duration.toDouble(), scale())
|
||||
fun toPixels(duration: Double): Double = toPixels(duration, scale())
|
||||
fun toPixels(duration: Double, scale: Double): Double = (duration / NANOS) * scale
|
||||
|
||||
fun toDuration(pixels: Int): Long = toDuration(pixels.toDouble(), scale())
|
||||
private fun toDuration(pixels: Double, scale: Double): Long = Math.round(pixels / scale * NANOS)
|
||||
|
||||
fun adjustUser(viewport: JViewport, xPosition: Int, delta: Double) {
|
||||
if (lastCorrectionTime + 50 < System.currentTimeMillis()) correctionDuration = 0
|
||||
val localX = xPosition - viewport.viewPosition.x
|
||||
|
||||
val currentTimeUnderCursor = toDuration(xPosition) + correctionDuration
|
||||
|
||||
userScale = scale() * delta
|
||||
|
||||
val newViewPositionX = toPixels(currentTimeUnderCursor) - localX
|
||||
val correctedViewPositionX = correctedViewPosition(newViewPositionX, localX, currentTimeUnderCursor)
|
||||
|
||||
correctionDuration = currentTimeUnderCursor - toDuration(correctedViewPositionX + localX)
|
||||
lastCorrectionTime = System.currentTimeMillis()
|
||||
|
||||
viewport.viewPosition = Point(correctedViewPositionX, viewport.viewPosition.y)
|
||||
}
|
||||
|
||||
private fun correctedViewPosition(newPosition: Double, x: Int, duration: Long): Int {
|
||||
val correctedX = Math.round(max(0.0, newPosition)).toInt()
|
||||
val index = listOf(abs(duration - toDuration(correctedX + x)),
|
||||
abs(duration - toDuration(correctedX + x + 1)),
|
||||
abs(duration - toDuration(correctedX + x - 1)),
|
||||
abs(duration - toDuration(correctedX + x + 2)),
|
||||
abs(duration - toDuration(correctedX + x - 2)))
|
||||
.withIndex().minBy { it.value }.index
|
||||
return when (index) {
|
||||
0 -> return correctedX
|
||||
1 -> return correctedX + 1
|
||||
2 -> return correctedX - 1
|
||||
3 -> return correctedX + 2
|
||||
4 -> return correctedX - 2
|
||||
else -> correctedX
|
||||
}
|
||||
}
|
||||
|
||||
private fun scale(): Double = if (userScale == -1.0) dynamicScale else userScale
|
||||
|
||||
fun adjustDynamic(totalDuration: Int, window: Int) = adjustDynamic(totalDuration.toDouble(), window.toDouble())
|
||||
|
||||
fun adjustDynamic(totalDuration: Double, window: Double) {
|
||||
dynamicScale = max(normalize(secondsToScale(totalDuration / NANOS, Math.round(window - 10).toInt())),
|
||||
secondsToScale(60.0, AXIS_DISTANCE_PX))
|
||||
}
|
||||
|
||||
private fun secondsToScale(seconds: Double, size: Int): Double = size / seconds
|
||||
private fun normalize(scale: Double): Double = max(secondsToScale(MAX_ZOOM_SECONDS, AXIS_DISTANCE_PX),
|
||||
min(scale, secondsToScale(MIN_ZOOM_SECONDS, AXIS_DISTANCE_PX)))
|
||||
|
||||
companion object {
|
||||
private const val NANOS: Long = 1_000_000_000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import com.intellij.compiler.charts.CompilationChartsBundle
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel.Modules
|
||||
import com.intellij.compiler.charts.CompilationChartsViewModel.StatisticData
|
||||
import com.intellij.openapi.util.text.Formats
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.UIUtil.FontSize
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.geom.Line2D
|
||||
import java.awt.geom.Path2D
|
||||
import java.awt.geom.Rectangle2D
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPopupMenu
|
||||
import javax.swing.JViewport
|
||||
import kotlin.math.max
|
||||
|
||||
fun charts(vm: CompilationChartsViewModel, zoom: Zoom, viewport: JViewport, init: Charts.() -> Unit): Charts {
|
||||
return Charts(vm, zoom, viewport).apply(init).also { charts ->
|
||||
val size = MaxSize(charts.progress, charts.settings)
|
||||
zoom.adjustDynamic(size.width, charts.area.width)
|
||||
|
||||
charts.progress.clip = Rectangle2D.Double(0.0,
|
||||
0.0,
|
||||
max(zoom.toPixels(size.width), charts.area.width),
|
||||
size.height)
|
||||
charts.usage.clip = Rectangle2D.Double(0.0,
|
||||
size.height,
|
||||
charts.progress.clip.width,
|
||||
max(charts.progress.height * 3, charts.area.height - charts.progress.clip.height - charts.axis.height))
|
||||
charts.axis.clip = Rectangle2D.Double(0.0,
|
||||
charts.progress.clip.height + charts.usage.clip.height,
|
||||
charts.progress.clip.width,
|
||||
charts.axis.height)
|
||||
}
|
||||
}
|
||||
|
||||
class Charts(private val vm: CompilationChartsViewModel, private val zoom: Zoom, viewport: JViewport) {
|
||||
internal val area: Area = Area(viewport.x.toDouble(), viewport.y.toDouble(), viewport.width.toDouble(), viewport.height.toDouble())
|
||||
internal lateinit var progress: ChartProgress
|
||||
internal lateinit var usage: ChartUsage
|
||||
internal lateinit var axis: ChartAxis
|
||||
internal var settings: ChartSettings = ChartSettings()
|
||||
|
||||
fun settings(init: ChartSettings.() -> Unit) {
|
||||
settings = ChartSettings().apply(init)
|
||||
}
|
||||
|
||||
fun progress(init: ChartProgress.() -> Unit) {
|
||||
progress = ChartProgress(zoom).apply(init)
|
||||
}
|
||||
|
||||
fun usage(init: ChartUsage.() -> Unit) {
|
||||
usage = ChartUsage(zoom).apply(init)
|
||||
}
|
||||
|
||||
fun axis(init: ChartAxis.() -> Unit) {
|
||||
axis = ChartAxis(zoom).apply(init)
|
||||
}
|
||||
|
||||
fun draw(g2d: Graphics2D, init: Charts.() -> Unit) {
|
||||
init()
|
||||
val components = listOf(progress, usage, axis)
|
||||
components.forEach { it.background(g2d, settings) }
|
||||
components.forEach { it.component(g2d, settings) }
|
||||
}
|
||||
|
||||
fun width(): Double = axis.clip.width
|
||||
fun height(): Double = axis.clip.run { y + height }
|
||||
}
|
||||
|
||||
interface ChartComponent {
|
||||
fun background(g2d: Graphics2D, settings: ChartSettings)
|
||||
fun component(g2d: Graphics2D, settings: ChartSettings)
|
||||
}
|
||||
|
||||
class ChartProgress(private val zoom: Zoom) : ChartComponent {
|
||||
lateinit var model: Map<Modules.EventKey, List<Modules.Event>>
|
||||
var selected: Modules.EventKey? = null
|
||||
|
||||
var height: Double = 25.5
|
||||
var threads: Int = 0
|
||||
private lateinit var block: ModuleBlock
|
||||
private lateinit var background: ModuleBackground
|
||||
|
||||
internal lateinit var clip: Rectangle2D
|
||||
|
||||
fun block(init: ModuleBlock.() -> Unit) {
|
||||
block = ModuleBlock().apply(init)
|
||||
}
|
||||
|
||||
class ModuleBlock {
|
||||
var border: Double = 2.0
|
||||
var padding: Double = 1.0
|
||||
lateinit var color: (Modules.Event) -> Color
|
||||
lateinit var outline: (Modules.Event) -> Color
|
||||
lateinit var selected: (Modules.Event) -> Color
|
||||
}
|
||||
|
||||
fun background(init: ModuleBackground.() -> Unit) {
|
||||
background = ModuleBackground().apply(init)
|
||||
}
|
||||
|
||||
class ModuleBackground {
|
||||
lateinit var color: (Int) -> Color
|
||||
}
|
||||
|
||||
override fun background(g2d: Graphics2D, settings: ChartSettings) {
|
||||
g2d.withColor(settings.background) {
|
||||
fill(this@ChartProgress.clip)
|
||||
}
|
||||
for (row in 0 until threads) {
|
||||
val cell = Rectangle2D.Double(clip.x, height * row + clip.y, clip.width, height)
|
||||
g2d.withColor(background.color(row)) {
|
||||
fill(cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun component(g2d: Graphics2D, settings: ChartSettings) {
|
||||
settings.mouse.clear()
|
||||
|
||||
g2d.withAntialiasing {
|
||||
for ((key, events) in model) {
|
||||
val start = events.filterIsInstance<Modules.StartEvent>().firstOrNull() ?: continue
|
||||
val end = events.filterIsInstance<Modules.FinishEvent>().firstOrNull()
|
||||
val rect = getRectangle(start, end, settings)
|
||||
|
||||
settings.mouse.module(rect, key, mutableMapOf(
|
||||
"duration" to Formats.formatDuration(((end?.target?.time ?: System.nanoTime()) - start.target.time) / 1_000_000),
|
||||
"name" to start.target.name,
|
||||
"type" to start.target.type,
|
||||
"test" to start.target.isTest.toString(),
|
||||
"fileBased" to start.target.isFileBased.toString(),
|
||||
))
|
||||
|
||||
withColor(block.color(start)) { // module
|
||||
fill(rect)
|
||||
}
|
||||
withColor(if(selected == key) block.selected(start) else block.outline(start)) { // module border
|
||||
draw(rect)
|
||||
}
|
||||
(g2d.create() as Graphics2D).withColor(settings.font.color) {
|
||||
withFont(UIUtil.getLabelFont(settings.font.size)) { // name
|
||||
clip(rect)
|
||||
drawString(" ${start.target.name}", rect.x.toFloat(), (rect.y + (this@ChartProgress.height - block.padding * 2) / 2 + fontMetrics.ascent / 2).toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRectangle(start: Modules.StartEvent, end: Modules.FinishEvent?, settings: ChartSettings): Rectangle2D {
|
||||
val x0 = zoom.toPixels(start.target.time - settings.duration.from) + block.border
|
||||
val x1 = zoom.toPixels((end?.target?.time ?: System.nanoTime()) - settings.duration.from)
|
||||
val width = max(x1 - x0 - block.padding, block.padding) - block.border * 2
|
||||
return Rectangle2D.Double(x0, (start.threadNumber * height + block.padding + block.border),
|
||||
width, height - block.padding - block.border * 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ChartUsage(private val zoom: Zoom) : ChartComponent {
|
||||
lateinit var model: Collection<StatisticData>
|
||||
lateinit var unit: String
|
||||
lateinit var color: UsageColor
|
||||
var maximum: Long = 0
|
||||
|
||||
internal lateinit var clip: Rectangle2D
|
||||
|
||||
fun color(init: UsageColor.() -> Unit) {
|
||||
color = UsageColor().apply(init)
|
||||
}
|
||||
|
||||
class UsageColor {
|
||||
lateinit var background: JBColor
|
||||
lateinit var border: JBColor
|
||||
}
|
||||
|
||||
override fun background(g2d: Graphics2D, settings: ChartSettings) {
|
||||
g2d.withColor(settings.background) {
|
||||
fill(this@ChartUsage.clip)
|
||||
}
|
||||
g2d.withColor(settings.line.color) {
|
||||
draw(Line2D.Double(0.0, this@ChartUsage.clip.y, this@ChartUsage.clip.width, this@ChartUsage.clip.y))
|
||||
}
|
||||
}
|
||||
|
||||
override fun component(g2d: Graphics2D, settings: ChartSettings) {
|
||||
if (model.isEmpty()) return
|
||||
|
||||
val path = path(settings)
|
||||
g2d.withStroke(BasicStroke(USAGE_BORDER)) {
|
||||
withColor(this@ChartUsage.color.border) {
|
||||
draw(path)
|
||||
}
|
||||
withColor(this@ChartUsage.color.background) {
|
||||
fill(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun path(settings: ChartSettings): Path2D {
|
||||
val y0 = clip.y + clip.height
|
||||
|
||||
val path = Path2D.Double()
|
||||
path.moveTo(0.0, y0)
|
||||
model.forEach { statistic ->
|
||||
path.lineTo(zoom.toPixels(statistic.time - settings.duration.from),
|
||||
y0 - (statistic.data.toDouble() / maximum * clip.height))
|
||||
}
|
||||
|
||||
path.lineTo(zoom.toPixels(model.last().time - settings.duration.from), y0)
|
||||
path.closePath()
|
||||
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
class ChartAxis(private val zoom: Zoom) : ChartComponent {
|
||||
var stroke: FloatArray = floatArrayOf(5f, 5f)
|
||||
var distance: Int = 250
|
||||
var count: Int = 10
|
||||
var height: Double = 0.0
|
||||
var padding: Int = 2
|
||||
|
||||
internal lateinit var clip: Rectangle2D
|
||||
|
||||
override fun background(g2d: Graphics2D, settings: ChartSettings) {
|
||||
g2d.withColor(settings.background) {
|
||||
fill(this@ChartAxis.clip)
|
||||
}
|
||||
g2d.withColor(settings.line.color) {
|
||||
draw(Line2D.Double(0.0, this@ChartAxis.clip.y, this@ChartAxis.clip.width, this@ChartAxis.clip.y))
|
||||
}
|
||||
}
|
||||
|
||||
override fun component(g2d: Graphics2D, settings: ChartSettings) {
|
||||
g2d.withAntialiasing {
|
||||
val size = UIUtil.getFontSize(settings.font.size) + padding
|
||||
|
||||
val from = 0
|
||||
val to = this@ChartAxis.clip.width.toInt()
|
||||
|
||||
withColor(COLOR_LINE) {
|
||||
for (x in from..to step distance) {
|
||||
// big axis
|
||||
withStroke(BasicStroke(1.5F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0f, this@ChartAxis.stroke, 0.0f)) {
|
||||
draw(Line2D.Double(x.toDouble(), 0.0, x.toDouble(), this@ChartAxis.clip.y))
|
||||
}
|
||||
for (x1 in x..x + distance step distance / count) {
|
||||
// additional axis
|
||||
draw(Line2D.Double(x1.toDouble(), this@ChartAxis.clip.y, x1.toDouble(), this@ChartAxis.clip.y + (size / 2)))
|
||||
}
|
||||
}
|
||||
}
|
||||
withColor(settings.font.color) {
|
||||
val step = zoom.toDuration(distance)
|
||||
val trim = if (TimeUnit.NANOSECONDS.toMinutes(step) > 2) 60_000
|
||||
else if (TimeUnit.NANOSECONDS.toMinutes(step) >= 1) 1_000
|
||||
else if (TimeUnit.NANOSECONDS.toSeconds(step) > 2) 1_000
|
||||
else 1
|
||||
|
||||
withFont(UIUtil.getLabelFont(settings.font.size)) {
|
||||
for (x in from..to step distance) {
|
||||
val time = Formats.formatDuration((TimeUnit.NANOSECONDS.toMillis(zoom.toDuration(x)) / trim) * trim)
|
||||
drawString(time, x + padding, (this@ChartAxis.clip.y + size).toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChartSettings {
|
||||
internal lateinit var font: ChartFont
|
||||
internal lateinit var mouse: CompilationChartsMouseAdapter
|
||||
var background: Color = JBColor.WHITE
|
||||
internal lateinit var duration: ChartDuration
|
||||
|
||||
internal var line: ChartLine = ChartLine()
|
||||
|
||||
fun font(init: ChartFont.() -> Unit) {
|
||||
font = ChartFont().apply(init)
|
||||
}
|
||||
|
||||
fun duration(init: ChartDuration.() -> Unit) {
|
||||
duration = ChartDuration().apply(init)
|
||||
}
|
||||
|
||||
fun line(init: ChartLine.() -> Unit) {
|
||||
line = ChartLine().apply(init)
|
||||
}
|
||||
|
||||
class ChartFont {
|
||||
var size: FontSize = FontSize.NORMAL
|
||||
var color: Color = JBColor.DARK_GRAY
|
||||
}
|
||||
|
||||
class ChartDuration {
|
||||
var from: Long = Long.MAX_VALUE
|
||||
var to: Long = 0
|
||||
}
|
||||
}
|
||||
|
||||
class ChartLine {
|
||||
var color: Color = JBColor.LIGHT_GRAY
|
||||
var size: Int = 1
|
||||
}
|
||||
|
||||
internal data class Area(val x: Double, val y: Double, val width: Double, val height: Double)
|
||||
internal data class MaxSize(val width: Double, val height: Double) {
|
||||
constructor(width: Long, height: Double) : this(width.toDouble(), height)
|
||||
constructor(progress: ChartProgress, settings: ChartSettings) : this(with(settings.duration) { to - from }, (progress.threads + 1) * progress.height)
|
||||
}
|
||||
|
||||
class CompilationChartsMouseAdapter(private val vm: CompilationChartsViewModel, private val component: Component) : MouseAdapter() {
|
||||
private val components: MutableList<Index> = mutableListOf()
|
||||
private var currentPopup: JPopupMenu? = null
|
||||
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
val info = search(e.point)?.info ?: return
|
||||
val popupMenu = JPopupMenu().apply {
|
||||
add(JLabel(CompilationChartsBundle.message("charts.module.info", info["name"], info["duration"]))) // TODO
|
||||
show(this@CompilationChartsMouseAdapter.component, e.point.x, e.point.y)
|
||||
}
|
||||
currentPopup = popupMenu
|
||||
}
|
||||
|
||||
override fun mouseExited(e: MouseEvent) {
|
||||
val popup = currentPopup ?: return
|
||||
if (popup.isShowing && !popup.contains(e.point)) {
|
||||
popup.isVisible = false // todo animation
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
components.clear()
|
||||
}
|
||||
|
||||
fun module(rect: Rectangle2D, key: Modules.EventKey, info: Map<String, String>) {
|
||||
components.add(Index(rect, key, info))
|
||||
}
|
||||
|
||||
private fun search(point: Point): Index? {
|
||||
components.forEach { index ->
|
||||
if (index.x0 <= point.x && index.x1 >= point.x && index.y0 <= point.y && index.y1 >= point.y) return index
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private data class Index(val x0: Double, val x1: Double,
|
||||
val y0: Double, val y1: Double,
|
||||
val key: Modules.EventKey,
|
||||
val info: Map<String, String>) {
|
||||
constructor(rect: Rectangle2D, key: Modules.EventKey, info: Map<String, String>) : this(rect.x, rect.x + rect.width,
|
||||
rect.y, rect.y + rect.height,
|
||||
key, info)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.util.ui.UIUtil.FontSize
|
||||
import java.awt.Color
|
||||
|
||||
val COLOR_TEST_BLOCK: JBColor = JBColor(Color(0xc5e5cc), Color(0x375239))
|
||||
val COLOR_TEST_BLOCK_DISABLED: JBColor = JBColor(Color(0xb6eedd), Color(0x26392b))
|
||||
val COLOR_TEST_BORDER: JBColor = JBColor(Color(0x3b9954), Color(0x549159))
|
||||
val COLOR_TEST_BORDER_SELECTED: JBColor = JBColor.GREEN
|
||||
|
||||
val COLOR_PRODUCTION_BLOCK: JBColor = JBColor(Color(0xc2d6fc), Color(0x2e436e))
|
||||
val COLOR_PRODUCTION_BLOCK_DISABLED: JBColor = JBColor(Color(0xd7e4fd), Color(0x202e4d))
|
||||
val COLOR_PRODUCTION_BORDER: JBColor = JBColor(Color(0x4781fa), Color(0x4978d6))
|
||||
val COLOR_PRODUCTION_BORDER_SELECTED: JBColor = JBColor.BLUE
|
||||
|
||||
val COLOR_BACKGROUND: JBColor = JBColor.WHITE
|
||||
val COLOR_BACKGROUND_ODD: JBColor = JBColor.WHITE
|
||||
val COLOR_BACKGROUND_EVEN: JBColor = JBColor(Color(0xfefefe), Color(0x3e4042))
|
||||
|
||||
val COLOR_TEXT: JBColor = JBColor(Color(0x1d1d1d), Color(0xbfbfbf))
|
||||
val COLOR_LINE: JBColor = JBColor.LIGHT_GRAY
|
||||
|
||||
val COLOR_MEMORY: JBColor = JBColor.namedColor("Profiler.MemoryChart.inactiveBorderColor")
|
||||
val COLOR_MEMORY_BORDER: JBColor = JBColor.namedColor("Profiler.MemoryChart.borderColor")
|
||||
|
||||
val COLOR_CPU: JBColor = JBColor.namedColor("Profiler.CpuChart.inactiveBorderColor")
|
||||
val COLOR_CPU_BORDER: JBColor = JBColor.namedColor("Profiler.CpuChart.borderColor")
|
||||
|
||||
|
||||
const val MODULE_BLOCK_PADDING: Double = 1.0
|
||||
const val MODULE_BLOCK_BORDER: Double = 2.0
|
||||
|
||||
const val USAGE_BORDER: Float = 2.0f
|
||||
|
||||
val FONT_SIZE: FontSize = FontSize.NORMAL
|
||||
|
||||
const val AXIS_DISTANCE_PX: Int = 250
|
||||
const val AXIS_MARKERS_COUNT: Int = 10
|
||||
const val AXIS_TEXT_PADDING: Int = 2
|
||||
|
||||
const val MIN_ZOOM_SECONDS = 0.1
|
||||
const val MAX_ZOOM_SECONDS = 100.0
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.compiler.charts.ui
|
||||
|
||||
import java.awt.*
|
||||
|
||||
fun Graphics2D.withColor(color: Color, block: Graphics2D.() -> Unit): Graphics2D {
|
||||
val oldColor = this.color
|
||||
this.color = color
|
||||
block()
|
||||
this.color = oldColor
|
||||
return this
|
||||
}
|
||||
|
||||
fun Graphics2D.withFont(font: Font, block: Graphics2D.() -> Unit): Graphics2D {
|
||||
val oldFont = this.font
|
||||
this.font = font
|
||||
block()
|
||||
this.font = oldFont
|
||||
return this
|
||||
}
|
||||
|
||||
fun Graphics2D.withAntialiasing(block: Graphics2D.() -> Unit): Graphics2D {
|
||||
val old = getRenderingHint(RenderingHints.KEY_ANTIALIASING)
|
||||
|
||||
setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
|
||||
block()
|
||||
|
||||
setRenderingHint(RenderingHints.KEY_ANTIALIASING, old)
|
||||
return this
|
||||
}
|
||||
|
||||
fun Graphics2D.withStroke(stroke: Stroke, block: Graphics2D.() -> Unit): Graphics2D {
|
||||
val oldStroke = this.stroke
|
||||
this.stroke = stroke
|
||||
block()
|
||||
this.stroke = oldStroke
|
||||
return this
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
<xi:include href="intellij.java.frontback.psi.impl.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="intellij.java.frontback.impl.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="intellij.java.compiler.charts.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="/META-INF/JvmAnalysisPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="/META-INF/JavaIndexingPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="/META-INF/JavaPsiPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
@@ -238,5 +238,6 @@
|
||||
<orderEntry type="library" name="ion" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.boot" />
|
||||
<orderEntry type="library" name="ktor-client-cio" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.java.compiler.charts.jps" scope="RUNTIME" />
|
||||
</component>
|
||||
</module>
|
||||
Reference in New Issue
Block a user