AT-126 Open-source performance plugin

Split performance plugin into core (and move it to community), async and YK. Move Cache Value Profiler to separate plugin.

GitOrigin-RevId: 80e68080197e86d40c76be45d5438ec0eee39818
This commit is contained in:
Maxim.Kolmakov
2022-10-26 13:08:49 +00:00
committed by intellij-monorepo-bot
parent 3f60eaf4d8
commit 1196257bd0
453 changed files with 11477 additions and 14044 deletions
-19
View File
@@ -1,19 +0,0 @@
<component name="libraryTable">
<library name="kodein-di-jvm" type="repository">
<properties maven-id="org.kodein.di:kodein-di-jvm:7.15.0">
<exclude>
<dependency maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8" />
<dependency maven-id="org.jetbrains.kotlin:kotlin-stdlib-common" />
</exclude>
</properties>
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/kodein/di/kodein-di-jvm/7.15.0/kodein-di-jvm-7.15.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/kodein/type/kaverit-jvm/2.2.1-kotlin-1.7.20-RC/kaverit-jvm-2.2.1-kotlin-1.7.20-RC.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/kodein/di/kodein-di-jvm/7.15.0/kodein-di-jvm-7.15.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/kodein/type/kaverit-jvm/2.2.1-kotlin-1.7.20-RC/kaverit-jvm-2.2.1-kotlin-1.7.20-RC-sources.jar!/" />
</SOURCES>
</library>
</component>
-14
View File
@@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="qodana-sarif" type="repository">
<properties maven-id="com.jetbrains.qodana:qodana-sarif:0.2.1">
<exclude>
<dependency maven-id="com.google.code.gson:gson" />
</exclude>
</properties>
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/qodana/qodana-sarif/0.2.1/qodana-sarif-0.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+1 -4
View File
@@ -774,6 +774,7 @@
<module fileurl="file://$PROJECT_DIR$/plugins/package-search/gradle/tooling/intellij.packageSearch.gradle.tooling.iml" filepath="$PROJECT_DIR$/plugins/package-search/gradle/tooling/intellij.packageSearch.gradle.tooling.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/package-search/kotlin/intellij.packageSearch.kotlin.iml" filepath="$PROJECT_DIR$/plugins/package-search/kotlin/intellij.packageSearch.kotlin.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/package-search/maven/intellij.packageSearch.maven.iml" filepath="$PROJECT_DIR$/plugins/package-search/maven/intellij.packageSearch.maven.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/performanceTesting/intellij.performanceTesting.iml" filepath="$PROJECT_DIR$/plugins/performanceTesting/intellij.performanceTesting.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/analysis-api/intellij.platform.analysis.iml" filepath="$PROJECT_DIR$/platform/analysis-api/intellij.platform.analysis.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/analysis-impl/intellij.platform.analysis.impl.iml" filepath="$PROJECT_DIR$/platform/analysis-impl/intellij.platform.analysis.impl.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/boot/intellij.platform.boot.iml" filepath="$PROJECT_DIR$/platform/boot/intellij.platform.boot.iml" />
@@ -1032,10 +1033,6 @@
<module fileurl="file://$PROJECT_DIR$/plugins/toml/core/intellij.toml.core.iml" filepath="$PROJECT_DIR$/plugins/toml/core/intellij.toml.core.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/toml/json/intellij.toml.json.iml" filepath="$PROJECT_DIR$/plugins/toml/json/intellij.toml.json.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/toml/core/src/test/intellij.toml.tests.iml" filepath="$PROJECT_DIR$/plugins/toml/core/src/test/intellij.toml.tests.iml" />
<module fileurl="file://$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.metricsCollector/intellij.tools.ide.metricsCollector.iml" filepath="$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.metricsCollector/intellij.tools.ide.metricsCollector.iml" />
<module fileurl="file://$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.starter/intellij.tools.ide.starter.iml" filepath="$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.starter/intellij.tools.ide.starter.iml" />
<module fileurl="file://$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.starter.junit4/intellij.tools.ide.starter.junit4.iml" filepath="$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.starter.junit4/intellij.tools.ide.starter.junit4.iml" />
<module fileurl="file://$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.starter.junit5/intellij.tools.ide.starter.junit5.iml" filepath="$PROJECT_DIR$/tools/ideTestingFramework/intellij.tools.ide.starter.junit5/intellij.tools.ide.starter.junit5.iml" />
<module fileurl="file://$PROJECT_DIR$/jps/antLayout/intellij.tools.jps.antTasks.iml" filepath="$PROJECT_DIR$/jps/antLayout/intellij.tools.jps.antTasks.iml" />
<module fileurl="file://$PROJECT_DIR$/jps/standalone-builder/intellij.tools.jps.build.standalone.iml" filepath="$PROJECT_DIR$/jps/standalone-builder/intellij.tools.jps.build.standalone.iml" />
<module fileurl="file://$PROJECT_DIR$/tools/launcher-generator/intellij.tools.launcherGenerator.iml" filepath="$PROJECT_DIR$/tools/launcher-generator/intellij.tools.launcherGenerator.iml" />
@@ -26,11 +26,7 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
"intellij.platform.debugger.testFramework",
"intellij.platform.vcs.testFramework",
"intellij.platform.externalSystem.testFramework",
"intellij.maven.testFramework",
"intellij.tools.ide.starter",
"intellij.tools.ide.metricsCollector",
"intellij.tools.ide.starter.junit4",
"intellij.tools.ide.starter.junit5"
"intellij.maven.testFramework"
)
}
+1
View File
@@ -198,5 +198,6 @@
<orderEntry type="module" module-name="intellij.fullLine" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.fullLine.java" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.fullLine.kotlin" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.performanceTesting" scope="RUNTIME" />
</component>
</module>
@@ -70,6 +70,7 @@ object CommunityRepositoryModules {
spec.withModule("intellij.xml.langInjection", "IntelliLang.jar")
spec.withModule("intellij.java.langInjection.jps")
},
plugin(listOf("intellij.performanceTesting")),
plugin("intellij.tasks.core") { spec ->
spec.directoryName = "tasks"
spec.withModule("intellij.tasks")
@@ -192,6 +192,7 @@ open class StartupManagerImpl(private val project: Project) : StartupManagerEx()
val pluginId = adapter.pluginDescriptor.pluginId
if (!isCorePlugin(adapter.pluginDescriptor) && pluginId.idString != "com.jetbrains.performancePlugin"
&& pluginId.idString != "com.intellij.clion-makefile"
&& pluginId.idString != "com.jetbrains.performancePlugin.yourkit"
&& pluginId.idString != "com.intellij.clion-swift"
&& pluginId.idString != "com.intellij.appcode"
&& pluginId.idString != "com.intellij.clion-compdb"
@@ -0,0 +1,703 @@
/* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */
package com.jetbrains.performancePlugin.lang.lexer;
import com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.TokenType;
import com.intellij.lexer.FlexLexer;
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.7.0
* from the specification file <tt>IJPerf.flex</tt>
*/
class IJPerfLexer implements FlexLexer {
/** This character denotes the end of file */
public static final int YYEOF = -1;
/** initial size of the lookahead buffer */
private static final int ZZ_BUFFERSIZE = 16384;
/** lexical states */
public static final int YYINITIAL = 0;
public static final int WAITING_INPUT = 2;
public static final int WAITING_TEXT = 4;
public static final int WAITING_LAST_OPTION = 6;
/**
* ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
* ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
* at the beginning of a line
* l is of the form l = 2*k, k a non negative integer
*/
private static final int ZZ_LEXSTATE[] = {
0, 0, 1, 1, 2, 2, 3, 3
};
/**
* Translates characters to character classes
* Chosen bits are [12, 6, 3]
* Total runtime size is 15696 bytes
*/
public static int ZZ_CMAP(int ch) {
return ZZ_CMAP_A[(ZZ_CMAP_Y[(ZZ_CMAP_Z[ch>>9]<<6)|((ch>>3)&0x3f)]<<3)|(ch&0x7)];
}
/* The ZZ_CMAP_Z table has 2176 entries */
static final char ZZ_CMAP_Z[] = zzUnpackCMap(
"\1\0\1\1\1\2\1\3\1\4\1\5\1\6\1\7\1\10\1\11\1\12\1\13\1\14\1\15\1\16\1\17\1"+
"\20\5\21\1\22\1\23\1\24\1\21\14\25\1\26\50\25\1\27\2\25\1\30\1\31\1\32\1\33"+
"\25\25\1\34\20\21\1\35\1\36\1\37\1\40\1\41\1\42\1\43\1\44\1\45\1\46\1\47\1"+
"\21\1\50\1\51\1\52\1\53\1\54\1\55\1\56\1\21\1\25\1\57\1\60\5\21\2\25\1\61"+
"\7\21\1\25\1\62\20\21\1\25\1\63\1\21\1\64\13\25\1\65\1\25\1\66\22\21\1\67"+
"\1\70\4\21\1\71\11\21\1\72\1\73\1\74\1\75\1\21\1\76\2\21\1\77\3\21\1\100\2"+
"\21\1\101\10\21\123\25\1\102\7\25\1\103\1\104\12\25\1\105\15\25\1\106\6\21"+
"\1\25\1\107\u0582\21\1\110\u017f\21");
/* The ZZ_CMAP_Y table has 4672 entries */
static final char ZZ_CMAP_Y[] = zzUnpackCMap(
"\1\0\1\1\1\0\1\2\1\3\1\4\1\5\1\6\1\7\2\0\1\10\1\7\2\0\1\11\1\12\3\0\1\13\1"+
"\14\1\15\1\16\2\0\1\17\3\0\1\17\71\0\1\20\1\0\1\21\1\22\1\23\1\24\2\22\16"+
"\0\1\25\1\26\1\27\1\30\2\0\1\31\11\0\1\32\21\0\1\31\1\33\24\0\1\7\3\0\1\17"+
"\1\34\1\7\4\0\1\35\1\7\4\0\1\32\1\36\1\22\3\0\2\37\1\22\1\40\1\41\1\0\1\42"+
"\10\0\1\5\1\43\14\0\1\44\1\32\1\0\1\45\1\5\1\46\1\22\1\35\7\0\1\47\14\0\1"+
"\21\1\22\1\5\1\50\4\0\1\40\1\16\5\0\1\40\2\22\3\0\1\2\1\0\1\37\6\22\2\0\1"+
"\25\1\40\2\22\1\51\21\0\1\52\1\5\1\7\1\0\1\44\1\53\1\54\2\0\1\45\1\55\1\56"+
"\1\53\1\57\1\35\1\60\1\52\1\5\1\2\1\61\1\62\1\63\1\54\2\0\1\45\1\64\1\65\1"+
"\63\1\66\1\34\1\67\1\70\1\5\1\40\1\22\1\62\1\32\1\31\2\0\1\45\1\71\1\56\1"+
"\32\1\72\1\73\1\22\1\52\1\5\1\34\1\7\1\62\1\53\1\54\2\0\1\45\1\71\1\56\1\53"+
"\1\66\1\74\1\60\1\52\1\5\1\34\1\22\1\75\1\76\1\77\1\100\1\61\1\76\1\0\1\20"+
"\1\76\1\77\1\101\1\22\1\70\1\5\1\22\1\34\1\44\1\25\1\45\2\0\1\45\1\0\1\102"+
"\1\25\1\77\1\103\1\37\1\52\1\5\2\22\1\44\1\25\1\45\2\0\1\45\1\44\1\56\1\25"+
"\1\77\1\103\1\27\1\52\1\5\1\104\1\22\1\44\1\25\1\45\5\0\1\25\1\105\1\51\1"+
"\35\1\52\1\5\1\22\1\33\1\75\1\0\1\17\1\33\2\0\1\31\1\106\1\17\1\107\1\110"+
"\1\0\1\70\1\5\1\111\1\22\1\7\6\0\1\63\1\0\1\17\1\5\1\112\4\22\1\113\1\114"+
"\1\51\1\7\1\115\1\75\1\0\1\72\1\110\1\40\1\5\1\116\4\22\1\73\2\22\1\21\1\5"+
"\1\112\1\117\1\120\1\0\1\7\3\0\1\23\1\7\1\0\1\25\2\0\1\7\3\0\1\23\1\27\7\22"+
"\10\0\1\5\1\112\10\0\1\5\1\121\4\0\1\32\1\15\5\0\1\122\51\0\1\77\1\17\1\77"+
"\5\0\1\77\4\0\1\77\1\17\1\77\1\0\1\17\7\0\1\77\10\0\1\47\4\22\2\0\2\22\12"+
"\0\2\40\1\7\114\0\1\53\2\0\1\7\2\0\1\37\11\0\1\76\1\0\1\73\1\0\1\25\1\23\1"+
"\22\2\0\1\23\1\22\2\0\1\2\1\22\1\0\1\25\1\123\1\22\12\0\1\124\1\125\1\5\1"+
"\112\3\22\1\126\1\5\1\112\13\0\1\22\5\0\1\37\10\0\1\40\1\22\3\0\1\17\1\0\1"+
"\2\1\0\1\2\1\70\1\5\3\0\1\40\1\23\1\22\5\0\1\2\3\0\1\21\1\5\1\112\4\22\3\0"+
"\1\2\7\0\1\17\3\0\1\53\1\5\1\112\1\5\1\112\1\35\1\22\1\0\1\40\10\22\11\0\1"+
"\2\1\5\1\112\1\22\1\127\1\2\1\22\6\0\1\5\1\50\6\0\1\2\1\22\7\0\1\22\1\5\1"+
"\130\1\5\1\50\3\0\1\40\1\0\1\73\10\22\1\122\4\0\1\21\37\0\1\31\42\0\2\40\4"+
"\0\2\40\1\0\1\131\3\0\1\40\6\0\1\25\1\110\1\132\1\23\1\133\1\2\1\0\1\23\1"+
"\132\1\23\1\22\1\127\3\22\1\134\1\22\1\35\1\73\1\22\1\135\1\22\1\25\1\0\1"+
"\34\1\35\2\22\1\0\1\23\4\0\2\22\1\0\1\23\1\136\1\0\1\73\1\22\1\107\1\33\1"+
"\106\1\137\1\24\1\140\1\0\1\56\1\141\1\142\2\22\5\0\1\73\116\22\5\0\1\17\5"+
"\0\1\17\20\0\1\23\1\127\1\2\1\22\4\0\1\32\1\15\7\0\1\35\1\22\1\35\2\0\1\17"+
"\1\22\10\17\4\0\5\22\1\35\72\22\1\141\3\22\1\7\1\0\1\137\1\23\1\7\11\0\1\17"+
"\1\143\1\7\12\0\1\122\1\141\4\0\1\17\1\7\12\0\1\17\2\22\3\0\1\37\6\22\170"+
"\0\1\40\11\22\75\0\1\37\2\22\21\0\1\23\10\22\5\0\1\40\41\0\1\23\2\0\1\5\1"+
"\144\2\22\6\0\1\51\1\32\16\0\1\21\3\22\1\35\1\0\1\33\14\0\1\54\3\0\1\17\1"+
"\0\7\22\1\35\6\0\2\22\1\73\6\0\1\2\1\22\10\0\1\40\1\22\1\5\1\112\3\0\1\145"+
"\1\5\1\50\3\0\1\40\4\0\1\2\1\22\3\0\1\23\10\0\1\73\1\35\1\5\1\112\2\0\1\5"+
"\1\146\6\0\1\17\1\22\1\0\1\40\1\5\1\112\2\0\1\17\1\33\10\0\1\37\2\22\1\125"+
"\2\0\1\147\1\22\3\150\1\22\2\17\5\0\1\122\1\40\1\22\17\0\1\151\1\5\1\112\64"+
"\0\1\2\1\22\2\0\1\17\1\127\5\0\1\2\40\22\55\0\1\40\15\0\1\21\4\22\1\17\1\22"+
"\1\127\1\141\1\0\1\45\1\17\1\110\1\152\15\0\1\21\3\22\1\127\54\0\1\40\2\22"+
"\10\0\1\33\6\0\5\22\1\0\1\23\2\0\2\22\2\0\1\61\2\22\1\141\3\22\1\34\1\25\20"+
"\0\1\53\1\135\1\22\1\5\1\112\1\7\2\0\1\63\1\7\2\0\1\37\1\74\12\0\1\17\3\33"+
"\1\153\1\154\2\22\1\155\1\0\1\44\2\0\1\17\2\0\1\156\1\0\1\40\1\0\1\40\4\22"+
"\17\0\1\37\10\22\6\0\1\23\20\22\1\15\20\22\3\0\1\23\6\0\1\73\1\22\1\73\3\22"+
"\4\0\1\22\1\141\3\0\1\37\5\0\1\37\3\0\1\40\4\0\1\2\1\0\1\137\5\22\23\0\1\40"+
"\1\5\1\112\4\0\1\2\4\0\1\2\5\0\1\22\6\0\1\2\23\22\46\0\1\17\1\22\2\0\1\40"+
"\1\22\1\0\23\22\1\40\1\45\4\0\1\32\1\157\2\0\1\40\1\22\2\0\1\17\1\22\3\0\1"+
"\17\10\22\2\0\1\151\1\22\2\0\1\40\1\22\3\0\1\21\10\22\7\0\1\74\10\22\1\160"+
"\1\51\1\44\1\7\2\0\1\2\1\63\4\22\3\0\1\23\3\0\1\23\4\22\1\0\1\7\2\0\1\17\3"+
"\22\6\0\1\40\1\22\2\0\1\40\1\22\2\0\1\37\1\22\2\0\1\21\15\22\11\0\1\73\6\22"+
"\6\0\1\37\1\22\6\0\1\37\41\22\10\0\1\17\3\22\1\70\1\5\1\22\1\35\7\0\1\161"+
"\2\22\3\0\1\73\1\5\1\112\6\0\1\162\1\5\2\22\4\0\1\163\1\22\10\0\1\23\1\153"+
"\1\5\1\164\4\22\2\0\1\31\4\0\1\27\10\22\1\17\1\140\1\0\1\32\1\0\1\73\7\0\1"+
"\37\1\5\1\112\1\44\1\53\1\54\2\0\1\45\1\71\1\56\1\53\1\66\1\101\1\141\1\133"+
"\2\23\21\22\11\0\1\37\1\5\1\112\4\22\10\0\1\32\1\22\1\5\1\112\24\22\6\0\1"+
"\40\1\0\1\73\2\22\1\40\4\22\10\0\1\165\1\22\1\5\1\112\4\22\7\0\1\22\1\5\1"+
"\112\6\22\3\0\1\102\1\0\1\2\1\5\1\112\54\22\10\0\1\5\1\112\1\22\1\35\40\22"+
"\7\0\1\17\1\35\1\22\6\0\1\133\2\0\1\21\4\22\7\0\1\73\40\22\1\0\1\45\4\0\1"+
"\17\1\0\1\73\1\22\1\5\1\112\2\22\1\33\3\0\1\33\2\0\1\7\1\17\11\22\1\17\1\31"+
"\4\0\1\17\1\166\1\0\1\22\1\0\1\21\24\22\63\0\1\21\14\22\15\0\1\17\2\22\30"+
"\0\1\2\27\22\5\0\1\17\72\22\10\0\1\17\67\22\7\0\1\73\3\0\1\17\1\5\1\112\14"+
"\22\3\0\1\40\1\23\1\22\6\0\1\17\1\22\1\2\1\22\1\5\1\112\1\127\2\0\1\141\2"+
"\0\56\22\10\0\1\23\1\22\5\0\1\17\1\22\1\35\2\0\10\22\1\21\3\22\75\0\1\23\2"+
"\22\36\0\1\37\41\22\43\0\1\17\12\22\61\0\1\2\40\22\15\0\1\37\1\0\1\23\1\0"+
"\1\73\1\0\1\154\1\2\127\22\1\141\1\102\2\0\1\47\1\2\3\22\1\13\22\22\1\153"+
"\67\22\12\0\1\25\10\0\1\25\1\167\1\170\1\0\1\171\1\44\7\0\1\32\1\47\2\25\3"+
"\0\1\172\1\110\1\33\1\45\51\0\1\40\3\0\1\45\2\0\1\122\3\0\1\122\2\0\1\25\3"+
"\0\1\25\2\0\1\17\3\0\1\17\3\0\1\45\3\0\1\45\2\0\1\122\1\52\6\5\6\0\1\17\1"+
"\127\5\0\1\23\1\15\1\22\1\135\2\22\1\127\1\7\1\0\52\22\1\17\2\0\1\54\1\152"+
"\1\37\72\22\30\0\1\23\1\22\1\17\5\22\11\0\1\37\1\5\1\112\24\22\1\44\3\0\1"+
"\113\1\7\1\122\1\173\1\107\1\174\1\113\1\131\1\113\2\122\1\67\1\0\1\31\1\0"+
"\1\2\1\62\1\31\1\0\1\2\50\22\32\0\1\17\5\22\106\0\1\23\1\22\33\0\1\40\120"+
"\0\1\21\1\22\146\0\1\73\3\22\3\0\1\40\74\22\1\34\3\22\14\0\20\22\36\0\2\22");
/* The ZZ_CMAP_A table has 1000 entries */
static final char ZZ_CMAP_A[] = zzUnpackCMap(
"\11\14\1\4\1\2\1\1\1\5\1\3\6\14\4\0\1\4\1\0\1\12\1\0\1\14\1\6\6\0\1\10\1\17"+
"\1\22\1\20\12\21\1\16\2\0\1\7\3\0\12\14\1\0\1\13\2\0\4\14\1\0\1\11\2\0\6\14"+
"\1\15\2\14\2\0\4\14\4\0\1\14\2\0\1\14\7\0\1\14\4\0\1\14\5\0\7\14\1\0\2\14"+
"\4\0\4\14\16\0\5\14\7\0\1\14\1\0\1\14\1\0\5\14\1\0\2\14\2\0\4\14\1\0\1\14"+
"\6\0\1\14\1\0\3\14\1\0\1\14\1\0\4\14\1\0\13\14\1\0\1\14\2\0\6\14\1\0\1\14"+
"\15\0\1\14\1\0\2\14\1\0\2\14\1\0\4\14\5\0\6\14\5\0\1\14\4\0\3\14\1\0\1\14"+
"\3\0\2\21\4\0\6\14\1\0\4\14\1\0\6\14\2\21\3\14\2\0\4\14\2\0\3\14\2\21\6\14"+
"\4\0\10\14\2\0\2\21\5\14\2\0\2\14\2\0\6\14\1\0\1\14\3\0\4\14\2\0\5\14\2\0"+
"\4\14\5\0\2\14\1\0\1\14\3\0\2\14\4\0\3\14\1\0\6\14\4\0\2\14\1\0\2\14\1\0\2"+
"\14\1\0\2\14\2\0\1\14\1\0\3\14\2\0\3\14\3\0\4\14\1\0\1\14\7\0\2\21\1\14\1"+
"\0\2\14\1\0\5\14\1\0\3\14\2\0\1\14\15\0\2\14\2\0\2\14\1\0\6\14\3\0\3\14\1"+
"\0\4\14\3\0\2\14\1\0\1\14\1\0\3\14\6\0\3\14\3\0\3\14\5\0\2\14\2\0\2\14\5\0"+
"\1\14\1\0\5\14\1\0\4\14\1\0\1\14\4\0\1\14\4\0\6\14\1\0\1\14\3\0\2\14\4\0\2"+
"\21\7\0\2\14\1\0\1\14\2\0\2\14\1\0\1\14\2\0\1\14\3\0\3\14\1\0\1\14\1\0\1\14"+
"\2\21\2\0\4\14\5\0\1\14\1\0\1\14\1\0\1\14\4\0\2\14\2\21\4\14\2\0\3\14\1\0"+
"\5\14\1\0\2\14\4\0\4\14\3\0\1\14\3\0\3\14\5\0\4\14\4\0\5\14\2\21\3\0\3\14"+
"\1\0\1\14\1\0\1\14\1\0\1\14\1\0\1\14\2\0\3\14\1\0\6\14\2\0\2\14\2\1\5\14\5"+
"\0\1\14\4\0\1\14\3\0\3\14\1\0\5\14\2\0\1\14\1\0\4\14\1\0\1\14\5\0\5\14\4\0"+
"\1\14\2\0\2\14\2\0\3\14\2\21\2\14\7\0\1\14\1\0\1\14\2\0\2\21\5\14\3\0\5\14"+
"\2\0\6\14\1\0\3\14\1\0\2\14\2\0\2\14\1\0\2\14\1\0\2\14\2\0\3\14\3\0\2\14\3"+
"\0\2\14\2\0\3\14\4\0\3\14\1\0\2\14\1\0\2\14\3\0\1\14\2\0\5\14\1\0\2\14\1\0"+
"\3\14\2\0\1\14\2\0\5\14\1\0\2\21\4\14\2\0\1\14\1\0\2\21\1\14\1\0\1\14\3\0"+
"\1\14\3\0\1\14\5\0\1\14\1\0\2\14\1\0\1\14\2\0\1\14\2\0\2\14\2\0\4\14\1\0\4"+
"\14\1\0\1\14\1\0\5\14\1\0\4\14\2\0\1\14\1\0\1\14\5\0\1\14\1\0\1\14\1\0\3\14");
/**
* Translates DFA states to action switch labels.
*/
private static final int [] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
"\4\0\2\1\1\2\2\3\1\4\1\2\1\5\1\3"+
"\1\6\1\4\1\2\1\4\1\7\2\10\1\4\1\11"+
"\1\12\1\11\1\13\1\4\1\0\1\4\4\0\1\6"+
"\1\4\1\0\1\4\2\2\1\14\3\0\2\4";
private static int [] zzUnpackAction() {
int [] result = new int[44];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAction(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/**
* Translates a state to a row index in the transition table
*/
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
private static final String ZZ_ROWMAP_PACKED_0 =
"\0\0\0\23\0\46\0\71\0\114\0\137\0\162\0\205"+
"\0\230\0\253\0\276\0\321\0\344\0\367\0\u010a\0\u011d"+
"\0\u0130\0\u0143\0\u0156\0\46\0\u0169\0\u017c\0\230\0\230"+
"\0\230\0\u018f\0\u01a2\0\276\0\u01b5\0\u01c8\0\u01db\0\344"+
"\0\u01ee\0\u0201\0\u0214\0\u011d\0\u0169\0\u018f\0\u0227\0\u023a"+
"\0\u024d\0\u0260\0\u0273\0\u0286";
private static int [] zzUnpackRowMap() {
int [] result = new int[44];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
}
private static int zzUnpackRowMap(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int high = packed.charAt(i++) << 16;
result[j++] = high | packed.charAt(i++);
}
return j;
}
/**
* The transition table of the DFA
*/
private static final int [] ZZ_TRANS = zzUnpackTrans();
private static final String ZZ_TRANS_PACKED_0 =
"\1\5\1\6\1\7\1\6\2\7\1\10\5\11\1\5"+
"\1\6\1\11\1\5\2\11\1\5\1\12\1\13\1\7"+
"\1\13\1\14\1\7\4\11\1\15\1\16\1\17\1\20"+
"\1\11\1\21\1\16\1\22\1\21\1\23\1\24\1\7"+
"\3\24\7\23\1\24\5\23\1\12\1\25\1\7\1\25"+
"\1\26\1\7\1\11\1\27\1\30\1\31\1\15\1\16"+
"\1\17\1\32\1\11\1\21\1\16\1\22\1\21\2\5"+
"\1\0\1\5\6\0\1\5\1\0\2\5\1\0\1\5"+
"\1\0\1\5\1\0\1\5\1\6\1\7\1\6\2\7"+
"\4\0\1\5\1\0\1\5\1\6\1\0\1\5\1\0"+
"\1\5\2\0\5\7\7\0\1\7\5\0\2\5\1\0"+
"\1\5\2\0\1\33\5\0\2\5\1\0\1\5\2\0"+
"\1\5\23\0\2\12\1\0\1\12\6\0\1\12\1\0"+
"\2\12\1\0\1\12\1\0\1\12\1\0\1\12\1\34"+
"\1\7\1\34\2\7\4\0\1\12\1\0\1\12\1\34"+
"\1\0\1\12\1\0\1\12\2\0\3\7\1\14\1\7"+
"\7\0\1\7\11\0\1\35\6\0\1\36\2\37\1\0"+
"\2\35\1\37\1\35\12\0\1\40\1\16\2\41\1\0"+
"\2\16\1\41\1\16\2\12\1\0\1\12\6\0\1\42"+
"\1\41\2\17\1\43\1\21\1\41\1\17\1\16\1\12"+
"\1\34\1\7\1\34\2\7\4\0\1\42\1\41\1\17"+
"\1\44\1\43\1\21\1\41\1\17\1\16\2\12\1\0"+
"\1\12\6\0\1\42\1\16\2\17\1\0\1\21\1\16"+
"\1\17\1\16\12\0\1\40\3\41\1\43\1\16\1\41"+
"\1\22\1\16\2\23\1\0\20\23\1\12\1\45\1\7"+
"\1\45\2\7\4\0\1\12\1\0\1\12\1\45\1\0"+
"\1\12\1\0\1\12\2\0\3\7\1\26\1\7\7\0"+
"\1\7\5\0\1\12\1\45\1\7\1\45\2\7\4\0"+
"\1\42\1\41\1\17\1\46\1\43\1\21\1\41\1\17"+
"\1\16\2\5\1\0\1\5\3\0\1\47\4\0\2\5"+
"\1\0\1\5\2\0\1\5\4\0\1\35\5\0\1\16"+
"\3\35\1\0\4\35\4\0\1\35\5\0\1\16\1\50"+
"\2\35\1\0\4\35\4\0\1\35\5\0\1\16\1\51"+
"\2\37\1\52\1\35\1\51\1\37\1\35\12\0\1\40"+
"\3\41\1\43\1\16\2\41\1\16\2\12\1\0\1\12"+
"\1\35\5\0\1\12\1\36\2\53\1\0\1\54\1\35"+
"\1\53\1\35\13\0\1\41\2\16\1\43\1\16\1\41"+
"\2\16\2\47\2\0\17\47\4\0\1\35\5\0\1\16"+
"\1\35\2\37\1\0\2\35\1\37\1\35\4\0\1\35"+
"\5\0\1\16\1\51\2\35\1\52\1\35\1\51\2\35"+
"\4\0\1\35\6\0\1\51\2\35\1\52\1\35\1\51"+
"\2\35\2\12\1\0\1\12\1\35\5\0\1\21\1\51"+
"\2\53\1\52\1\54\1\51\1\53\1\35\2\12\1\0"+
"\1\12\1\35\5\0\1\21\1\35\2\54\1\0\1\54"+
"\1\35\1\54\1\35";
private static int [] zzUnpackTrans() {
int [] result = new int[665];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
}
private static int zzUnpackTrans(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
value--;
do result[j++] = value; while (--count > 0);
}
return j;
}
/* error codes */
private static final int ZZ_UNKNOWN_ERROR = 0;
private static final int ZZ_NO_MATCH = 1;
private static final int ZZ_PUSHBACK_2BIG = 2;
/* error messages for the codes above */
private static final String[] ZZ_ERROR_MSG = {
"Unknown internal scanner error",
"Error: could not match input",
"Error: pushback value was too large"
};
/**
* ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
*/
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 =
"\4\0\4\1\1\11\15\1\3\11\1\1\1\0\1\1"+
"\4\0\2\1\1\0\4\1\3\0\2\1";
private static int [] zzUnpackAttribute() {
int [] result = new int[44];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAttribute(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/** the input device */
private java.io.Reader zzReader;
/** the current state of the DFA */
private int zzState;
/** the current lexical state */
private int zzLexicalState = YYINITIAL;
/** this buffer contains the current text to be matched and is
the source of the yytext() string */
private CharSequence zzBuffer = "";
/** the textposition at the last accepting state */
private int zzMarkedPos;
/** the current text position in the buffer */
private int zzCurrentPos;
/** startRead marks the beginning of the yytext() string in the buffer */
private int zzStartRead;
/** endRead marks the last character in the buffer, that has been read
from input */
private int zzEndRead;
/**
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
*/
private boolean zzAtBOL = true;
/** zzAtEOF == true <=> the scanner is at the EOF */
private boolean zzAtEOF;
/** denotes if the user-EOF-code has already been executed */
private boolean zzEOFDone;
/**
* Creates a new scanner
*
* @param in the java.io.Reader to read input from.
*/
IJPerfLexer(java.io.Reader in) {
this.zzReader = in;
}
/**
* Unpacks the compressed character translation table.
*
* @param packed the packed character translation table
* @return the unpacked character translation table
*/
private static char [] zzUnpackCMap(String packed) {
int size = 0;
for (int i = 0, length = packed.length(); i < length; i += 2) {
size += packed.charAt(i);
}
char[] map = new char[size];
int i = 0; /* index in packed string */
int j = 0; /* index in unpacked array */
while (i < packed.length()) {
int count = packed.charAt(i++);
char value = packed.charAt(i++);
do map[j++] = value; while (--count > 0);
}
return map;
}
public final int getTokenStart() {
return zzStartRead;
}
public final int getTokenEnd() {
return getTokenStart() + yylength();
}
public void reset(CharSequence buffer, int start, int end, int initialState) {
zzBuffer = buffer;
zzCurrentPos = zzMarkedPos = zzStartRead = start;
zzAtEOF = false;
zzAtBOL = true;
zzEndRead = end;
yybegin(initialState);
}
/**
* Refills the input buffer.
*
* @return {@code false}, iff there was new input.
*
* @exception java.io.IOException if any I/O-Error occurs
*/
private boolean zzRefill() throws java.io.IOException {
return true;
}
/**
* Returns the current lexical state.
*/
public final int yystate() {
return zzLexicalState;
}
/**
* Enters a new lexical state
*
* @param newState the new lexical state
*/
public final void yybegin(int newState) {
zzLexicalState = newState;
}
/**
* Returns the text matched by the current regular expression.
*/
public final CharSequence yytext() {
return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
}
/**
* Returns the character at position {@code pos} from the
* matched text.
*
* It is equivalent to yytext().charAt(pos), but faster
*
* @param pos the position of the character to fetch.
* A value from 0 to yylength()-1.
*
* @return the character at position pos
*/
public final char yycharat(int pos) {
return zzBuffer.charAt(zzStartRead+pos);
}
/**
* Returns the length of the matched text region.
*/
public final int yylength() {
return zzMarkedPos-zzStartRead;
}
/**
* Reports an error that occurred while scanning.
*
* In a wellformed scanner (no or only correct usage of
* yypushback(int) and a match-all fallback rule) this method
* will only be called with things that "Can't Possibly Happen".
* If this method is called, something is seriously wrong
* (e.g. a JFlex bug producing a faulty scanner etc.).
*
* Usual syntax/scanner level error handling should be done
* in error fallback rules.
*
* @param errorCode the code of the errormessage to display
*/
private void zzScanError(int errorCode) {
String message;
try {
message = ZZ_ERROR_MSG[errorCode];
}
catch (ArrayIndexOutOfBoundsException e) {
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
}
throw new Error(message);
}
/**
* Pushes the specified amount of characters back into the input stream.
*
* They will be read again by then next call of the scanning method
*
* @param number the number of characters to be read again.
* This number must not be greater than yylength()!
*/
public void yypushback(int number) {
if ( number > yylength() )
zzScanError(ZZ_PUSHBACK_2BIG);
zzMarkedPos -= number;
}
/**
* Contains user EOF-code, which will be executed exactly once,
* when the end of file is reached
*/
private void zzDoEOF() {
if (!zzEOFDone) {
zzEOFDone = true;
}
}
/**
* Resumes scanning until the next regular expression is matched,
* the end of input is encountered or an I/O-Error occurs.
*
* @return the next token
* @exception java.io.IOException if any I/O-Error occurs
*/
public IElementType advance() throws java.io.IOException {
int zzInput;
int zzAction;
// cached fields:
int zzCurrentPosL;
int zzMarkedPosL;
int zzEndReadL = zzEndRead;
CharSequence zzBufferL = zzBuffer;
int [] zzTransL = ZZ_TRANS;
int [] zzRowMapL = ZZ_ROWMAP;
int [] zzAttrL = ZZ_ATTRIBUTE;
while (true) {
zzMarkedPosL = zzMarkedPos;
zzAction = -1;
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
zzState = ZZ_LEXSTATE[zzLexicalState];
// set up zzAction for empty match case:
int zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
}
zzForAction: {
while (true) {
if (zzCurrentPosL < zzEndReadL) {
zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
zzCurrentPosL += Character.charCount(zzInput);
}
else if (zzAtEOF) {
zzInput = YYEOF;
break zzForAction;
}
else {
// store back cached positions
zzCurrentPos = zzCurrentPosL;
zzMarkedPos = zzMarkedPosL;
boolean eof = zzRefill();
// get translated positions and possibly new buffer
zzCurrentPosL = zzCurrentPos;
zzMarkedPosL = zzMarkedPos;
zzBufferL = zzBuffer;
zzEndReadL = zzEndRead;
if (eof) {
zzInput = YYEOF;
break zzForAction;
}
else {
zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
zzCurrentPosL += Character.charCount(zzInput);
}
}
int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(zzInput) ];
if (zzNext == -1) break zzForAction;
zzState = zzNext;
zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
if ( (zzAttributes & 8) == 8 ) break zzForAction;
}
}
}
// store back cached position
zzMarkedPos = zzMarkedPosL;
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
zzDoEOF();
return null;
}
else {
switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
case 1:
{ yybegin(WAITING_INPUT); return IJPerfElementTypes.COMMAND;
}
// fall through
case 13: break;
case 2:
{ yybegin(YYINITIAL); return TokenType.WHITE_SPACE;
}
// fall through
case 14: break;
case 3:
{ return TokenType.BAD_CHARACTER;
}
// fall through
case 15: break;
case 4:
{ yybegin(WAITING_LAST_OPTION); return IJPerfElementTypes.IDENTIFIER;
}
// fall through
case 16: break;
case 5:
{ yybegin(WAITING_LAST_OPTION); return TokenType.WHITE_SPACE;
}
// fall through
case 17: break;
case 6:
{ yybegin(WAITING_LAST_OPTION); return IJPerfElementTypes.FILE_PATH;
}
// fall through
case 18: break;
case 7:
{ yybegin(WAITING_LAST_OPTION); return IJPerfElementTypes.NUMBER;
}
// fall through
case 19: break;
case 8:
{ yybegin(YYINITIAL); return IJPerfElementTypes.TEXT;
}
// fall through
case 20: break;
case 9:
{ yybegin(WAITING_LAST_OPTION); return IJPerfElementTypes.OPTIONS_SEPARATOR;
}
// fall through
case 21: break;
case 10:
{ yybegin(WAITING_LAST_OPTION); return IJPerfElementTypes.ASSIGNMENT_OPERATOR;
}
// fall through
case 22: break;
case 11:
{ yybegin(WAITING_TEXT); return IJPerfElementTypes.PIPE;
}
// fall through
case 23: break;
case 12:
{ yybegin(YYINITIAL); return IJPerfElementTypes.COMMENT;
}
// fall through
case 24: break;
default:
zzScanError(ZZ_NO_MATCH);
}
}
}
}
}
@@ -0,0 +1,209 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.parser;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiBuilder.Marker;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import static com.intellij.lang.parser.GeneratedParserUtilBase.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.lang.ASTNode;
import com.intellij.psi.tree.TokenSet;
import com.intellij.lang.PsiParser;
import com.intellij.lang.LightPsiParser;
@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"})
public class IJPerfParser implements PsiParser, LightPsiParser {
public ASTNode parse(IElementType t, PsiBuilder b) {
parseLight(t, b);
return b.getTreeBuilt();
}
public void parseLight(IElementType t, PsiBuilder b) {
boolean r;
b = adapt_builder_(t, b, this, null);
Marker m = enter_section_(b, 0, _COLLAPSE_, null);
r = parse_root_(t, b);
exit_section_(b, 0, m, t, r, true, TRUE_CONDITION);
}
protected boolean parse_root_(IElementType t, PsiBuilder b) {
return parse_root_(t, b, 0);
}
static boolean parse_root_(IElementType t, PsiBuilder b, int l) {
return scriptFile(b, l + 1);
}
/* ********************************************************** */
// commandName optionList?
public static boolean commandLine(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "commandLine")) return false;
if (!nextTokenIs(b, COMMAND)) return false;
boolean r;
Marker m = enter_section_(b);
r = commandName(b, l + 1);
r = r && commandLine_1(b, l + 1);
exit_section_(b, m, COMMAND_LINE, r);
return r;
}
// optionList?
private static boolean commandLine_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "commandLine_1")) return false;
optionList(b, l + 1);
return true;
}
/* ********************************************************** */
// COMMAND
public static boolean commandName(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "commandName")) return false;
if (!nextTokenIs(b, COMMAND)) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, COMMAND);
exit_section_(b, m, COMMAND_NAME, r);
return r;
}
/* ********************************************************** */
// NUMBER PIPE TEXT
public static boolean delayTypingOption(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "delayTypingOption")) return false;
if (!nextTokenIs(b, NUMBER)) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeTokens(b, 0, NUMBER, PIPE, TEXT);
exit_section_(b, m, DELAY_TYPING_OPTION, r);
return r;
}
/* ********************************************************** */
// NUMBER OPTIONS_SEPARATOR NUMBER
public static boolean gotoOption(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "gotoOption")) return false;
if (!nextTokenIs(b, NUMBER)) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeTokens(b, 0, NUMBER, OPTIONS_SEPARATOR, NUMBER);
exit_section_(b, m, GOTO_OPTION, r);
return r;
}
/* ********************************************************** */
// simpleOption | gotoOption | delayTypingOption | FILE_PATH | NUMBER
public static boolean option(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "option")) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, OPTION, "<option>");
r = simpleOption(b, l + 1);
if (!r) r = gotoOption(b, l + 1);
if (!r) r = delayTypingOption(b, l + 1);
if (!r) r = consumeToken(b, FILE_PATH);
if (!r) r = consumeToken(b, NUMBER);
exit_section_(b, l, m, r, false, null);
return r;
}
/* ********************************************************** */
// option (OPTIONS_SEPARATOR option)*
public static boolean optionList(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "optionList")) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, OPTION_LIST, "<option list>");
r = option(b, l + 1);
r = r && optionList_1(b, l + 1);
exit_section_(b, l, m, r, false, null);
return r;
}
// (OPTIONS_SEPARATOR option)*
private static boolean optionList_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "optionList_1")) return false;
while (true) {
int c = current_position_(b);
if (!optionList_1_0(b, l + 1)) break;
if (!empty_element_parsed_guard_(b, "optionList_1", c)) break;
}
return true;
}
// OPTIONS_SEPARATOR option
private static boolean optionList_1_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "optionList_1_0")) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, OPTIONS_SEPARATOR);
r = r && option(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
/* ********************************************************** */
// statement*
static boolean scriptFile(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "scriptFile")) return false;
while (true) {
int c = current_position_(b);
if (!statement(b, l + 1)) break;
if (!empty_element_parsed_guard_(b, "scriptFile", c)) break;
}
return true;
}
/* ********************************************************** */
// IDENTIFIER (ASSIGNMENT_OPERATOR (IDENTIFIER|NUMBER|FILE_PATH))?
public static boolean simpleOption(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "simpleOption")) return false;
if (!nextTokenIs(b, IDENTIFIER)) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, IDENTIFIER);
r = r && simpleOption_1(b, l + 1);
exit_section_(b, m, SIMPLE_OPTION, r);
return r;
}
// (ASSIGNMENT_OPERATOR (IDENTIFIER|NUMBER|FILE_PATH))?
private static boolean simpleOption_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "simpleOption_1")) return false;
simpleOption_1_0(b, l + 1);
return true;
}
// ASSIGNMENT_OPERATOR (IDENTIFIER|NUMBER|FILE_PATH)
private static boolean simpleOption_1_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "simpleOption_1_0")) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, ASSIGNMENT_OPERATOR);
r = r && simpleOption_1_0_1(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
// IDENTIFIER|NUMBER|FILE_PATH
private static boolean simpleOption_1_0_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "simpleOption_1_0_1")) return false;
boolean r;
r = consumeToken(b, IDENTIFIER);
if (!r) r = consumeToken(b, NUMBER);
if (!r) r = consumeToken(b, FILE_PATH);
return r;
}
/* ********************************************************** */
// commandLine|COMMENT
public static boolean statement(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "statement")) return false;
if (!nextTokenIs(b, "<statement>", COMMAND, COMMENT)) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, STATEMENT, "<statement>");
r = commandLine(b, l + 1);
if (!r) r = consumeToken(b, COMMENT);
exit_section_(b, l, m, r, false, null);
return r;
}
}
@@ -0,0 +1,16 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfCommandLine extends PsiElement {
@NotNull
IJPerfCommandName getCommandName();
@Nullable
IJPerfOptionList getOptionList();
}
@@ -0,0 +1,17 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
public interface IJPerfCommandName extends PsiNameIdentifierOwner {
PsiElement setName(@NotNull String name);
String getName();
PsiElement getNameIdentifier();
}
@@ -0,0 +1,10 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfDelayTypingOption extends PsiElement {
}
@@ -0,0 +1,60 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.PsiElement;
import com.intellij.lang.ASTNode;
import com.jetbrains.performancePlugin.lang.psi.impl.*;
public interface IJPerfElementTypes {
IElementType COMMAND_LINE = new IJPerfElementType("COMMAND_LINE");
IElementType COMMAND_NAME = new IJPerfElementType("COMMAND_NAME");
IElementType DELAY_TYPING_OPTION = new IJPerfElementType("DELAY_TYPING_OPTION");
IElementType GOTO_OPTION = new IJPerfElementType("GOTO_OPTION");
IElementType OPTION = new IJPerfElementType("OPTION");
IElementType OPTION_LIST = new IJPerfElementType("OPTION_LIST");
IElementType SIMPLE_OPTION = new IJPerfElementType("SIMPLE_OPTION");
IElementType STATEMENT = new IJPerfElementType("STATEMENT");
IElementType ASSIGNMENT_OPERATOR = new IJPerfTokenType("ASSIGNMENT_OPERATOR");
IElementType COMMAND = new IJPerfTokenType("COMMAND");
IElementType COMMENT = new IJPerfTokenType("COMMENT");
IElementType FILE_PATH = new IJPerfTokenType("FILE_PATH");
IElementType IDENTIFIER = new IJPerfTokenType("IDENTIFIER");
IElementType NUMBER = new IJPerfTokenType("NUMBER");
IElementType OPTIONS_SEPARATOR = new IJPerfTokenType("OPTIONS_SEPARATOR");
IElementType PIPE = new IJPerfTokenType("PIPE");
IElementType TEXT = new IJPerfTokenType("TEXT");
class Factory {
public static PsiElement createElement(ASTNode node) {
IElementType type = node.getElementType();
if (type == COMMAND_LINE) {
return new IJPerfCommandLineImpl(node);
}
else if (type == COMMAND_NAME) {
return new IJPerfCommandNameImpl(node);
}
else if (type == DELAY_TYPING_OPTION) {
return new IJPerfDelayTypingOptionImpl(node);
}
else if (type == GOTO_OPTION) {
return new IJPerfGotoOptionImpl(node);
}
else if (type == OPTION) {
return new IJPerfOptionImpl(node);
}
else if (type == OPTION_LIST) {
return new IJPerfOptionListImpl(node);
}
else if (type == SIMPLE_OPTION) {
return new IJPerfSimpleOptionImpl(node);
}
else if (type == STATEMENT) {
return new IJPerfStatementImpl(node);
}
throw new AssertionError("Unknown element type: " + type);
}
}
}
@@ -0,0 +1,10 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfGotoOption extends PsiElement {
}
@@ -0,0 +1,19 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfOption extends PsiElement {
@Nullable
IJPerfDelayTypingOption getDelayTypingOption();
@Nullable
IJPerfGotoOption getGotoOption();
@Nullable
IJPerfSimpleOption getSimpleOption();
}
@@ -0,0 +1,13 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfOptionList extends PsiElement {
@NotNull
List<IJPerfOption> getOptionList();
}
@@ -0,0 +1,10 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfSimpleOption extends PsiElement {
}
@@ -0,0 +1,13 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface IJPerfStatement extends PsiElement {
@Nullable
IJPerfCommandLine getCommandLine();
}
@@ -0,0 +1,51 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
public class IJPerfVisitor extends PsiElementVisitor {
public void visitCommandLine(@NotNull IJPerfCommandLine o) {
visitPsiElement(o);
}
public void visitCommandName(@NotNull IJPerfCommandName o) {
visitPsiNameIdentifierOwner(o);
}
public void visitDelayTypingOption(@NotNull IJPerfDelayTypingOption o) {
visitPsiElement(o);
}
public void visitGotoOption(@NotNull IJPerfGotoOption o) {
visitPsiElement(o);
}
public void visitOption(@NotNull IJPerfOption o) {
visitPsiElement(o);
}
public void visitOptionList(@NotNull IJPerfOptionList o) {
visitPsiElement(o);
}
public void visitSimpleOption(@NotNull IJPerfSimpleOption o) {
visitPsiElement(o);
}
public void visitStatement(@NotNull IJPerfStatement o) {
visitPsiElement(o);
}
public void visitPsiNameIdentifierOwner(@NotNull PsiNameIdentifierOwner o) {
visitElement(o);
}
public void visitPsiElement(@NotNull PsiElement o) {
visitElement(o);
}
}
@@ -0,0 +1,42 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfCommandLineImpl extends ASTWrapperPsiElement implements IJPerfCommandLine {
public IJPerfCommandLineImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitCommandLine(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
@Override
@NotNull
public IJPerfCommandName getCommandName() {
return findNotNullChildByClass(IJPerfCommandName.class);
}
@Override
@Nullable
public IJPerfOptionList getOptionList() {
return findChildByClass(IJPerfOptionList.class);
}
}
@@ -0,0 +1,45 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfCommandNameImpl extends ASTWrapperPsiElement implements IJPerfCommandName {
public IJPerfCommandNameImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitCommandName(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
@Override
public PsiElement setName(@NotNull String name) {
return IJPerfPsiImplUtil.setName(this, name);
}
@Override
public String getName() {
return IJPerfPsiImplUtil.getName(this);
}
@Override
public PsiElement getNameIdentifier() {
return IJPerfPsiImplUtil.getNameIdentifier(this);
}
}
@@ -0,0 +1,30 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfDelayTypingOptionImpl extends ASTWrapperPsiElement implements IJPerfDelayTypingOption {
public IJPerfDelayTypingOptionImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitDelayTypingOption(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
}
@@ -0,0 +1,30 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfGotoOptionImpl extends ASTWrapperPsiElement implements IJPerfGotoOption {
public IJPerfGotoOptionImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitGotoOption(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
}
@@ -0,0 +1,48 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfOptionImpl extends ASTWrapperPsiElement implements IJPerfOption {
public IJPerfOptionImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitOption(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
@Override
@Nullable
public IJPerfDelayTypingOption getDelayTypingOption() {
return findChildByClass(IJPerfDelayTypingOption.class);
}
@Override
@Nullable
public IJPerfGotoOption getGotoOption() {
return findChildByClass(IJPerfGotoOption.class);
}
@Override
@Nullable
public IJPerfSimpleOption getSimpleOption() {
return findChildByClass(IJPerfSimpleOption.class);
}
}
@@ -0,0 +1,36 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfOptionListImpl extends ASTWrapperPsiElement implements IJPerfOptionList {
public IJPerfOptionListImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitOptionList(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
@Override
@NotNull
public List<IJPerfOption> getOptionList() {
return PsiTreeUtil.getChildrenOfTypeAsList(this, IJPerfOption.class);
}
}
@@ -0,0 +1,30 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfSimpleOptionImpl extends ASTWrapperPsiElement implements IJPerfSimpleOption {
public IJPerfSimpleOptionImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitSimpleOption(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
}
@@ -0,0 +1,36 @@
// This is a generated file. Not intended for manual editing.
package com.jetbrains.performancePlugin.lang.psi.impl;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import static com.jetbrains.performancePlugin.lang.psi.IJPerfElementTypes.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.jetbrains.performancePlugin.lang.psi.*;
public class IJPerfStatementImpl extends ASTWrapperPsiElement implements IJPerfStatement {
public IJPerfStatementImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull IJPerfVisitor visitor) {
visitor.visitStatement(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof IJPerfVisitor) accept((IJPerfVisitor)visitor);
else super.accept(visitor);
}
@Override
@Nullable
public IJPerfCommandLine getCommandLine() {
return findChildByClass(IJPerfCommandLine.class);
}
}
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="kotlin-stdlib-jdk8" level="project" />
<orderEntry type="module" module-name="intellij.platform.analysis" />
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
<orderEntry type="library" scope="PROVIDED" name="cli-parser" level="project" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.diagnostic" />
<orderEntry type="library" name="StreamEx" level="project" />
<orderEntry type="library" name="jackson" level="project" />
<orderEntry type="library" name="jackson-module-kotlin" level="project" />
<orderEntry type="library" name="jackson-databind" level="project" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.platform.statistics" />
<orderEntry type="module" module-name="intellij.platform.execution.impl" />
<orderEntry type="module-library">
<library name="github.oshi.core" type="repository">
<properties maven-id="com.github.oshi:oshi-core:6.2.2">
<exclude>
<dependency maven-id="net.java.dev.jna:jna-platform" />
<dependency maven-id="net.java.dev.jna:jna" />
<dependency maven-id="org.slf4j:slf4j-api" />
</exclude>
</properties>
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/oshi/oshi-core/6.2.2/oshi-core-6.2.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/oshi/oshi-core/6.2.2/oshi-core-6.2.2-sources.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="opentelemetry" level="project" />
<orderEntry type="library" name="kotlinx-coroutines-jdk8" level="project" />
<orderEntry type="module" module-name="intellij.platform.diagnostic.telemetry" />
<orderEntry type="module" module-name="intellij.platform.vcs.log.impl" />
<orderEntry type="module" module-name="intellij.platform.vcs.log" />
</component>
</module>
@@ -0,0 +1,81 @@
<idea-plugin package="com.jetbrains.performancePlugin">
<id>com.jetbrains.performancePlugin</id>
<name>Performance Testing</name>
<vendor>JetBrains</vendor>
<category>Other Tools</category>
<resource-bundle>messages.PerformanceTestingBundle</resource-bundle>
<description><![CDATA[
Plugin for automated execution of test scripts, capturing performance snapshots
and gathering performance statistics.
]]></description>
<dependencies>
<plugin id="com.intellij.modules.lang"/>
</dependencies>
<actions>
<group id="StartProfileGroup">
<add-to-group group-id="HelpDiagnosticTools" anchor="last"/>
</group>
<group id="IndexingDiagnosticGroup">
<action class="com.jetbrains.performancePlugin.actions.OpenIndexingDiagnosticsAction" id="performancePlugin.OpenIndexingDiagnosticsAction"/>
<add-to-group group-id="HelpDiagnosticTools" anchor="after" relative-to-action="CollectTroubleshootingInformation"/>
</group>
<group id="DiagnosticGroup">
<separator/>
<action class="com.jetbrains.performancePlugin.actions.ExecuteScriptAction" id="performancePlugin.ExecuteScriptAction"/>
<separator/>
<add-to-group group-id="HelpDiagnosticTools" anchor="last"/>
</group>
</actions>
<extensionPoints>
<extensionPoint interface="com.jetbrains.performancePlugin.CommandProvider" name="commandProvider" dynamic="true"/>
<extensionPoint interface="com.jetbrains.performancePlugin.RunCallbackHandler" name="runCallbackHandler"/>
<extensionPoint interface="com.jetbrains.performancePlugin.profilers.SnapshotOpener" name="snapshotOpener" dynamic="true"/>
<extensionPoint interface="com.jetbrains.performancePlugin.profilers.Profiler" name="profiler" dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<troubleInfoCollector implementation="com.jetbrains.performancePlugin.PerformanceTroubleInfoCollector"/>
<troubleInfoCollector implementation="com.jetbrains.performancePlugin.utils.HardwareCollector"/>
<fileType name="IntegrationPerformanceTest"
implementationClass="com.jetbrains.performancePlugin.lang.IJPerfFileType"
fieldName="INSTANCE" language="IntegrationPerformanceTest" extensions="ijperf"/>
<lang.parserDefinition language="IntegrationPerformanceTest"
implementationClass="com.jetbrains.performancePlugin.lang.IJPerfParserDefinition"/>
<lang.syntaxHighlighterFactory language="IntegrationPerformanceTest"
implementationClass="com.jetbrains.performancePlugin.lang.IJPerfSyntaxHighlighterFactory"/>
<completion.contributor language="IntegrationPerformanceTest"
implementationClass="com.jetbrains.performancePlugin.lang.completion.IJPerfCompletionContributor"/>
<annotator language="IntegrationPerformanceTest" implementationClass="com.jetbrains.performancePlugin.lang.inspections.IJPerfStartStopProfileInspection"/>
<documentationProvider implementation="com.jetbrains.performancePlugin.lang.doc.IJPerfDocumentationProvider"/>
<startupActivity implementation="com.jetbrains.performancePlugin.ProjectLoaded"/>
<postStartupActivity implementation="com.jetbrains.performancePlugin.PerformanceTestTotalTimeTimer" />
<applicationInitializedListener implementation="com.jetbrains.performancePlugin.ProjectLoaded"/>
<notificationGroup id="PerformancePlugin" displayType="BALLOON" bundle="messages.PerformanceTestingBundle" key="notification.group.performance.plugin"/>
<registryKey key="performance.plugin.playback.runner.useDirectActionCall" description="Setup PlaybackRunner to use direct action calls over key strokes for actions in Playback framework" defaultValue="false" restartRequired="false"/>
</extensions>
<applicationListeners>
<listener class="com.jetbrains.performancePlugin.ProjectLoaded$MyAppLifecycleListener" topic="com.intellij.ide.AppLifecycleListener"/>
</applicationListeners>
<projectListeners>
<listener class="com.jetbrains.performancePlugin.ProjectIndexingComponent"
topic="com.intellij.openapi.project.DumbService$DumbModeListener"/>
</projectListeners>
<extensions defaultExtensionNs="com.jetbrains.performancePlugin">
<commandProvider implementation="com.jetbrains.performancePlugin.BaseCommandProvider"/>
<runCallbackHandler implementation="com.jetbrains.performancePlugin.BaseRunCallbackHandler"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,13 @@
<html>
<title>
Command runs code inspection using custom tool
</title>
<body>
<p>
Syntax: %InspectCodeEx [parameters]
</p>
<p>
Example: %InspectCodeEx -directory app -toolShortName RubyResolve
</p>
</body>
</html>
@@ -0,0 +1,11 @@
<html>
<title>Command to add file to project</title>
<body>
<p>
Create file in project. File name and path to project get from parameters.
</p>
<p>
Example: %addFile C:\Users\username\intellij, fileName.java
</p>
</body>
</html>
@@ -0,0 +1,14 @@
<html>
<title>Command invokes alt+enter combination</title>
<body>
<p>
Simulate invocation of alt+enter and pressing item in the list defined by parameter.
</p>
<p>
Syntax: %altEnter [parameters]
</p>
<p>
Example: %altEnter Find cause
</p>
</body>
</html>
@@ -0,0 +1,14 @@
<html>
<title>Command builds all modules in a project</title>
<body>
<p>
builds all modules in a project. There are two modes: BUILD and REBUILD
</p>
<p>
Syntax: %buildProject [mode]
</p>
<p>
Example: %buildProject REBUILD
</p>
</body>
</html>
@@ -0,0 +1,14 @@
<html>
<title>Command checks whether an opened document in the editor has errors</title>
<body>
<p>
Simulate analyzing of document on errors (red code). The command finds only ERROR severity
</p>
<p>
Syntax: %checkOnRedCode
</p>
<p>
Example: %checkOnRedCode
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>Command invalidates indexes</title>
<body>
<p>
Simulate invocation of 'Invalidate caches' action
</p>
</body>
</html>
@@ -0,0 +1,9 @@
<html>
<title>Command compares stored indices with indices on the disk</title>
<body>
<p>
Compares the stored indices with the last created indices, which are in the current moment on the disk.
The folder with the stored indices should be defined by the parameter '-Dcompare.indices.command.stored.indexes.directory'.
</p>
</body>
</html>
@@ -0,0 +1,14 @@
<html>
<title>Command types text with some delay between typing</title>
<body>
<p>
Text and delay are being set as parameters
</p>
<p>
Syntax: %delayType &lt;delay in ms&gt;|&lt;Text to type&gt;
</p>
<p>
Example: %delayType 150|Sample text for typing scenario
</p>
</body>
</html>
@@ -0,0 +1,11 @@
<html>
<title>Command to delete file from project</title>
<body>
<p>
Delete file from project if file exists. File name and path to project get from parameters.
</p>
<p>
Example: %deleteFile C:\Users\username\intellij, fileName.java
</p>
</body>
</html>
@@ -0,0 +1,5 @@
<html>
<title>Command invokes simple completion</title>
<body>
</body>
</html>
@@ -0,0 +1,9 @@
<html>
<title>Command runs highlighting pass</title>
<body>
Runs all highlighting passes. Can't be invoked multiple times in a row since it drops caches before run.
<p>
Example: %doHighlighting
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>Command runs local inspection</title>
<body>
<p>
Runs local inspection using <i>DaemonCodeAnalyzer</i>
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>Command exits app</title>
<body>
<p>
Closes IDE
</p>
</body>
</html>
@@ -0,0 +1,10 @@
<html>
<title>Command to test how long does it take to open context menu in project view</title>
<body>
Command to test how long does it take to open context menu in project view.
The popup won't be shown but all heavy methods that may cause context menu long load will be invoked
<p>
Example: %expandProjectMenu
</p>
</body>
</html>
@@ -0,0 +1,13 @@
<html>
<title>
Command looks for psi element usages
</title>
<body>
<p>
Looks for and count psi usages of element under caret
</p>
<p>
Example: %findUsages
</p>
</body>
</html>
@@ -0,0 +1,16 @@
<html>
<title>
Command switches to another branch
</title>
<body>
<p>
Call from the project dir git checkout command in command line
</p>
<p>
Syntax: %gitCheckout [branch]
</p>
<p>
Example: %gitCheckout master
</p>
</body>
</html>
@@ -0,0 +1,16 @@
<html>
<title>
Command puts caret to the line and column
</title>
<body>
<p>
Puts the caret to the location defined by parameters
</p>
<p>
Syntax: %goto &lt;line&gt; &lt;column&gt;
</p>
<p>
Example: %goto 25 17
</p>
</body>
</html>
@@ -0,0 +1,16 @@
<html>
<title>
Command runs 'Inspect code' action
</title>
<body>
<p>
Run 'Inspect code' command in context defined by parameters
</p>
<p>
Syntax: %inspectCode [directory]
</p>
<p>
Example: %inspectCode src
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>
Command dumps memory
</title>
<body>
</body>
</html>
@@ -0,0 +1,10 @@
<html>
<title>
Command opens file
</title>
<body>
<p>
Example: %openFile MyClass.php
</p>
</body>
</html>
@@ -0,0 +1,22 @@
<html>
<title>Command to open a new project</title>
<body>
To open a project in the current frame closing the current one:
<p>
Example: %openProject C:\Users\username\intellij
</p>
<br/>
To open a project in a new frame and don't close the current one:
<p>
Example: %openProject C:\Users\username\intellij false
</p>
If you do the following:
%openProject /tmp/a false
%openProject /tmp/b false
%openProject /tmp/a false
In the end, the same project a will be active and there will be 2 window frames.
</body>
</html>
@@ -0,0 +1,16 @@
<html>
<title>
Command simulates pressing a keyboard key
</title>
<body>
<p>
Only defined set of key is supported for now: "ENTER", "BACKSPACE", "TAB" and "ESCAPE"
</p>
<p>
Syntax: %pressKey &lt;KEY&gt;
</p>
<p>
Example: %pressKey ENTER
</p>
</body>
</html>
@@ -0,0 +1,15 @@
<html>
<title>Command declares project used for test</title>
<body>
Name of project that is being used for test
<p>
Command must be followed by project name and optionally by project archive</b>.
</p>
<p>
Syntax: %%project &lt;projetName&gt; [projectArchive]
</p>
<p>
Example: %%project mpdf mpdf-5.6.zip
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>
Command reformats project
</title>
<body>
</body>
</html>
@@ -0,0 +1,11 @@
<html>
<title>Command to reload project files from disk</title>
<body>
<p>
Command to reload project files from disk.
</p>
<p>
Example: %reloadFiles
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>
</title>
<body>
</body>
</html>
@@ -0,0 +1,17 @@
<html>
<title>
Command runs specified configuration
</title>
<body>
<p>
Command runs specified configuration if configuration is absent prints all available for the project.
There are two modes: TILL_STARTED and TILL_TERMINATED
</p>
<p>
Syntax: %runConfiguration [mode] &lt;configurationName&gt;
</p>
<p>
Example: %runConfiguration TILL_STARTED IDEA
</p>
</body>
</html>
@@ -0,0 +1,13 @@
<html>
<title>
Command runs single inspection
</title>
<body>
<p>
Runs single inspection by short name
</p>
<p>
Syntax: %runSingleInspection &lt;name&gt;
</p>
</body>
</html>
@@ -0,0 +1,14 @@
<html>
<title>Command calls search everywhere with the given text and tab</title>
<body>
<p>
Simulate calling search everywhere action with a given text and specified tab.
</p>
<p>
Syntax: %searchEverywhere [tab]|[text]
</p>
<p>
Example: %searchEverywhere symbol|EditorIm
</p>
</body>
</html>
@@ -0,0 +1,9 @@
<html>
<title>Command to select file in project view</title>
<body>
Command to select file in project view.
<p>
Example: %selectFileInProjectView MyClass.php
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>
</title>
<body>
</body>
</html>
@@ -0,0 +1,17 @@
<html>
<title>Command starts async or yourkit profiler</title>
<body>
<p>
Command starts async or yourkit profiler based on system property <i>integrationTests.profiler</i>.
</p>
<p>
Command must be followed by activity name and optionally by parameters
</p>
<p>
Syntax: %startProfile &lt;activityName&gt; [parameters]
</p>
<p>
Example: %startProfile magento_inspection event=alloc
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>
</title>
<body>
</body>
</html>
@@ -0,0 +1,17 @@
<html>
<title>Command stops profiler</title>
<body>
<p>
Command stops async or yourkit profiler based on system property <i>integrationTests.profiler</i>.
</p>
<p>
In case when yourkit profiler was used parameters are ignored
</p>
<p>
Syntax: %stopProfile [parameters]
</p>
<p>
Example: %stopProfile collapsed,flamegraph,traces=5000
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>Command stores created indices</title>
<body>
<p>
Stores created during indexing indices in the defined by the parameter '-Dstore.indices.command.stored.indexes.dump.directory' directory.
</p>
</body>
</html>
@@ -0,0 +1,16 @@
<html>
<title>
Command takes screenshot
</title>
<body>
<p>
Takes JPG screenshot of current screen to specified file.
</p>
<p>
Syntax: %takeScreenshot &lt;fullPathToFile&gt;
</p>
<p>
Example: %takeScreenshot ./myScreenshot.jpg
</p>
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<title>
</title>
<body>
</body>
</html>
@@ -0,0 +1,79 @@
profiling.compress.snapshot.progress=Compressing snapshot
profiling.capture.snapshot.success=Captured snapshot ''{0}'' is placed in user home directory.
profiling.capture.snapshot.error=Failed to capture snapshot: {0}
profiling.capture.snapshot.action.showInFolder=Show in {0}
profiling.capture.snapshot.action.open=Open
command.goto.finish=GoTo was executed
command.inspection.extension=No files with extension:
command.inspection.finish=Inspection was executed
command.inspection.finish.result=with {0} found problems
command.file.not.found=File not found: {0}
command.file.opened=File opened successfully: {0}
command.reformat.finish=Reformat was executed
command.start.error=Start profile should contain key: %startProfile <key>
command.build.finish=Build finished successfully
command.build.finish.with.errors=Build finished with errors
command.build.finish.with.compilation.errors=Build finished with compilation errors
startup.noscript=Script file {0} doesn''t exist.
startup.silent=Performance Plugin is in silent mode
startup.script.read.error=Script file can\'t be read.
callback.stop=Performance Plugin was stopped
summary.action=&Open Support Request
summary.link=https://intellij-support.jetbrains.com/anonymous_requests/new
summary.link.rider=https://rider-support.jetbrains.com/hc/en-us/requests/new
finish.path=Snapshot path is: {0}
finish.title=Snapshot Was Captured
finish.open=&Open Snapshot in {0}
delay.notification.title=Performance Plugin finished execution
delay.notification.message=Total time: {0} ms \n Average delay: {1} ms Max delay: {2} ms
command.singleInspection.noinspection=Cannot run inspection, inspection doesn''t exists {0}
select.scenario=Please select &scenario:
executor.title=Performance Issue Reporter
execute.script.button=&Execute
typing.scenario=Typing
formatting.scenario=Formatting
local.inspection.scenario=Local Inspection
input.below.parameters.for.async.profiler=Input below parameters for async profiler
action.performancePlugin.ExecuteScriptAction.text=Execute Performance Script\u2026
action.performancePlugin.ExecuteScriptAction.description=Execute performance test script
action.performancePlugin.OpenIndexingDiagnosticsAction.text=Open Indexing Diagnostics
action.performancePlugin.OpenIndexingDiagnosticsAction.description=Open Indexing Diagnostics for the current project
comparing.indexes=Comparing Indexes\u2026
storing.indexes=Storing indexes\u2026
flushing.indexes=Flushing indexes\u2026
storing.indexes.copying=Copying current indexes\u2026
checking.shared.indexes=Checking Shared Indexes\u2026
checking.shared.indexes.collecting.shared.files=Collecting files available in shared indexes\u2026
checking.shared.indexes.collecting.all.indexed.files=Collecting all indexed files\u2026
compare.indexes.comparing.index=Comparing current and stored indexes of {0}\u2026
compare.indexes.comparing.forward.index=Comparing forward index of the current and stored indexes of {0}\u2026
compare.indexes.comparing.inverted.index=Comparing inverted index of the current and stored indexes of {0}\u2026
compare.indexes.comparing.inverted.index.collecting.keys=Collecting keys of the inverted index\u2026
compare.indexes.resolving.files=Resolving files for comparison\u2026
compare.indexes.preparing.indexes=Preparing indexes\u2026
button.text.cancel=Not Now
filetype.ijperformance.test.description=Script of integration performance test
filetype.ijperformance.test.display.name=ijperf script
task.title.executing.performance.script=Executing performance script
dumping.project.files=Dumping Project Files\u2026
comparing.project.files=Comparing Project Files\u2026
comparing.project.files.for.0=Comparing Project Files for {0}\u2026
intention.add.stopcommand=Add `%stopProfile` command
inspection.message.activity.started.but.never.closed.with.stopprofile=`{0}` activity started, but never closed with `%stopProfile`
inspection.message.two.startprofile.commands.cant.follow.each.other=`%startProfile` commands can''t follow each other. Add `%stopProfile` before `{0}`
notification.group.performance.plugin=IDE self-profiling
@@ -0,0 +1,70 @@
package com.jetbrains.performancePlugin;
import com.jetbrains.performancePlugin.commands.*;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public final class BaseCommandProvider implements CommandProvider {
@Override
public @NotNull Map<String, CreateCommand> getCommands() {
return Map.<String, CreateCommand>ofEntries(
Map.entry(RunConfigurationCommand.PREFIX, RunConfigurationCommand::new),
Map.entry(TakeScreenshotCommand.PREFIX, TakeScreenshotCommand::new),
Map.entry(OpenFileCommand.PREFIX, OpenFileCommand::new),
Map.entry(StartProfileCommand.PREFIX, StartProfileCommand::new),
Map.entry(StopProfileCommand.PREFIX, StopProfileCommand::new),
Map.entry(InspectionCommand.PREFIX, InspectionCommand::new),
Map.entry(InspectionCommandEx.PREFIX, InspectionCommandEx::new),
Map.entry(ReformatCommand.PREFIX, ReformatCommand::new),
Map.entry(GoToCommand.PREFIX, GoToCommand::new),
Map.entry(DoLocalInspection.PREFIX, DoLocalInspection::new),
Map.entry(CompletionCommand.PREFIX, CompletionCommand::new),
Map.entry(DelayTypeCommand.PREFIX, DelayTypeCommand::new),
Map.entry(ReplaceTextCommand.PREFIX, ReplaceTextCommand::new),
Map.entry(ExitAppCommand.PREFIX, ExitAppCommand::new),
Map.entry(ExitAppWithTimeoutCommand.PREFIX, ExitAppWithTimeoutCommand::new),
Map.entry(OpenFileWithTerminateCommand.PREFIX, OpenFileWithTerminateCommand::new),
Map.entry(WaitForSmartCommand.PREFIX, WaitForSmartCommand::new),
Map.entry(WaitForVcsLogCommand.PREFIX, WaitForVcsLogCommand::new),
Map.entry(WaitForAsyncRefreshCommand.PREFIX, WaitForAsyncRefreshCommand::new),
Map.entry(SingleInspectionCommand.PREFIX, SingleInspectionCommand::new),
Map.entry(StartPowerSave.PREFIX, StartPowerSave::new),
Map.entry(StopPowerSave.PREFIX, StopPowerSave::new),
Map.entry(MemoryDumpCommand.PREFIX, MemoryDumpCommand::new),
Map.entry(CleanCaches.PREFIX, CleanCaches::new),
Map.entry(FindUsagesCommand.PREFIX, FindUsagesCommand::new),
Map.entry(EnterCommand.PREFIX, EnterCommand::new),
Map.entry(ShowAltEnter.PREFIX, ShowAltEnter::new),
Map.entry(SelectCommand.PREFIX, SelectCommand::new),
Map.entry(CompareIndicesKt.PREFIX, CompareIndices::new),
Map.entry(StoreIndices.PREFIX, StoreIndices::new),
Map.entry(GitCheckoutCommand.PREFIX, GitCheckoutCommand::new),
Map.entry(OpenProjectView.PREFIX, OpenProjectView::new),
Map.entry(MoveDirectoryCommand.PREFIX, MoveDirectoryCommand::new),
Map.entry(RunClassInPlugin.PREFIX, RunClassInPlugin::new),
Map.entry(SetupProjectSdkCommand.PREFIX, SetupProjectSdkCommand::new),
Map.entry(OpenProjectCommand.PREFIX, OpenProjectCommand::new),
Map.entry(CodeAnalysisCommand.PREFIX, CodeAnalysisCommand::new),
Map.entry(DumpProjectFiles.PREFIX, DumpProjectFiles::new),
Map.entry(CompareProjectFiles.PREFIX, CompareProjectFiles::new),
Map.entry(RecordCounterCollectorBaselinesCommand.PREFIX, RecordCounterCollectorBaselinesCommand::new),
Map.entry(RecordStateCollectorsCommand.PREFIX, RecordStateCollectorsCommand::new),
Map.entry(CreateAllServicesAndExtensionsCommand.PREFIX, CreateAllServicesAndExtensionsCommand::new),
Map.entry(RecoveryActionCommand.PREFIX, RecoveryActionCommand::new),
Map.entry(CorruptIndexesCommand.PREFIX, CorruptIndexesCommand::new),
Map.entry(FlushIndexesCommand.PREFIX, FlushIndexesCommand::new),
Map.entry(SearchEverywhereCommand.PREFIX, SearchEverywhereCommand::new),
Map.entry(SelectFileInProjectViewCommand.PREFIX, SelectFileInProjectViewCommand::new),
Map.entry(ExpandProjectMenuCommand.PREFIX, ExpandProjectMenuCommand::new),
Map.entry(DoHighlighting.PREFIX, DoHighlighting::new),
Map.entry(ReloadFilesCommand.PREFIX, ReloadFilesCommand::new),
Map.entry(AddFileCommand.PREFIX, AddFileCommand::new),
Map.entry(DeleteFileCommand.PREFIX, DeleteFileCommand::new),
Map.entry(TestTipsAndFeaturesIntegrationCommand.PREFIX, TestTipsAndFeaturesIntegrationCommand::new),
Map.entry(ExpandMainMenuCommand.PREFIX, ExpandMainMenuCommand::new),
Map.entry(ExpandEditorMenuCommand.PREFIX, ExpandEditorMenuCommand::new),
Map.entry(OpenRandomFileCommand.PREFIX, OpenRandomFileCommand::new)
);
}
}
@@ -0,0 +1,11 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ActionCallback;
public class BaseRunCallbackHandler implements RunCallbackHandler {
@Override
public void patchCommandCallback(Project project, ActionCallback callback) { }
}
@@ -0,0 +1,23 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.ui.playback.PlaybackRunner.StatusCallback;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public class CommandLogger implements StatusCallback {
@Override
public void message(@Nullable PlaybackContext context, String text, Type type) {
if (type == Type.error) {
System.err.println(text);
if (System.getProperty("testscript.filename") != null) {
ApplicationManagerEx.getApplicationEx().exit(true, true);
}
}
else {
System.out.println(text);
}
}
}
@@ -0,0 +1,30 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public interface CommandProvider {
ExtensionPointName<CommandProvider> EP_NAME = new ExtensionPointName<>("com.jetbrains.performancePlugin.commandProvider");
@NotNull Map<@NonNls String, CreateCommand> getCommands();
static @NotNull List<@NonNls String> getAllCommandNames() {
return EP_NAME.getExtensionList().stream()
.flatMap(e -> e.getCommands().keySet().stream()).toList();
}
static @Nullable CreateCommand findCommandCreator(@NonNls @NotNull String commandName) {
return EP_NAME.getExtensionList().stream()
.map(provider -> provider.getCommands().get(commandName))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
}
@@ -0,0 +1,35 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.ActionCallback;
import org.jetbrains.annotations.NotNull;
import static com.jetbrains.performancePlugin.ProjectLoaded.runScript;
public class CommandsRunner {
private static CommandsRunner myCommandsRunner;
private ActionCallback startActionCallback;
synchronized static CommandsRunner getInstance() {
if (myCommandsRunner == null) {
myCommandsRunner = new CommandsRunner();
}
return myCommandsRunner;
}
//TODO: add methode with project in parameter? AT-114
public static void doRunScript(@NotNull String text) {
runScript(ProjectManager.getInstance().getOpenProjects()[0], text);
}
public static void setStartActionCallback(ActionCallback actionCallback) {
getInstance().startActionCallback = actionCallback;
}
public static boolean areCommandsAfterStartFinished() {
if (getInstance().startActionCallback == null) return false;
return getInstance().startActionCallback.isDone();
}
}
@@ -0,0 +1,9 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.ui.playback.PlaybackCommand;
import org.jetbrains.annotations.NotNull;
public interface CreateCommand {
@NotNull
PlaybackCommand invoke(@NotNull String command, int line);
}
@@ -0,0 +1,6 @@
package com.jetbrains.performancePlugin;
public interface OnEndListener<T> {
void onEnd(T t);
}
@@ -0,0 +1,29 @@
package com.jetbrains.performancePlugin;
import com.intellij.diagnostic.telemetry.IJTracer;
import com.intellij.diagnostic.telemetry.TraceManager;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
public class PerformanceTestSpan {
public static final String SPAN_NAME = "performance_test";
public final static IJTracer TRACER = TraceManager.INSTANCE.getTracer("performance-plugin");
private static Span performanceTestSpan;
private static Scope performanceScope;
static void startSpan() {
performanceTestSpan = TRACER.spanBuilder(SPAN_NAME).setNoParent().startSpan();
performanceScope = performanceTestSpan.makeCurrent();
}
static void endSpan() {
if (performanceScope != null) performanceScope.close();
if (performanceTestSpan != null) performanceTestSpan.end();
}
public static Context getContext(){
if (performanceTestSpan != null) return Context.current().with(performanceTestSpan);
return null;
}
}
@@ -0,0 +1,27 @@
package com.jetbrains.performancePlugin;
import com.intellij.ide.AppLifecycleListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
public final class PerformanceTestTotalTimeTimer implements StartupActivity.DumbAware {
public static final String TOTAL_TEST_TIMER_NAME = "test";
@Override
public void runActivity(@NotNull Project project) {
if (ProjectLoaded.TEST_SCRIPT_FILE_PATH != null) {
Timer myTimer = new Timer();
myTimer.start(TOTAL_TEST_TIMER_NAME, true);
MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect();
connection.subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener() {
@Override
public void appWillBeClosed(boolean isRestart) {
myTimer.stop();
}
});
}
}
}
@@ -0,0 +1,26 @@
package com.jetbrains.performancePlugin;
import com.intellij.DynamicBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Supplier;
public final class PerformanceTestingBundle extends DynamicBundle {
@NonNls private static final String BUNDLE = "messages.PerformanceTestingBundle";
private static final PerformanceTestingBundle INSTANCE = new PerformanceTestingBundle();
private PerformanceTestingBundle() { super(BUNDLE); }
@NotNull
public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getMessage(key, params);
}
@NotNull
public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getLazyMessage(key, params);
}
}
@@ -0,0 +1,19 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.project.Project;
import com.intellij.troubleshooting.TroubleInfoCollector;
import com.jetbrains.performancePlugin.utils.StatisticCollector;
import org.jetbrains.annotations.NotNull;
public class PerformanceTroubleInfoCollector implements TroubleInfoCollector {
@NotNull
@Override
public String collectInfo(@NotNull Project project) {
return "=====PERFORMANCE SUMMARY=====\n" + new StatisticCollector(project).collectMetrics(false);
}
@Override
public String toString() {
return "Performance";
}
}
@@ -0,0 +1,102 @@
package com.jetbrains.performancePlugin;
import com.intellij.ide.plugins.CannotUnloadPluginException;
import com.intellij.ide.plugins.DynamicPluginListener;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.playback.PlaybackCommand;
import com.intellij.openapi.ui.playback.PlaybackRunner;
import com.intellij.openapi.ui.playback.commands.AbstractCommand;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public final class PlaybackRunnerExtended extends PlaybackRunner {
public static final String NOTIFICATION_GROUP = "PerformancePlugin";
private Project myProject;
private boolean myStopped;
private final DynamicPluginListener myDynamicPluginListener;
public PlaybackRunnerExtended(String script, StatusCallback callback, @NotNull Project project) {
super(script, callback, Registry.is("performance.plugin.playback.runner.useDirectActionCall", false), false, true);
setProject(project);
myDynamicPluginListener = new DynamicPluginListener() {
@Override
public void checkUnloadPlugin(@NotNull IdeaPluginDescriptor pluginDescriptor) throws CannotUnloadPluginException {
if (PluginId.getId("com.jetbrains.performancePlugin").equals(pluginDescriptor.getPluginId())) {
throw new CannotUnloadPluginException("Cannot unload plugin during playback execution");
}
}
};
}
@Override
public void setProject(@Nullable Project project) {
myProject = project;
if (project != null && !project.isDefault()) {
Disposer.register(project, () -> {
if (project == myProject) {
Disposer.dispose(myOnStop);
}
});
}
}
@Override
@Nullable
public Project getProject() {
return myProject;
}
@Override
protected void subscribeListeners(MessageBusConnection connection) {
super.subscribeListeners(connection);
connection.subscribe(DynamicPluginListener.TOPIC, myDynamicPluginListener);
}
@Override
protected void onStop() {
super.onStop();
myStopped = true;
}
@Nullable
@Override
protected PlaybackCommand createCommand(@NotNull String _command, int line, @NotNull File scriptDir) {
String command = _command.replaceAll("[\r\n]+$", "");
String[] cmdline = command.split("\\s+");
PlaybackCommand playbackCommand = null;
if (cmdline.length > 0) {
String commandName = cmdline[0];
CreateCommand createCommand = CommandProvider.findCommandCreator(commandName);
if (createCommand != null) {
playbackCommand = createCommand.invoke(command, line);
if (playbackCommand instanceof AbstractCommand) {
((AbstractCommand)playbackCommand).setScriptDir(scriptDir);
}
}
}
if (playbackCommand == null) {
playbackCommand = super.createCommand(command, line, scriptDir);
}
return playbackCommand;
}
@Override
public ActionCallback run() {
if (myStopped) {
throw new IllegalStateException("PlaybackRunnerExtended can be run only once.");
}
ActionCallback callback = super.run();
RunCallbackHandler.applyPatchesToCommandCallback(myProject, callback);
return callback;
}
}
@@ -0,0 +1,69 @@
package com.jetbrains.performancePlugin;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.util.Alarm;
import com.intellij.util.AlarmFactory;
import com.jetbrains.performancePlugin.profilers.Profiler;
import com.jetbrains.performancePlugin.profilers.ProfilersController;
import com.jetbrains.performancePlugin.ui.FinishScriptDialog;
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
public final class ProjectIndexingComponent implements DumbService.DumbModeListener {
public static final String PROFILE_INDEXING_COMPONENT = "performancePlugin.isProfileIndexing";
public static final String PROFILE_WITH_ASYNC = "performancePlugin.isProfileIndexingWithAsync";
@NotNull private final Project project;
private static final int TIMEOUT = 500;
private final Alarm myAlarm = AlarmFactory.getInstance().create();
ProjectIndexingComponent(@NotNull Project project) {
this.project = project;
}
@Override
public void enteredDumbMode() {
try {
if (isProfilingEnabled()) {
PropertiesComponent.getInstance(project).setValue(PROFILE_INDEXING_COMPONENT, false);
PropertiesComponent.getInstance(project).setValue(PROFILE_WITH_ASYNC, false);
Profiler.getCurrentProfilerHandler(project).startProfiling(project.getName(), new ArrayList<>());
DumbService.getInstance(project).runReadActionInSmartMode(()->{
runScriptAfterDumb(project);
});
}
}
catch (Exception e) {
new ActionCallbackProfilerStopper().reject(e.getMessage());
}
}
private void runScriptAfterDumb(Project project) {
DumbService.getInstance(project).smartInvokeLater(() -> myAlarm.addRequest(() -> {
if (DumbService.isDumb(project)) {
runScriptAfterDumb(project);
}
else {
ProfilersController.getInstance();
if (Profiler.isAnyProfilingStarted()) {
try {
ProfilersController.getInstance().getCurrentProfilerHandler().stopProfiling(new ArrayList<>());
ProfilersController.getInstance().setStoppedByScript(true);
}
catch (Exception e) {
new ActionCallbackProfilerStopper().setRejected();
}
ApplicationManager.getApplication().invokeLater(() -> new FinishScriptDialog(project).show());
}
}
}, TIMEOUT));
}
private boolean isProfilingEnabled() {
return PropertiesComponent.getInstance(project).getBoolean(PROFILE_INDEXING_COMPONENT);
}
}
@@ -0,0 +1,501 @@
package com.jetbrains.performancePlugin;
import com.intellij.diagnostic.AbstractMessage;
import com.intellij.diagnostic.MessagePool;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.diagnostic.VMOptions;
import com.intellij.diagnostic.startUpPerformanceReporter.StartUpPerformanceReporter;
import com.intellij.ide.AppLifecycleListener;
import com.intellij.ide.ApplicationInitializedListener;
import com.intellij.ide.actions.RevealFileAction;
import com.intellij.ide.lightEdit.LightEditService;
import com.intellij.ide.lightEdit.LightEditorInfo;
import com.intellij.ide.lightEdit.LightEditorListener;
import com.intellij.idea.LoggerFactory;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionNotApplicableException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.impl.CoreProgressManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.InitProjectActivityJavaShim;
import com.intellij.openapi.ui.playback.PlaybackRunner;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.Alarm;
import com.intellij.util.AlarmFactory;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.jetbrains.performancePlugin.commands.OpenProjectCommand;
import com.jetbrains.performancePlugin.commands.TakeScreenshotCommand;
import com.jetbrains.performancePlugin.profilers.ProfilersController;
import io.opentelemetry.context.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
@SuppressWarnings({"UseOfSystemOutOrSystemErr", "SpellCheckingInspection"})
public final class ProjectLoaded extends InitProjectActivityJavaShim implements ApplicationInitializedListener {
private static final Logger LOG = Logger.getInstance("PerformancePlugin");
private static final int TIMEOUT = 500;
public static final String TEST_SCRIPT_FILE_PATH = System.getProperty("testscript.filename");
/**
* If an IDE error occurs and this flag is true, a failure of a TeamCity test will be printed
* to stdout using TeamCity Service Messages (see {@link #reportTeamCityFailedTestAndBuildProblem(String, String, String)}).
* The name of the failed test will be inferred from the name of the script file
* (its name without extension, see {@link #getTeamCityFailedTestName()}).
*/
private static final boolean MUST_REPORT_TEAMCITY_TEST_FAILURE_ON_IDE_ERROR = Boolean.parseBoolean(
System.getProperty("testscript.must.report.teamcity.test.failure.on.error", "true")
);
/**
* If an IDE error occurs during test script execution, the IDE will exit.
* This flag determines whether the status code of the exiting process will be 0 (if false) or 1 (if true).
*/
private static final boolean MUST_EXIT_PROCESS_WITH_NON_SUCCESS_CODE_ON_IDE_ERROR = Boolean.parseBoolean(
System.getProperty("testscript.must.exist.process.with.non.success.code.on.ide.error", "false")
);
private static final String INDEXING_PROFILER_PREFIX = "%%profileIndexing";
private static ScheduledExecutorService screenshotExecutor;
private final Alarm myAlarm = AlarmFactory.getInstance().create();
private static boolean ourScriptStarted;
@SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
@Override
public void runActivity(@NotNull Project project) {
if (TEST_SCRIPT_FILE_PATH != null && !ourScriptStarted) {
ourScriptStarted = true;
if (System.getProperty("ide.performance.screenshot") != null) {
registerScreenshotTaking(project, System.getProperty("ide.performance.screenshot"));
}
LOG.info("Start Execution");
PerformanceTestSpan.startSpan();
Pair<String, List<String>> profilerSettings = initializeProfilerSettingsForIndexing();
if (profilerSettings != null) {
try {
ProfilersController.getInstance().getCurrentProfilerHandler()
.startProfiling(profilerSettings.first, profilerSettings.second);
}
catch (Exception e) {
System.err.println("Start profile failed: " + e.getMessage());
ApplicationManagerEx.getApplicationEx().exit(true, true);
}
}
if (OpenProjectCommand.Companion.shouldOpenInSmartMode(project)) {
runScriptAfterDumb(project);
}
else if (SystemProperties.getBooleanProperty("performance.execute.script.after.scanning", false)) {
runScriptDuringIndexing(project);
}
else {
runScriptFromFile(project);
}
}
else {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
LOG.info(PerformanceTestingBundle.message("startup.silent"));
}
}
}
private static void registerScreenshotTaking(@NotNull Project project, String fileName) {
screenshotExecutor = ConcurrencyUtil.newSingleScheduledThreadExecutor("Performance plugin screenshoter");
screenshotExecutor.scheduleWithFixedDelay(()-> {
TakeScreenshotCommand.takeScreenshotOfFrame(project, fileName);
}, 0, 1, TimeUnit.MINUTES);
}
private static Pair<String, List<String>> initializeProfilerSettingsForIndexing() {
try {
List<String> lines = FileUtil.loadLines(getTestFile());
for (String line : lines) {
if (line.startsWith(INDEXING_PROFILER_PREFIX)) {
String[] command = line.substring(INDEXING_PROFILER_PREFIX.length()).trim().split("\\s+", 2);
String indexingActivity = command[0];
List<String> profilingParameters = command.length > 1 ? Arrays.asList(command[1].trim().split(","))
: new ArrayList<>();
return new Pair<>(indexingActivity, profilingParameters);
}
}
}
catch (IOException ignored) {
System.err.println(PerformanceTestingBundle.message("startup.script.read.error"));
ApplicationManagerEx.getApplicationEx().exit(true, true);
}
return null;
}
private static File getTestFile() {
File file = new File(TEST_SCRIPT_FILE_PATH);
if (!file.isFile()) {
System.err.println(PerformanceTestingBundle.message("startup.noscript", file.getAbsolutePath()));
ApplicationManagerEx.getApplicationEx().exit(true, true);
}
return file;
}
public static void reportErrorsFromMessagePool() {
MessagePool messagePool = MessagePool.getInstance();
List<AbstractMessage> ideErrors = messagePool.getFatalErrors(false, true);
for (AbstractMessage message : ideErrors) {
try {
reportScriptError(message);
}
catch (IOException e) {
LOG.error(e);
}
finally {
message.setRead(true);
}
}
}
static String generifyErrorMessage(String originalMessage) {
return originalMessage
// text@3ba5aac, text => text<ID>, text
.replaceAll("[$@#][A-Za-z0-9-_]+", "<ID>")
// java-design-patterns-master.db451f59 => java-design-patterns-master.<HASH>
.replaceAll("[.]([A-Za-z]+[0-9]|[0-9]+[A-Za-z])[A-Za-z0-9]*", ".<HASH>")
// 0x01 => <HEX>
.replaceAll("0x[0-9a-fA-F]+", "<HEX>")
// text1234text => text<NUM>text
.replaceAll("[0-9]+", "<NUM>");
}
private static void reportScriptError(@NotNull AbstractMessage errorMessage) throws IOException {
Throwable throwable = errorMessage.getThrowable();
Throwable cause = throwable;
String causeMessage = "";
while (cause.getCause() != null) {
cause = cause.getCause();
causeMessage = cause.getMessage();
}
if (causeMessage == null || causeMessage.isEmpty()) {
causeMessage = errorMessage.getMessage();
if (causeMessage == null || causeMessage.isEmpty()) {
String throwableMessage = getNonEmptyThrowableMessage(throwable);
int index = throwableMessage.indexOf("\tat ");
if (index != -1) {
causeMessage = throwableMessage.substring(0, index);
}
else {
causeMessage = throwableMessage;
}
}
}
Path scriptErrorsDir = Paths.get(PathManager.getLogPath(), "script-errors");
Files.createDirectories(scriptErrorsDir);
try (Stream<Path> stream = Files.walk(scriptErrorsDir)) {
String finalCauseMessage = causeMessage;
boolean isDuplicated = stream.filter(path -> path.getFileName().toString().equals("message.txt"))
.anyMatch(path -> {
try {
return Files.readString(path).equals(finalCauseMessage);
}
catch (IOException ex) {
LOG.error(ex.getMessage());
return false;
}
});
if (isDuplicated) {
return;
}
}
for (int i = 1; i < 1000; i++) {
Path errorDir = scriptErrorsDir.resolve("error-" + i);
if (Files.exists(errorDir)) {
continue;
}
Files.createDirectories(errorDir);
Files.writeString(errorDir.resolve("message.txt"), causeMessage);
Files.writeString(errorDir.resolve("stacktrace.txt"), errorMessage.getThrowableText());
List<Attachment> attachments = errorMessage.getAllAttachments();
for (int j = 0; j < attachments.size(); j++) {
Attachment attachment = attachments.get(j);
writeAttachmentToErrorDir(attachment, errorDir.resolve(j + "-" + attachment.getName()));
}
return;
}
LOG.error("Too many errors have been reported during script execution. See " + scriptErrorsDir);
}
private static void writeAttachmentToErrorDir(Attachment attachment, Path path) {
var executor = AppExecutorUtil.getAppScheduledExecutorService();
var counter = new AtomicInteger(0);
var isDone = new AtomicBoolean(false);
Path attachmentPath = Paths.get(attachment.getPath());
try {
Files.writeString(path, attachment.getDisplayText(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
Files.writeString(path, System.lineSeparator(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
}
catch (Exception e) {
LOG.warn("Failed to write attachment `display text`", e);
}
var task = new Runnable() {
@Override
public void run() {
if (isDone.get()) return;
try (FileChannel channel = FileChannel.open(attachmentPath, StandardOpenOption.READ)) {
channel.lock();
int bufferSize = 1024;
if (bufferSize > channel.size()) {
bufferSize = (int)channel.size();
}
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
try (var writer = Files.newByteChannel(path)) {
while (channel.read(buffer) > 0) {
writer.write(buffer);
buffer.clear();
}
}
isDone.set(true);
}
catch (Exception e) {
LOG.warn("Failed to store attachment", e);
}
finally {
if (counter.getAndIncrement() >= 5) {
LOG.warn(String.format("Unable to store attachment %s %s to path %s",
attachment.getName(), attachment.getPath(), path.toString()));
isDone.set(true);
}
}
}
};
executor.scheduleWithFixedDelay(task, 0, 200, TimeUnit.MILLISECONDS);
}
@NotNull
private static String getNonEmptyThrowableMessage(@NotNull Throwable throwable) {
if (throwable.getMessage() != null && !throwable.getMessage().isEmpty()) {
return throwable.getMessage();
}
return throwable.getClass().getName();
}
@NotNull
private static String getTeamCityFailedTestName() {
return FileUtilRt.getNameWithoutExtension(getTestFile().getName());
}
private static void reportTeamCityFailedTestAndBuildProblem(@NotNull String testName,
@NotNull String failureMessage,
@NotNull String failureDetails) {
testName = generifyErrorMessage(testName);
System.out.printf("##teamcity[testFailed name='%s' message='%s' details='%s']\n",
encodeStringForTC(testName),
encodeStringForTC(failureMessage),
encodeStringForTC(failureDetails));
System.out.printf("##teamcity[buildProblem description='%s' identity='%s'] ",
encodeStringForTC(failureMessage),
encodeStringForTC(testName));
}
private static @NotNull String encodeStringForTC(@NotNull String line) {
final int MAX_DESCRIPTION_LENGTH = 7_500;
return line.substring(0, Math.min(MAX_DESCRIPTION_LENGTH, line.length())).
replaceAll("\\|", "||").
replaceAll("\\[", "|[").
replaceAll("]", "|]").
replaceAll("\n", "|n").
replaceAll("'", "|'").
replaceAll("\r", "|r");
}
@Override
public void componentsInitialized() {
if (ApplicationManagerEx.getApplicationEx().isLightEditMode()) {
LightEditService.getInstance().getEditorManager().addListener(new LightEditorListener() {
@Override
public void afterSelect(@Nullable LightEditorInfo editorInfo) {
StartUpPerformanceReporter.Companion.logStats("LightEditor");
runActivity(Objects.requireNonNull(LightEditService.getInstance().getProject()));
}
});
}
}
private void runScriptAfterDumb(Project project) {
DumbService.getInstance(project).smartInvokeLater(Context.current().wrap(() -> {
myAlarm.addRequest(Context.current().wrap(() -> {
if (DumbService.isDumb(project) || CoreProgressManager.getCurrentIndicators().size() != 0) {
runScriptAfterDumb(project);
}
else {
runScriptFromFile(project);
}
}), TIMEOUT);
}));
}
private void runScriptDuringIndexing(Project project) {
ApplicationManager.getApplication().executeOnPooledThread(Context.current().wrap(() -> myAlarm.addRequest(Context.current().wrap(() -> {
List<ProgressIndicator> indicators = CoreProgressManager.getCurrentIndicators();
boolean indexingInProgress = false;
for (ProgressIndicator indicator : indicators) {
String indicatorText = indicator.getText();
if (indicatorText != null && indicatorText.contains("Indexing")) {
indexingInProgress = true;
break;
}
}
if (indexingInProgress) {
runScriptFromFile(project);
}
else {
runScriptDuringIndexing(project);
}
}), TIMEOUT)));
}
public static void runScript(Project project, String script) {
PlaybackRunner playback = new PlaybackRunnerExtended(script, new CommandLogger(), project);
ActionCallback scriptCallback = playback.run();
runScript(scriptCallback, project);
}
private static void runScriptFromFile(Project project) {
PlaybackRunner playback = new PlaybackRunnerExtended("%include " + getTestFile(), new CommandLogger(), project);
playback.setScriptDir(getTestFile().getParentFile());
ActionCallback scriptCallback = playback.run();
CommandsRunner.setStartActionCallback(scriptCallback);
runScript(scriptCallback, project);
}
private static void runScript(ActionCallback scriptCallback, Project project) {
scriptCallback
.doWhenDone(() -> {
LOG.info("Execution of the script has been finished successfully");
})
.doWhenRejected(errorMessage -> {
String message = "IDE will be terminated because some errors are detected while running the startup script: " + errorMessage;
if (MUST_REPORT_TEAMCITY_TEST_FAILURE_ON_IDE_ERROR) {
String testName = getTeamCityFailedTestName();
reportTeamCityFailedTestAndBuildProblem(testName, message, "");
}
if (SystemProperties.getBooleanProperty("startup.performance.framework", false)) {
storeFailureToFile(errorMessage);
}
LOG.error(message);
String threadDump = "Thread dump before IDE termination:\n" + ThreadDumper.dumpThreadsToString();
LOG.info(threadDump);
if (System.getProperty("ide.performance.screenshot.on.failure") != null) {
TakeScreenshotCommand.takeScreenshotOfFrame(project, System.getProperty("ide.performance.screenshot.before.kill"));
}
if (MUST_EXIT_PROCESS_WITH_NON_SUCCESS_CODE_ON_IDE_ERROR) {
System.exit(1);
}
else {
ApplicationManagerEx.getApplicationEx().exit(true, true);
}
});
}
private static void storeFailureToFile(String errorMessage) {
try {
Path logDir = Path.of(PathManager.getLogPath());
String ideaLogContent = Files.readString(logDir.resolve(LoggerFactory.LOG_FILE_NAME));
String substringBegin = ideaLogContent.substring(ideaLogContent.indexOf(errorMessage));
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
String date = timestamp.toString().substring(0, 10);
int endIndex = substringBegin.indexOf(date);
String errorMessageFromLog;
if (endIndex != -1) {
errorMessageFromLog = substringBegin.substring(0, endIndex);
}
else {
errorMessageFromLog = substringBegin;
}
Path failureCause = logDir.resolve("failure_cause.txt");
Files.writeString(failureCause, errorMessageFromLog);
}
catch (Exception ex) {
LOG.error(ex.getMessage());
}
}
static final class MyAppLifecycleListener implements AppLifecycleListener {
MyAppLifecycleListener() {
if (TEST_SCRIPT_FILE_PATH == null) {
throw ExtensionNotApplicableException.create();
}
}
@Override
public void appFrameCreated(@NotNull List<String> commandLineArgs) {
MessagePool messagePool = MessagePool.getInstance();
LOG.info("Error watcher has started");
messagePool.addListener(ProjectLoaded::reportErrorsFromMessagePool);
}
@Override
public void appClosing() {
var executor = screenshotExecutor;
if (executor != null) {
executor.shutdown();
}
PerformanceTestSpan.endSpan();
reportErrorsFromMessagePool();
}
}
}
@@ -0,0 +1,17 @@
package com.jetbrains.performancePlugin;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ActionCallback;
public interface RunCallbackHandler {
ExtensionPointName<RunCallbackHandler> EP_NAME = ExtensionPointName.create("com.jetbrains.performancePlugin.runCallbackHandler");
void patchCommandCallback(Project project, ActionCallback callback);
static void applyPatchesToCommandCallback(Project project, ActionCallback callback) {
EP_NAME.getExtensionList().forEach(extension ->
extension.patchCommandCallback(project, callback)
);
}
}
@@ -0,0 +1,86 @@
package com.jetbrains.performancePlugin;
import com.jetbrains.performancePlugin.utils.JvmRuntimeUtils;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
public class SpanBuilderWithSystemInfoAttributes implements SpanBuilder {
private final SpanBuilder spanBuilder;
public SpanBuilderWithSystemInfoAttributes(SpanBuilder spanBuilder) {
this.spanBuilder = spanBuilder;
}
@Override
public SpanBuilder setParent(@NotNull Context context) {
return spanBuilder.setParent(context);
}
@Override
public SpanBuilder setNoParent() {
return spanBuilder.setNoParent();
}
@Override
public SpanBuilder addLink(@NotNull SpanContext spanContext) {
return spanBuilder.addLink(spanContext);
}
@Override
public SpanBuilder addLink(@NotNull SpanContext spanContext, @NotNull Attributes attributes) {
return spanBuilder.addLink(spanContext, attributes);
}
@Override
public SpanBuilder setAttribute(@NotNull String key, @NotNull String value) {
return spanBuilder.setAttribute(key, value);
}
@Override
public SpanBuilder setAttribute(@NotNull String key, long value) {
return spanBuilder.setAttribute(key, value);
}
@Override
public SpanBuilder setAttribute(@NotNull String key, double value) {
return spanBuilder.setAttribute(key, value);
}
@Override
public SpanBuilder setAttribute(@NotNull String key, boolean value) {
return spanBuilder.setAttribute(key, value);
}
@Override
public <T> SpanBuilder setAttribute(@NotNull AttributeKey<T> key, @NotNull T value) {
return spanBuilder.setAttribute(key, value);
}
@Override
public SpanBuilder setSpanKind(@NotNull SpanKind spanKind) {
return spanBuilder.setSpanKind(spanKind);
}
@Override
public SpanBuilder setStartTimestamp(long startTimestamp, @NotNull TimeUnit unit) {
return spanBuilder.setStartTimestamp(startTimestamp, unit);
}
@Override
public Span startSpan() {
long jitTime = JvmRuntimeUtils.INSTANCE.getJitTime();
long gcTime = JvmRuntimeUtils.INSTANCE.getGCTime();
return new SpanWithOnEndListener(spanBuilder.startSpan(), span -> {
span.setAttribute("jitTime", JvmRuntimeUtils.INSTANCE.getJitTime() - jitTime);
span.setAttribute("gcTime", JvmRuntimeUtils.INSTANCE.getGCTime() - gcTime);
});
}
}
@@ -0,0 +1,72 @@
package com.jetbrains.performancePlugin;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.StatusCode;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
public class SpanWithOnEndListener implements Span {
private final Span span;
private final OnEndListener<? super Span> listener;
public SpanWithOnEndListener(Span span, OnEndListener<? super Span> listener) {
this.span = span;
this.listener = listener;
}
@Override
public <T> Span setAttribute(@NotNull AttributeKey<T> key, @NotNull T value) {
return span.setAttribute(key, value);
}
@Override
public Span addEvent(@NotNull String name, @NotNull Attributes attributes) {
return span.addEvent(name, attributes);
}
@Override
public Span addEvent(@NotNull String name, @NotNull Attributes attributes, long timestamp, @NotNull TimeUnit unit) {
return span.addEvent(name, attributes, timestamp, unit);
}
@Override
public Span setStatus(@NotNull StatusCode statusCode, @NotNull String description) {
return span.setStatus(statusCode, description);
}
@Override
public Span recordException(@NotNull Throwable exception, @NotNull Attributes additionalAttributes) {
return span.recordException(exception, additionalAttributes);
}
@Override
public Span updateName(@NotNull String name) {
return span.updateName(name);
}
@Override
public void end() {
listener.onEnd(this);
span.end();
}
@Override
public void end(long timestamp, @NotNull TimeUnit unit) {
listener.onEnd(this);
span.end(timestamp, unit);
}
@Override
public SpanContext getSpanContext() {
return span.getSpanContext();
}
@Override
public boolean isRecording() {
return span.isRecording();
}
}
@@ -0,0 +1,165 @@
package com.jetbrains.performancePlugin;
import com.google.common.util.concurrent.AtomicDouble;
import com.intellij.util.ConcurrencyUtil;
import com.sun.management.OperatingSystemMXBean;
import io.opentelemetry.api.trace.Span;
import oshi.SystemInfo;
import oshi.software.os.OSProcess;
import javax.swing.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
public class Timer {
public static final Timer instance = new Timer();
private String myActivityName = "default";
private volatile long myStartTime;
private volatile long myStopTime;
private LongAccumulator myLongestDelay;
private volatile double myHighestCPULoad;
private volatile long myHighestRAMUsage;
private final AtomicLong myTotalDelay = new AtomicLong();
private final AtomicDouble myTotalCPULoad = new AtomicDouble();
private final AtomicLong myTotalRAMUsage = new AtomicLong();
private final AtomicLong myCounter = new AtomicLong();
private final AtomicLong myHWCounter = new AtomicLong();
private Span mySpan;
private ScheduledExecutorService executor;
private long totalTime;
public void start() {
start("default", true);
}
public void start(String activityName, boolean withSpan) {
myActivityName = activityName;
myStartTime = System.currentTimeMillis();
myLongestDelay = new LongAccumulator(Math::max, 0);
myHighestCPULoad = 0;
myHighestRAMUsage = 0;
myTotalRAMUsage.set(0);
myTotalCPULoad.set(0);
myTotalDelay.set(0);
myCounter.set(0);
myHWCounter.set(0);
executor = ConcurrencyUtil.newSingleScheduledThreadExecutor("Performance plugin timer");
SystemInfo info = new SystemInfo();
int processId = info.getOperatingSystem().getProcessId();
OSProcess process = info.getOperatingSystem().getProcess(processId);
executor.scheduleWithFixedDelay(() -> {
long before = System.currentTimeMillis();
try {
SwingUtilities.invokeAndWait(() -> {
});
long after = System.currentTimeMillis();
long delay = after - before;
myTotalDelay.addAndGet(delay);
myCounter.incrementAndGet();
myLongestDelay.accumulate(delay);
}
catch (InterruptedException | InvocationTargetException ignored) {
}
}, 0, 50, TimeUnit.MILLISECONDS);
OperatingSystemMXBean bean = (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
executor.scheduleWithFixedDelay(() -> {
myHWCounter.incrementAndGet();
double cpuLoad = bean.getProcessCpuLoad();
if (cpuLoad > 0 && cpuLoad <= 1) {
myTotalCPULoad.addAndGet(cpuLoad);
if (myHighestCPULoad < cpuLoad) myHighestCPULoad = cpuLoad;
}
long ramUsage = process.getResidentSetSize();
myTotalRAMUsage.addAndGet(ramUsage);
if (myHighestRAMUsage < ramUsage) myHighestRAMUsage = ramUsage;
}, 0, 1, TimeUnit.SECONDS);
if(withSpan) {
String spanName = activityName.equals("default") ? "timer" : activityName;
// We need to set parent or null since it's part of general startup activity
//noinspection DataFlowIssue Parent can be null.
mySpan = PerformanceTestSpan.TRACER.spanBuilder(spanName).setParent(PerformanceTestSpan.getContext()).startSpan();
}
}
public void stop() {
if(mySpan != null) {
mySpan.setAttribute("max_awt_delay", getLongestDelay());
mySpan.setAttribute("average_awt_delay", getAverageDelay());
mySpan.end();
}
myStopTime = System.currentTimeMillis();
totalTime = myStopTime - myStartTime;
executor.shutdownNow();
}
public void reportToTeamCity() {
logValue(myActivityName, totalTime);
logValue(myActivityName + " | Total Time Execution", totalTime);
logValue(myActivityName + " | Responsiveness", getLongestDelay());
logValue(myActivityName + " | Average Responsiveness", getAverageDelay());
logValue(myActivityName + " | Average RAM Usage", getAverageRAMUsage());
logValue(myActivityName + " | Average CPU Usage", getAverageCPULoad());
logValue(myActivityName + " | Max RAM Usage", myHighestRAMUsage);
logValue(myActivityName + " | Max CPU Usage", myHighestCPULoad);
}
public long getLongestDelay() {
return myLongestDelay.get();
}
public long getTotalTime() {
return totalTime;
}
public long getStopTime() {
return myStopTime;
}
public double getAverageCPULoad() {
long counterValue = myHWCounter.get();
return counterValue != 0 ? myTotalCPULoad.get() / counterValue : 0.0;
}
public int getAverageRAMUsage() {
long counterValue = myHWCounter.get();
return counterValue != 0 ? (int)(myTotalRAMUsage.get() / counterValue) : 0;
}
public long getAverageDelay() {
long counterValue = myCounter.get();
return counterValue != 0 ? myTotalDelay.get() / counterValue : 0;
}
public String getActivityName() {
return myActivityName;
}
public boolean isStarted() {
return executor != null && !executor.isShutdown();
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private static void logValue(String key, long value) {
if (value >= 0L) {
System.out.printf("##teamcity[buildStatisticValue key='%s' value='%d']", key, value);
System.out.println();
}
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private static void logValue(String key, double value) {
if (value >= 0L) {
System.out.printf("##teamcity[buildStatisticValue key='%s' value='%f']", key, value);
System.out.println();
}
}
}
@@ -0,0 +1,33 @@
package com.jetbrains.performancePlugin.actions;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.jetbrains.performancePlugin.ui.ExecuteScriptDialog;
import org.jetbrains.annotations.NotNull;
public class ExecuteScriptAction extends AnAction implements DumbAware {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project == null) {
return;
}
ExecuteScriptDialog executeScriptDialog = new ExecuteScriptDialog(project);
executeScriptDialog.show();
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}
@Override
public void update(@NotNull AnActionEvent e) {
e.getPresentation().setEnabledAndVisible(e.getProject() != null);
}
}
@@ -0,0 +1,34 @@
package com.jetbrains.performancePlugin.actions;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.diagnostic.IndexDiagnosticDumper;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
final class OpenIndexingDiagnosticsAction extends AnAction implements DumbAware {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project == null) {
return;
}
Path reportHtml = IndexDiagnosticDumper.Companion.getProjectDiagnosticDirectory(project).resolve("report.html");
BrowserUtil.browse(reportHtml);
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}
@Override
public void update(@NotNull AnActionEvent e) {
e.getPresentation().setEnabledAndVisible(e.getProject() != null);
}
}
@@ -0,0 +1,54 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.util.ActionCallback
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.jetbrains.performancePlugin.utils.AbstractCallbackBasedCommand
import org.jetbrains.annotations.NonNls
import java.io.IOException
import java.nio.file.Path
import java.nio.file.Paths
abstract class AbstractFileCommand(text: String, line: Int) : AbstractCallbackBasedCommand(text, line, true) {
protected fun execute(path: Path, callback: ActionCallback, f: (VirtualFile) -> Any) {
val virtualFile = VirtualFileManager.getInstance().findFileByNioPath(path)
if (virtualFile == null) {
callback.reject("Directory or file by ${path} path from parameters was not found.")
return
}
WriteAction.runAndWait<IOException> {
f(virtualFile)
}
callback.setDone()
}
protected fun isCommandParametersRight(input: ArrayList<String>): Boolean {
return !(input.size < 2 || input.size > 2 || input[0] == "" || input[1] == "")
}
}
class AddFileCommand(text: String, line: Int) : AbstractFileCommand(text, line) {
@Throws(Exception::class)
override fun execute(callback: ActionCallback,
context: PlaybackContext) {
val input = extractCommandList(PREFIX, ",")
if (!isCommandParametersRight(input)) {
callback.reject("Command $PREFIX should have 2 non empty parameters (1 - path to directory, 2 - file name) with delimiter ','.")
return
}
if (VirtualFileManager.getInstance().findFileByNioPath(Paths.get(input[0], input[1])) != null) {
callback.reject("File by ${Paths.get(input[0], input[1])} path from parameters already exists.")
return
}
LOG.info("Add file ${input[1]} ${input[0]}")
super.execute(Paths.get(input[0]), callback) { virtualFile: VirtualFile -> virtualFile.createChildData(null, input[1]) }
}
companion object {
private val LOG = Logger.getInstance(AddFileCommand::class.java)
const val PREFIX: @NonNls String = CMD_PREFIX + "addFile"
}
}
@@ -0,0 +1,39 @@
package com.jetbrains.performancePlugin.commands;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.ui.playback.commands.AbstractCommand;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
import com.intellij.psi.PsiManager;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.stubs.StubIndexEx;
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
public class CleanCaches extends AbstractCommand {
public static final String PREFIX = CMD_PREFIX + "cleanCaches";
public CleanCaches(@NotNull String text, int line) {
super(text, line);
}
@Override
protected @NotNull Promise<Object> _execute(@NotNull PlaybackContext context) {
final ActionCallback actionCallback = new ActionCallbackProfilerStopper();
WriteAction.runAndWait(() -> {
Project project = context.getProject();
PsiManager.getInstance(project).dropResolveCaches();
PsiManager.getInstance(project).dropPsiCaches();
((StubIndexEx)StubIndex.getInstance()).cleanCaches();
((PersistentFSImpl)PersistentFS.getInstance()).incStructuralModificationCount();
actionCallback.setDone();
});
return Promises.toPromise(actionCallback);
}
}
@@ -0,0 +1,108 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.AbstractCommand
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiManager
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.toPromise
class CodeAnalysisCommand(text: String, line: Int) : AbstractCommand(text, line) {
companion object {
const val PREFIX = CMD_PREFIX + "codeAnalysis"
private val LOG = Logger.getInstance(CodeAnalysisCommand::class.java)
}
override fun _execute(context: PlaybackContext): Promise<Any?> {
val actionCallback = ActionCallbackProfilerStopper()
val project = context.project
val commandArgs = extractCommandArgument(PREFIX).split(" ", limit = 2)
val commandType = commandArgs[0]
DumbService.getInstance(project).waitForSmartMode()
val busConnection = project.messageBus.connect()
busConnection.subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC, object : DaemonCodeAnalyzer.DaemonListener {
override fun daemonFinished() {
val myDaemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(project) as DaemonCodeAnalyzerImpl
val editor = FileEditorManager.getInstance(project).selectedTextEditor
require(editor != null)
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document)
require(psiFile != null)
if (myDaemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)) {
busConnection.disconnect()
when (commandType) {
"CHECK_ON_RED_CODE" -> {
val errorsOnHighlighting =
DaemonCodeAnalyzerImpl.getHighlights(editor.document, HighlightSeverity.ERROR, project)
if (errorsOnHighlighting.size > 0) {
val errorMessages = StringBuilder("Analysis on red code detected some errors: " + errorsOnHighlighting.size)
errorsOnHighlighting.forEach {
errorMessages.append("\n").append(it.description)
}
actionCallback.reject(errorMessages.toString())
}
else {
context.message("Analysis on red code performed successfully", line)
actionCallback.setDone()
}
}
"WARNINGS_ANALYSIS" -> {
val warningsOnHighlighting = DaemonCodeAnalyzerImpl.getHighlights(editor.document, HighlightSeverity.WARNING, project)
if (warningsOnHighlighting.size > 0) {
val warnings = StringBuilder("Highlighting detected some warnings: " + warningsOnHighlighting.size)
for (warning in warningsOnHighlighting) {
warnings.append("\n").append(warning.description)
}
LOG.info(warnings.toString())
val args = when (commandArgs.size > 1) {
true -> commandArgs[1].split(",")
false -> listOf()
}
if (args.isNotEmpty()) {
args.forEach {
var found = false
for (warning in warningsOnHighlighting) {
if (warning.description.contains(it)) {
found = true
break
}
}
if (!found) {
actionCallback.reject("Highlighting did not detect the warning $it")
}
}
}
if (!actionCallback.isRejected) {
actionCallback.setDone()
}
}
else {
actionCallback.reject("Highlighting did not detect any warning")
}
}
else -> error("Wrong type of code analysis: $commandType")
}
}
}
})
DumbService.getInstance(project).smartInvokeLater {
PsiManager.getInstance(project).dropPsiCaches()
context.message("Code highlighting started", line)
DaemonCodeAnalyzer.getInstance(project).restart()
}
return actionCallback.toPromise()
}
}
@@ -0,0 +1,686 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.concurrency.JobLauncher
import com.intellij.diagnostic.AttachmentFactory
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.Attachment
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.AbstractCommand
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.SerializationManagerImpl
import com.intellij.psi.stubs.SerializedStubTree
import com.intellij.psi.stubs.StubForwardIndexExternalizer
import com.intellij.psi.stubs.StubUpdatingIndex
import com.intellij.util.Processor
import com.intellij.util.Processors
import com.intellij.util.SystemProperties
import com.intellij.util.indexing.*
import com.intellij.util.indexing.diagnostic.dump.IndexContentDiagnostic
import com.intellij.util.indexing.diagnostic.dump.IndexContentDiagnosticDumper
import com.intellij.util.indexing.diagnostic.dump.paths.IndexedFilePath
import com.intellij.util.indexing.diagnostic.dump.paths.IndexedFilePaths
import com.intellij.util.indexing.diagnostic.dump.paths.PortableFilePaths
import com.intellij.util.indexing.diagnostic.dump.paths.hasPresentablePathMatching
import com.intellij.util.indexing.impl.storage.DefaultIndexStorageLayout
import com.intellij.util.indexing.impl.storage.VfsAwareMapReduceIndex
import com.intellij.util.indexing.impl.storage.VfsAwareMapReduceIndex.IndexerIdHolder
import com.intellij.util.io.readText
import com.intellij.util.progress.ConcurrentTasksProgressManager
import com.jetbrains.performancePlugin.PerformanceTestingBundle
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper
import com.jetbrains.performancePlugin.utils.errors.ErrorCollector
import com.jetbrains.performancePlugin.utils.errors.ToDirectoryWritingErrorCollector
import com.jetbrains.performancePlugin.utils.indexes.CurrentIndexedFileResolver
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.toPromise
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
internal const val PREFIX = AbstractCommand.CMD_PREFIX + "compareIndices"
private val LOG = Logger.getInstance(CompareIndices::class.java)
/**
* Fully compares two indexes built for the same project: the *stored* index and the *current* index,
* which were built for the *old project* and the *new project*, respectively.
* The old project is the same as the current project except that it might have been located in another directory.
* All files' contents of the old project are equivalent to the current project.
*
* The stored index was dumped on disk with [StoreIndices] command.
* The stored index may contain data irrelevant to the old project (that extra data may have belonged to an irrelevant project).
* The [StoreIndices] command provides list of files that belonged to the old project (see [IndexContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds]).
*
* The current index is available in the currently running IDE via [FileBasedIndex] API.
*
* All data belonging to the old project's index must also be present in the current project's index:
* all triplets `(file, key, value)` of the stored index, which belonged to the old project,
* must be present in the current index and belong to the current project.
*
* The current index must not contain extra data for the current project files, as compared to the stored index.
* But the current index is allowed to contain extra data for irrelevant files.
*/
internal class CompareIndices(text: String, line: Int) : AbstractCommand(text, line) {
private companion object {
private const val LIMIT_OF_ERRORS_PER_COLLECTOR = 100
}
override fun _execute(context: PlaybackContext): Promise<Any?> {
val actionCallback = ActionCallbackProfilerStopper()
val storedIndexDir = getStoredIndicesDirectory()
val project = context.project
DumbService.getInstance(project).smartInvokeLater {
object : Task.Modal(project, PerformanceTestingBundle.message("comparing.indexes"), false) {
override fun run(indicator: ProgressIndicator) {
if (!StubForwardIndexExternalizer.USE_SHAREABLE_STUBS) {
actionCallback.reject(
"Index comparison available only with with -D${StubForwardIndexExternalizer.USE_SHAREABLE_STUBS_PROP}=true")
return
}
try {
compareIndexes(storedIndexDir, indicator, project)
actionCallback.setDone()
}
catch (e: Throwable) {
LOG.error(e)
actionCallback.reject(e.message)
}
}
}.queue()
}
return actionCallback.toPromise()
}
private fun getStoredIndicesDirectory(): Path {
val property = System.getProperty("compare.indices.command.stored.indexes.directory")
return checkNotNull(property) { "Path to stored indices is not specified" }.let { Paths.get(it) }
}
private fun getFailureDiagnosticDirectory(): Path {
val property = System.getProperty("compare.indices.command.failure.diagnostic.directory")
if (property != null) {
return Paths.get(property)
}
return FileUtil.createTempDirectory("compare-indices", "failure").toPath()
}
/**
* Patterns of files to be ignored from comparison.
* Can be used to ignore transient files that change every time the project is open.
*
* Examples:
* - `<project home>/some/tvolatile-file.txt`
* - `<project home>/build/\*`
* - `*.txt`
*/
private val ignoredFilesPatterns: List<String> by lazy {
val listPath = System.getProperty("compare.indices.list.of.files.to.ignore.from.comparison") ?: return@lazy emptyList<String>()
val patterns = Paths.get(listPath).readText().lines().map { it.trim() }.filterNot { it.isEmpty() }
LOG.info("The following files will be ignored from indexes comparison:\n" + patterns.joinToString(separator = "\n"))
patterns
}
private val ignoredPatternsForReporting: List<Pair<String, String>> by lazy {
val fileWithPatterns = System.getProperty("compare.indices.list.of.patterns.to.ignore.from.reporting") ?: return@lazy emptyList()
val patternsList = mutableListOf<Pair<String, String>>()
Paths.get(fileWithPatterns).readText().lines().map { it.trim() }.filterNot { it.isEmpty() }.forEach {
val attrs = it.split(" ")
patternsList.add(Pair(attrs[0], attrs[1]))
}
LOG.info("The following patterns will be ignored from failure reporting: ")
patternsList.forEach{ LOG.info("Files .${it.first} in index ${it.second}") }
patternsList
}
/**
* File types for which Stub tree is not built (but only the forward index).
* For all such file types [com.intellij.psi.stubs.SerializedStubTree] has [com.intellij.psi.stubs.SerializedStubTree.NO_STUB]
* as result of [com.intellij.psi.stubs.SerializedStubTree.getStub].
*/
private val fileTypesWithNoStubTree: Set<String> by lazy {
System.getProperty("compare.indices.file.types.with.no.stub.tree", "").split(",").filterNot { it.isEmpty() }.toSet()
}
private fun compareIndexes(storedIndexDir: Path, indicator: ProgressIndicator, project: Project) {
val failureDiagnosticDirectory = getFailureDiagnosticDirectory()
(FileBasedIndex.getInstance() as FileBasedIndexImpl).flushIndexes()
indicator.text = IndexingBundle.message("index.content.diagnostic.reading")
val storedIndexContentDiagnostic = IndexContentDiagnosticDumper.readFrom(StoreIndices.getFileForDiagnostic(storedIndexDir))
val storedIndexedFileResolver = StoredIndexedFileResolver(storedIndexContentDiagnostic)
val filesErrorCollector = ToDirectoryWritingErrorCollector(
"resolve-files",
failureDiagnosticDirectory.resolve("files-resolution-errors"),
LIMIT_OF_ERRORS_PER_COLLECTOR
)
val resolvedFiles = resolveFiles(storedIndexedFileResolver, filesErrorCollector, indicator, project)
if (filesErrorCollector.numberOfErrors > 0) {
throw IllegalArgumentException("Some files cannot be resolved. See <failure-diagnostics-dir>/files-resolution-errors")
}
LOG.info("Stored index directory contains: ${StoreIndices.dumpDirectory(storedIndexDir)}")
val idsToCompare = FileBasedIndexExtension.EXTENSION_POINT_NAME.extensionList.asSequence()
.filter {
it.dependsOnFileContent()
// https://youtrack.jetbrains.com/issue/IDEA-255298
&& it.name.name != "HashFragmentIndex"
}
.map { it.name }.toList()
LOG.info("Comparing indexes concurrently")
val errorCollectors = idsToCompare.associateWith {
ToDirectoryWritingErrorCollector(
it.name,
failureDiagnosticDirectory.resolve("errors-for-${it.name}"),
LIMIT_OF_ERRORS_PER_COLLECTOR
)
}
val finishedCounter = AtomicInteger()
val comparisonTaskProgressManager = ConcurrentTasksProgressManager(indicator, idsToCompare.size)
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(idsToCompare, indicator, Processor { id ->
val subIndicator = comparisonTaskProgressManager.createSubTaskIndicator(1)
val (extension, disposable) = runReadAction { findFileBasedIndexExtension(id, storedIndexDir) }
try {
val errorCollector = errorCollectors.getValue(id)
compareCurrentAndStoredIndexData(
resolvedFiles,
extension,
storedIndexDir,
subIndicator,
errorCollector,
project
)
}
catch (e: Exception) {
LOG.warn("Index comparison for ${id.name} has failed", e)
}
finally {
runCatching { Disposer.dispose(disposable) }.onFailure { LOG.warn(it) }
}
LOG.info("Index comparison has finished for ${finishedCounter.incrementAndGet()} / ${idsToCompare.size}")
true
})
val failedIndexes = errorCollectors.filterValues { it.numberOfErrors > 0 }.map { it.key }
if (failedIndexes.isNotEmpty()) {
throw RuntimeException(
"Comparison has failed for indexes [${failedIndexes.joinToString { it.name }}]. Details are saved to $failureDiagnosticDirectory")
}
LOG.info("Success. All indices are equal: ${idsToCompare.joinToString { it.name }}")
}
private fun findFileBasedIndexExtension(id: ID<*, *>, storedIndexDir: Path): Pair<FileBasedIndexExtension<*, *>, Disposable> {
return if (id.name == "Stubs") {
val storedSerializationManager = SerializationManagerImpl(storedIndexDir.resolve("rep.names"), true)
// Stored indexes must have been dumped with -D${com.intellij.psi.stubs.StubForwardIndexExternalizer.USE_SHAREABLE_STUBS_PROP}=true option specified
// to make stubs be serialized in shareable form (when [ID.getUniqueId()] is not serialized but the [ID.getName()] ID name is).
val nameStorageDump = storedSerializationManager.dumpNameStorage()
LOG.info("stored stub element name storage $nameStorageDump")
val stubForwardIndexExternalizer = StubForwardIndexExternalizer.createFileLocalExternalizer()
val storedStubUpdatingIndex = StubUpdatingIndex(stubForwardIndexExternalizer,
storedSerializationManager) as FileBasedIndexExtension<*, *>
storedStubUpdatingIndex to storedSerializationManager
}
else {
val usualExtension = FileBasedIndexExtension.EXTENSION_POINT_NAME.findFirstSafe { ex -> ex.name == id }!!
val emptyDisposable = Disposer.newDisposable()
usualExtension to emptyDisposable
}
}
private data class FileDescriptor(
val originalFilePath: IndexedFilePath,
val currentFile: VirtualFile
)
private fun resolveFiles(
storedIndexedFileResolver: StoredIndexedFileResolver,
errorCollector: ErrorCollector,
indicator: ProgressIndicator,
project: Project
): List<FileDescriptor> {
val allCurrentFiles = CurrentIndexedFileResolver.getAllToBeIndexedFilesInProject(project, indicator).values.flatMapTo(
hashSetOf()) { it }
indicator.text = PerformanceTestingBundle.message("compare.indexes.resolving.files")
indicator.isIndeterminate = false
val fileDescriptors = Collections.synchronizedList(arrayListOf<FileDescriptor>())
val finished = AtomicInteger()
JobLauncher.getInstance().invokeConcurrentlyUnderProgress(
storedIndexedFileResolver.originalIndexedFiles.toList(),
indicator,
Processor { originalFilePath ->
if (ignoredFilesPatterns.any { originalFilePath.portableFilePath.hasPresentablePathMatching(it) }) {
return@Processor true
}
val currentFile: VirtualFile = errorCollector.runCatchingError {
storedIndexedFileResolver.findFileInCurrentProject(originalFilePath, project)
} ?: return@Processor true
indicator.fraction = finished.incrementAndGet().toDouble() / storedIndexedFileResolver.originalIndexedFiles.size
indicator.text2 = currentFile.url
if (currentFile in allCurrentFiles) {
fileDescriptors += FileDescriptor(originalFilePath, currentFile)
}
true
}
)
return fileDescriptors
}
private fun <K, V> compareCurrentAndStoredIndexData(
resolvedFiles: List<FileDescriptor>,
extension: FileBasedIndexExtension<K, V>,
storedIndexDir: Path,
indicator: ProgressIndicator,
errorCollector: ErrorCollector,
project: Project
) {
val indexId = extension.name
indicator.text = PerformanceTestingBundle.message("compare.indexes.comparing.index", indexId.name)
indicator.text2 = PerformanceTestingBundle.message("compare.indexes.preparing.indexes")
indicator.isIndeterminate = true
val fileBasedIndex = FileBasedIndex.getInstance() as FileBasedIndexImpl
runReadAction { fileBasedIndex.ensureUpToDate(indexId, project, GlobalSearchScope.allScope(project)) }
val currentIndex = fileBasedIndex.getIndex(indexId)
val storedIndex = openStoredIndex(storedIndexDir, extension)
try {
indicator.isIndeterminate = false
indicator.fraction = 0.0
indicator.text2 = ""
check(extension.needsForwardIndexWhenSharing() || extension !is SingleEntryFileBasedIndexExtension) {
"Index ${indexId.name} is SingleEntryFileBasedIndexExtension and does not need forward index. " +
"This is a wrong index because SingleEntryFileBasedIndexExtension by its nature effectively consists of only the forward index."
}
if (extension.needsForwardIndexWhenSharing()) {
compareForwardIndexes(extension, resolvedFiles, currentIndex, storedIndex, errorCollector, indicator, project)
}
if (extension !is SingleEntryFileBasedIndexExtension) {
compareInvertedIndexes(extension, resolvedFiles, storedIndex, currentIndex, errorCollector, indicator, project)
}
}
finally {
storedIndex.dispose()
}
}
private fun <K, V> compareForwardIndexes(
extension: FileBasedIndexExtension<K, V>,
resolvedFiles: List<FileDescriptor>,
currentIndex: UpdatableIndex<K, V, FileContent, *>,
storedIndex: UpdatableIndex<K, V, FileContent, *>,
errorCollector: ErrorCollector,
indicator: ProgressIndicator,
project: Project
) {
val indexId = extension.name
indicator.text = PerformanceTestingBundle.message("compare.indexes.comparing.forward.index", indexId.name)
for ((finished, resolvedFile) in resolvedFiles.withIndex()) {
indicator.fraction = finished.toDouble() / resolvedFiles.size
indicator.text2 = resolvedFile.currentFile.url
compareFileData(resolvedFile, currentIndex, storedIndex, extension, errorCollector, project)
}
}
private fun <K, V> compareInvertedIndexes(
extension: FileBasedIndexExtension<K, V>,
resolvedFiles: List<FileDescriptor>,
storedIndex: UpdatableIndex<K, V, FileContent, *>,
currentIndex: UpdatableIndex<K, V, FileContent, *>,
errorCollector: ErrorCollector,
indicator: ProgressIndicator,
project: Project
) {
val indexId = extension.name
indicator.text = PerformanceTestingBundle.message("compare.indexes.comparing.inverted.index", indexId.name)
indicator.text2 = PerformanceTestingBundle.message("compare.indexes.comparing.inverted.index.collecting.keys")
indicator.isIndeterminate = true
val allStoredKeys = hashSetOf<K>()
errorCollector.runCatchingError {
runReadAction {
storedIndex.processAllKeys(Processors.cancelableCollectProcessor(allStoredKeys), GlobalSearchScope.allScope(project), null)
}
}
val allCurrentKeys = hashSetOf<K>()
errorCollector.runCatchingError {
runReadAction {
currentIndex.processAllKeys(Processors.cancelableCollectProcessor(allCurrentKeys), GlobalSearchScope.allScope(project), null)
}
}
checkNoKeysAreMissing(extension, storedIndex, resolvedFiles, allStoredKeys, allCurrentKeys, errorCollector, project)
val originalFileIdToFileDescriptor = resolvedFiles.associateBy { it.originalFilePath.originalFileSystemId }
indicator.isIndeterminate = false
indicator.fraction = 0.0
for ((index, storedKey: K) in allStoredKeys.withIndex()) {
indicator.fraction = index.toDouble() / allStoredKeys.size
val storedFileIdToValue = errorCollector.runCatchingError {
runReadAction { storedIndex.getData(storedKey!!).toMap() }
} ?: continue
val currentFileIdToValue = errorCollector.runCatchingError {
runReadAction { currentIndex.getData(storedKey!!).toMap() }
} ?: continue
for ((storedFileId, storedValue) in storedFileIdToValue) {
val fileDescriptor = originalFileIdToFileDescriptor[storedFileId] ?: continue
val currentFileId = FileBasedIndexImpl.getFileId(fileDescriptor.currentFile)
val currentValue = currentFileIdToValue[currentFileId]
if (IndexDataComparer.areValuesTheSame(extension, storedValue, currentValue)) {
continue
}
if (ignoredPatternsForReporting.isEmpty() || !isKnownError(extension, fileDescriptor.originalFilePath.portableFilePath.presentablePath)) {
errorCollector.runCatchingError {
val message = buildFileDataMismatchMessage(
"Values mismatch for key ${IndexDataPresenter.getPresentableIndexKey(storedKey)}",
extension,
fileDescriptor,
project
)
val attachments = arrayListOf<Attachment>()
attachments += Attachment("expected-value.txt", IndexDataPresenter.getPresentableIndexValue(storedValue))
attachments += Attachment("actual-value.txt", IndexDataPresenter.getPresentableIndexValue(currentValue))
attachments += createAttachmentsForActualFile(fileDescriptor)
throw RuntimeExceptionWithAttachments(message, *attachments.toTypedArray())
}
}
}
}
}
private fun <K, V> checkNoKeysAreMissing(
extension: FileBasedIndexExtension<K, V>,
storedIndex: UpdatableIndex<K, V, FileContent, *>,
resolvedFiles: List<FileDescriptor>,
allStoredKeys: Set<K>,
allCurrentKeys: Set<K>,
errorCollector: ErrorCollector,
project: Project
) {
val missingKeys = allStoredKeys - allCurrentKeys
if (missingKeys.isNotEmpty()) {
val originalFileIdToFileDescriptor = resolvedFiles.associateBy { it.originalFilePath.originalFileSystemId }
for (missingKey in missingKeys) {
val storedFileIdToValue = errorCollector.runCatchingError {
runReadAction { storedIndex.getData(missingKey!!).toMap().filterKeys { it in originalFileIdToFileDescriptor } }
} ?: continue
if (storedFileIdToValue.isEmpty()) {
continue
}
val fileDescriptor = originalFileIdToFileDescriptor[storedFileIdToValue.entries.iterator().next().key]
if (ignoredPatternsForReporting.isEmpty() || !isKnownError(extension, IndexDataPresenter.getPresentableIndexKey(missingKey))) {
if (!isKnownError(extension, fileDescriptor!!)) {
errorCollector.runCatchingError {
val message = buildString {
appendLine(
"Index ${extension.name.name}: key is unknown to actual index data: ${
IndexDataPresenter.getPresentableIndexKey(missingKey)
}"
)
appendLine("The key must be present in the index for the following files: ")
for ((storedFileId, _) in storedFileIdToValue) {
val fileDescriptor = originalFileIdToFileDescriptor[storedFileId] ?: continue
appendLine(
buildFileDataMismatchMessage(
"Key is not available in the actual index for the file",
extension,
fileDescriptor,
project
).withIndent(" ")
)
appendLine()
}
}
val attachments = arrayListOf<Attachment>()
attachments += Attachment("key.txt", IndexDataPresenter.getPresentableIndexKey(missingKey))
var nextMismatchedFileId = 1
for ((storedFileId, storedValue) in storedFileIdToValue) {
val fileDescriptor = originalFileIdToFileDescriptor[storedFileId] ?: continue
val pathPrefix = "file-and-value-${nextMismatchedFileId++}/"
attachments += Attachment("${pathPrefix}value.txt", IndexDataPresenter.getPresentableIndexValue(storedValue))
attachments += createAttachmentsForActualFile(fileDescriptor, pathPrefix = pathPrefix)
}
throw RuntimeExceptionWithAttachments(message, *attachments.toTypedArray())
}
}
}
}
}
}
private fun <V> ValueContainer<V>.toMap(): Map</* File ID */ Int, V?> {
val map = hashMapOf<Int, V>()
forEach { fileId, value ->
if (value != null) {
map[fileId] = value
}
true
}
return map
}
@Synchronized
private fun <K, V> openStoredIndex(storedIndexDir: Path,
extension: FileBasedIndexExtension<K, V>): VfsAwareMapReduceIndex<K, V, IndexerIdHolder> {
val propertyName = "index_root_path"
val oldValue = System.setProperty(propertyName, storedIndexDir.toAbsolutePath().toString())
try {
return VfsAwareMapReduceIndex<K, V, IndexerIdHolder>(extension, DefaultIndexStorageLayout.getLayout(extension, true), null)
}
finally {
SystemProperties.setProperty(propertyName, oldValue)
}
}
private fun <K, V> compareFileData(
fileDescriptor: FileDescriptor,
currentIndex: UpdatableIndex<K, V, FileContent, *>,
storedIndex: UpdatableIndex<K, V, FileContent, *>,
extension: FileBasedIndexExtension<K, V>,
errorCollector: ErrorCollector,
project: Project
) {
val storedData: Map<K, V> = errorCollector.runCatchingError {
runReadAction { storedIndex.getIndexedFileData(fileDescriptor.originalFilePath.originalFileSystemId) }
} ?: return
val currentData: Map<K, V> = errorCollector.runCatchingError {
runReadAction { currentIndex.getIndexedFileData(FileBasedIndex.getFileId(fileDescriptor.currentFile)) }
} ?: return
errorCollector.runCatchingError {
assertForwardIndexDataAreTheSameForFile(
fileDescriptor,
extension,
storedData,
currentData,
project
)
} ?: return
}
private fun <K, V> assertForwardIndexDataAreTheSameForFile(
fileDescriptor: FileDescriptor,
extension: FileBasedIndexExtension<K, V>,
expectedData0: Map<K, V>,
actualData0: Map<K, V>,
project: Project
) {
val expectedData: Map<K, V>
val actualData: Map<K, V>
@Suppress("UNCHECKED_CAST")
if (extension.name == StubUpdatingIndex.INDEX_ID && fileDescriptor.originalFilePath.fileType in fileTypesWithNoStubTree) {
expectedData = expectedData0.mapValues { (it.value as SerializedStubTree).withoutStub() } as Map<K, V>
actualData = actualData0.mapValues { (it.value as SerializedStubTree).withoutStub() } as Map<K, V>
}
else {
expectedData = expectedData0
actualData = actualData0
}
if (IndexDataComparer.areIndexedDataOfFileTheSame(extension, expectedData, actualData)) {
return
}
if (ignoredPatternsForReporting.isEmpty() || !isKnownError(extension, fileDescriptor.originalFilePath.portableFilePath.presentablePath)) {
val message = buildFileDataMismatchMessage(
"Indexed data maps do not match for ${fileDescriptor.originalFilePath.portableFilePath.presentablePath}",
extension,
fileDescriptor,
project
)
val expectedDataAttachment = if (expectedData.isEmpty()) {
Attachment("expected-data-is-empty.txt", "")
}
else {
Attachment("expected-data.txt", IndexDataPresenter.getPresentableKeyValueMap(expectedData))
}
val actualDataAttachment = if (actualData.isEmpty()) {
Attachment("actual-data-is-empty.txt", "")
}
else {
Attachment("actual-data.txt", IndexDataPresenter.getPresentableKeyValueMap(actualData))
}
val attachments = arrayListOf(expectedDataAttachment, actualDataAttachment)
attachments += createAttachmentsForActualFile(fileDescriptor)
attachments += createAttachmentsForExpectedFile(fileDescriptor)
throw RuntimeExceptionWithAttachments(message, *attachments.toTypedArray())
}
}
private fun isKnownError(extension: FileBasedIndexExtension<*, *>,
fileNameOrIndexData: String): Boolean {
val fileExtension = File(fileNameOrIndexData).extension
ignoredPatternsForReporting.forEach {
if (it.first == extension.name.name && it.second == fileExtension) {
LOG.info("Reporting index mismatch of $fileNameOrIndexData in ${extension.name.name} extension is ignored")
return true
}
}
return false
}
private fun isKnownError(extension: FileBasedIndexExtension<*, *>,
fileDescriptor: FileDescriptor): Boolean {
val fileExtension = fileDescriptor.currentFile.extension
ignoredPatternsForReporting.forEach {
if (it.first == extension.name.name && it.second == fileExtension) {
LOG.info("Reporting index mismatch of $fileExtension in ${extension.name.name} extension is ignored")
return true
}
}
return false
}
private fun createAttachmentsForActualFile(fileDescriptor: FileDescriptor, pathPrefix: String = ""): List<Attachment> =
findFileAndAllRelevantSiblings(fileDescriptor.currentFile).map { file ->
AttachmentFactory.createAttachment("${pathPrefix}actual-file/${file.name}", file)
}
private fun createAttachmentsForExpectedFile(fileDescriptor: FileDescriptor, pathPrefix: String = ""): List<Attachment> {
val expectedFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(fileDescriptor.originalFilePath.originalFileUrl)
return if (expectedFile != null) {
findFileAndAllRelevantSiblings(expectedFile).map { file ->
AttachmentFactory.createAttachment("${pathPrefix}expected-file/${file.name}", file)
}
}
else {
emptyList()
}
}
private fun buildFileDataMismatchMessage(
reason: String,
extension: FileBasedIndexExtension<*, *>,
fileDescriptor: FileDescriptor,
project: Project
) = buildString {
appendLine("Index mismatch ${extension.name.name} for ${fileDescriptor.originalFilePath.portableFilePath.presentablePath}: $reason")
appendLine("File of expected data:")
appendLine(fileDescriptor.originalFilePath.toString().withIndent(" "))
appendLine("File of actual data:")
val actualIndexedFilePath = IndexedFilePaths.createIndexedFilePath(fileDescriptor.currentFile, project)
appendLine(actualIndexedFilePath.toString().withIndent(" "))
if (doesFileHaveProvidedIndex(fileDescriptor.currentFile, extension, project)) {
appendLine(" Index ${extension.name.name} of this file is provided by index infrastructure extension")
}
}
private fun doesFileHaveProvidedIndex(file: VirtualFile, extension: FileBasedIndexExtension<*, *>, project: Project): Boolean {
val fileId = FileBasedIndex.getFileId(file)
return FileBasedIndexInfrastructureExtension.EP_NAME.extensionList.asSequence()
.mapNotNull { it.createFileIndexingStatusProcessor(project) }
.any { it.hasIndexForFile(file, fileId, extension) }
}
private fun findFileAndAllRelevantSiblings(file: VirtualFile): List<VirtualFile> {
if (file.extension != "class") {
return listOf(file)
}
val fullClassName = file.nameWithoutExtension
val baseClassName = fullClassName.substringBefore("$")
val parent = file.parent ?: return listOf(file)
parent.refresh(false, true)
return parent.children.orEmpty().filter {
it.extension == "class" && (it.nameWithoutExtension == fullClassName || it.nameWithoutExtension.startsWith("$baseClassName$"))
}
}
private fun String.withIndent(indent: String) = lineSequence().joinToString(separator = "\n") { "$indent$it" }
}
/**
* Provides access to indexed files that belonged to the old project.
*/
private class StoredIndexedFileResolver(storedIndexContentDiagnostic: IndexContentDiagnostic) {
private val originalFileIdToIndexedFilePath: Map<Int, IndexedFilePath> =
storedIndexContentDiagnostic.allIndexedFilePaths.associateBy { it.originalFileSystemId }
val originalIndexedFiles: Set<IndexedFilePath> =
storedIndexContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds.values.asSequence().flatten()
.mapNotNullTo(hashSetOf()) { originalFileIdToIndexedFilePath[it] }
fun findFileInCurrentProject(filePath: IndexedFilePath, currentProject: Project): VirtualFile {
val currentFile = PortableFilePaths.findFileByPath(filePath.portableFilePath, currentProject)
checkNotNull(currentFile) {
buildString {
appendLine("File corresponding to the old file system is not found in the current file system.")
appendLine("Portable file path = ${filePath.portableFilePath.presentablePath}")
appendLine("Original file URL = ${filePath.originalFileUrl}")
appendLine("Original file ID = ${filePath.originalFileSystemId}")
}
}
return currentFile
}
}
@@ -0,0 +1,225 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.progress.util.ProgressIndicatorBase
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.AbstractCommand
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.FileBasedIndexImpl
import com.intellij.util.indexing.diagnostic.dump.IndexContentDiagnosticDumper
import com.intellij.util.indexing.diagnostic.dump.paths.IndexedFilePath
import com.intellij.util.indexing.diagnostic.dump.paths.hasPresentablePathMatching
import com.intellij.util.io.readText
import com.jetbrains.performancePlugin.PerformanceTestingBundle
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper
import com.jetbrains.performancePlugin.utils.errors.ErrorCollector
import com.jetbrains.performancePlugin.utils.errors.ToDirectoryWritingErrorCollector
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.toPromise
import java.nio.file.Path
import java.nio.file.Paths
class CompareProjectFiles(text: String, line: Int) : AbstractCommand(text, line) {
companion object {
const val PREFIX = CMD_PREFIX + "compareProjectFiles"
private val LOG = Logger.getInstance(CompareProjectFiles::class.java)
@JvmStatic
fun main(args: Array<String>) {
val expectedPath = Paths.get(args[0])
val actualPath = Paths.get(args[1])
compareProjectFiles(expectedPath, actualPath, actualPath.parent, ProgressIndicatorBase())
}
private fun compareProjectFiles(
expectedDirectory: Path,
actualDirectory: Path,
failureDiagnosticDirectory: Path,
indicator: ProgressIndicator
) {
val expectedContentDiagnostic = IndexContentDiagnosticDumper.readFrom(StoreIndices.getFileForDiagnostic(expectedDirectory))
val actualContentDiagnostic = IndexContentDiagnosticDumper.readFrom(StoreIndices.getFileForDiagnostic(actualDirectory))
val (expectedIteratorNames, expectedUnstableNames) = expectedContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds.keys.partition { name ->
meaningfulDebugNames.any { name.startsWith(it) }
}
val (actualIteratorNames, actualUnstableNames) = actualContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds.keys.partition { name ->
meaningfulDebugNames.any { name.startsWith(it) }
}
val errorCollectors = arrayListOf<ErrorCollector>()
val collector = ToDirectoryWritingErrorCollector(
"set-of-iterators",
failureDiagnosticDirectory.resolve("errors-for-set-of-iterators"),
100
)
errorCollectors.add(collector)
collector.runCatchingError { compareSetsOfIndexableIterators(expectedIteratorNames, actualIteratorNames) }
val expectedFileIdToFile = expectedContentDiagnostic.allIndexedFilePaths.associateBy { it.originalFileSystemId }
val actualFileIdToFile = actualContentDiagnostic.allIndexedFilePaths.associateBy { it.originalFileSystemId }
for (iteratorName in expectedIteratorNames) {
val errorCollector = ToDirectoryWritingErrorCollector(
iteratorName,
failureDiagnosticDirectory.resolve("errors-for-${FileUtil.sanitizeFileName(iteratorName)}"),
100
)
errorCollectors += errorCollector
indicator.text = PerformanceTestingBundle.message("comparing.project.files.for.0", iteratorName)
val expectedIds = expectedContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds[iteratorName].orEmpty()
val actualIds = actualContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds[iteratorName].orEmpty()
compareSetsOfFiles(expectedIds, expectedFileIdToFile, actualIds, actualFileIdToFile, errorCollector, iteratorName)
}
// Compare files from iterators with unstable names.
val expectedUnstableIds = expectedUnstableNames.flatMap {
expectedContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds.getValue(it)
}
val actualUnstableIds = actualUnstableNames.flatMap {
actualContentDiagnostic.projectIndexedFileProviderDebugNameToFileIds.getValue(it)
}
val unstableIteratorsName = "iterators-with-unstable-names"
val errorCollector = ToDirectoryWritingErrorCollector(
unstableIteratorsName,
failureDiagnosticDirectory.resolve("errors-for-$unstableIteratorsName"),
100
)
errorCollectors += errorCollector
compareSetsOfFiles(expectedUnstableIds, expectedFileIdToFile, actualUnstableIds, actualFileIdToFile, errorCollector,
unstableIteratorsName)
if (errorCollectors.any { it.numberOfErrors > 0 }) {
throw RuntimeException("Some errors during files comparison have been collected. See details in $failureDiagnosticDirectory")
}
LOG.info("Success. Project files are equal")
}
// There may be iterators with non-presentable and unstable debug names, like "com.intellij.javaee.ExternalResourcesRootsProvider@eeabcf4"
// For such iterators we compare files as a whole set.
private val meaningfulDebugNames = listOf("Module", "Library", "SDK", "JDK", "Synthetic library", "Go SDK module")
private fun compareSetsOfFiles(
expectedIds: Iterable<Int>,
expectedFileIdToFile: Map<Int, IndexedFilePath>,
actualIds: Iterable<Int>,
actualFileIdToFile: Map<Int, IndexedFilePath>,
errorCollector: ToDirectoryWritingErrorCollector,
iteratorName: String
) {
val expectedFiles = expectedIds
.mapNotNull { expectedFileIdToFile[it] }
.associateBy { it.portableFilePath }
.filterKeys { file -> ignoredFilesPatterns.none { file.hasPresentablePathMatching(it) } }
val actualFiles = actualIds
.mapNotNull { actualFileIdToFile[it] }
.associateBy { it.portableFilePath }
.filterKeys { file -> ignoredFilesPatterns.none { file.hasPresentablePathMatching(it) } }
val missingFilePaths = expectedFiles.keys - actualFiles.keys
val redundantFilePaths = actualFiles.keys - expectedFiles.keys
if (missingFilePaths.isNotEmpty() || redundantFilePaths.isNotEmpty()) {
errorCollector.addError(RuntimeException(
buildString {
appendLine("The sets of indexed files for $iteratorName do not match")
appendLine(" Missing file paths:")
missingFilePaths.forEach { appendLine(" ${it.presentablePath}") }
appendLine(" Redundant file paths:")
redundantFilePaths.forEach { appendLine(" ${it.presentablePath}") }
}
))
}
if (errorCollector.numberOfErrors > 0) return
for (filePath in expectedFiles.keys) {
val expectedIndexedFile = expectedFiles.getValue(filePath)
val actualIndexedFile = actualFiles.getValue(filePath)
// Ignore "originalFileSystemId" field. It is expected that original VFS IDs mismatch between IDE restarts.
val expectedData = expectedIndexedFile.copy(originalFileSystemId = 0, originalFileUrl = "")
val actualData = actualIndexedFile.copy(originalFileSystemId = 0, originalFileUrl = "")
if (expectedData != actualData) {
errorCollector.addError(RuntimeException(
buildString {
appendLine("Indexed file ${filePath.presentablePath} data mismatch")
appendLine(" Expected:")
appendLine(expectedIndexedFile.toString().lineSequence().joinToString("\n") { " $it" })
appendLine(" Actual:")
appendLine(actualIndexedFile.toString().lineSequence().joinToString("\n") { " $it" })
})
)
}
if (errorCollector.numberOfErrors > 100) break
}
}
private fun compareSetsOfIndexableIterators(
expectedIteratorNames: List<String>,
actualIteratorNames: List<String>
) {
val missingIteratorNames = expectedIteratorNames - actualIteratorNames
val redundantIteratorNames = actualIteratorNames - expectedIteratorNames
if (missingIteratorNames.isNotEmpty() || redundantIteratorNames.isNotEmpty()) {
error(buildString {
appendLine("The sets of indexable file iterators do not match")
appendLine(" Missing iterators: [" + missingIteratorNames.joinToString() + "]")
appendLine(" Redundant iterators: [" + redundantIteratorNames.joinToString() + "]")
})
}
}
private val ignoredFilesPatterns: List<String> by lazy {
val listPath = System.getProperty("compare.project.files.list.of.files.to.ignore.from.comparison") ?: return@lazy emptyList<String>()
val patterns = Paths.get(listPath).readText().lines().map { it.trim() }.filterNot { it.isEmpty() }
LOG.info("The following files will be ignored from project files comparison:\n" + patterns.joinToString(separator = "\n"))
patterns
}
}
override fun _execute(context: PlaybackContext): Promise<Any?> {
val actionCallback = ActionCallbackProfilerStopper()
val input = text.substring(PREFIX.length).trim()
val index = input.split(" ")
val expectedDirectory = Paths.get(index[0])
val actualDirectory = Paths.get(index[1])
val failureDiagnosticDirectory = getFailureDiagnosticDirectory()
val project = context.project
DumbService.getInstance(project).smartInvokeLater {
object : Task.Modal(project, PerformanceTestingBundle.message("comparing.project.files"), false) {
override fun run(indicator: ProgressIndicator) {
try {
(FileBasedIndex.getInstance() as FileBasedIndexImpl).flushIndexes()
compareProjectFiles(expectedDirectory, actualDirectory, failureDiagnosticDirectory, indicator)
actionCallback.setDone()
}
catch (e: Throwable) {
LOG.error(e)
actionCallback.reject(e.message)
}
}
}.queue()
}
return actionCallback.toPromise()
}
private fun getFailureDiagnosticDirectory(): Path {
val property = System.getProperty("compare.project.files.command.failure.diagnostic.directory")
if (property != null) {
return Paths.get(property)
}
return FileUtil.createTempDirectory("compare-project-files", "failure").toPath()
}
}
@@ -0,0 +1,110 @@
package com.jetbrains.performancePlugin.commands;
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
import com.intellij.codeInsight.completion.CompletionPhase;
import com.intellij.codeInsight.completion.CompletionPhaseListener;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
public class CompletionCommand extends PerformanceCommand {
public static final String NAME = "doComplete";
public static final String PREFIX = CMD_PREFIX + NAME;
public static final String SPAN_NAME = "completion";
private static final Logger LOG = Logger.getInstance(CompletionCommand.class);
public CompletionCommand(@NotNull String text, int line) {
super(text, line);
}
private CompletionType getCompletionType() {
String completionTypeArg = getArgument(0);
switch (completionTypeArg) {
case "SMART" -> {
LOG.info(String.format("'%s' was passed as argument, so SMART completion will be used", completionTypeArg));
return CompletionType.SMART;
}
//case "BASIC",
default -> {
LOG.info(String.format("'%s' was passed as argument, so BASIC completion will be used", completionTypeArg));
return CompletionType.BASIC;
}
}
}
private String getArgument(int index){
String[] completionArgs = extractCommandArgument(PREFIX).trim().toUpperCase().split(" ");
if(completionArgs.length > index) {
return completionArgs[index];
}
else {
return "";
}
}
@Override
protected String getName() {
return NAME;
}
@Override
protected @NotNull Promise<Object> _execute(final @NotNull PlaybackContext context) {
final ActionCallback actionCallback = new ActionCallbackProfilerStopper();
Disposable listenerDisposable = Disposer.newDisposable();
Ref<Span> span = new Ref<>();
Ref<Scope> scope = new Ref<>();
ApplicationManager.getApplication().getMessageBus().connect(listenerDisposable)
.subscribe(CompletionPhaseListener.TOPIC, new CompletionPhaseListener() {
@Override
public void completionPhaseChanged(boolean isCompletionRunning) {
if (!isCompletionRunning && !CompletionServiceImpl.isPhase(CompletionPhase.CommittingDocuments.class)) {
if (CompletionServiceImpl.getCurrentCompletionProgressIndicator() == null) {
String description =
"CompletionServiceImpl.getCurrentCompletionProgressIndicator() is null on " + CompletionServiceImpl.getCompletionPhase();
span.get().setStatus(StatusCode.ERROR, description);
actionCallback.reject(description);
}
else {
int size = CompletionServiceImpl.getCurrentCompletionProgressIndicator().getLookup().getItems().size();
span.get().setAttribute("number", size);
span.get().end();
scope.get().close();
context.message("Number of elements: " + size, getLine());
actionCallback.setDone();
}
Disposer.dispose(listenerDisposable);
}
}
});
ApplicationManager.getApplication().invokeLater(Context.current().wrap(() -> {
Project project = context.getProject();
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
span.set(startSpan(SPAN_NAME));
scope.set(span.get().makeCurrent());
new CodeCompletionHandlerBase(getCompletionType(), true, false, true).invokeCompletion(project, editor);
}));
return Promises.toPromise(actionCallback);
}
}
@@ -0,0 +1,52 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.AbstractCommand
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.toPromise
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.random.Random
class CorruptIndexesCommand(text: String, line: Int) : AbstractCommand(text, line) {
companion object {
const val PREFIX = CMD_PREFIX + "corruptIndex"
private val LOG = Logger.getInstance(CorruptIndexesCommand::class.java)
}
override fun _execute(context: PlaybackContext): Promise<Any?> {
val actionCallback = ActionCallbackProfilerStopper()
val args = text.split(" ".toRegex()).toTypedArray()
val path = when (args.size) {
2 -> Paths.get(args[1])
3 -> Paths.get(args[1], args[2])
else -> error("There are more than 2 args")
}
if (path.toFile().isFile) {
LOG.info("Corrupting file: $path")
corruptFile(path.toFile())
}
else {
LOG.info("Corrupting files in dir: $path")
corruptFilesInDir(path)
}
actionCallback.setDone()
return actionCallback.toPromise()
}
private fun corruptFilesInDir(dir: Path) {
val listOfFiles = dir.toFile().walkTopDown().filter { it.isFile }
listOfFiles.forEach {
corruptFile(it)
}
}
private fun corruptFile(file: File) {
LOG.info("Corrupting file: ${file.absolutePath}")
file.writeBytes(Random(42).nextBytes(100 * 1024 * 1024))
}
}
@@ -0,0 +1,36 @@
package com.jetbrains.performancePlugin.commands;
import com.intellij.ide.plugins.CreateAllServicesAndExtensionsActionKt;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.util.ActionCallback;
import com.jetbrains.performancePlugin.utils.AbstractCallbackBasedCommand;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public final class CreateAllServicesAndExtensionsCommand extends AbstractCallbackBasedCommand {
public static final @NonNls String PREFIX = CMD_PREFIX + "CreateAllServicesAndExtensions";
public CreateAllServicesAndExtensionsCommand(@NotNull String text, int line) {
super(text, line, true);
}
@Override
protected void execute(@NotNull ActionCallback callback,
@NotNull PlaybackContext context) {
if (ApplicationManager.getApplication().isInternal()) {
try {
CreateAllServicesAndExtensionsActionKt.performAction();
callback.setDone();
}
catch (Exception e) {
callback.reject(e.getMessage());
}
}
else {
callback.reject("Internal mode is required for the '" + CreateAllServicesAndExtensionsActionKt.ACTION_ID + "' command." +
"Please see https://plugins.jetbrains.com/docs/intellij/enabling-internal.html");
}
}
}
@@ -0,0 +1,82 @@
package com.jetbrains.performancePlugin.commands;
import com.intellij.diagnostic.telemetry.TraceUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.TypingTarget;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.ui.playback.commands.KeyCodeTypeCommand;
import com.intellij.util.ConcurrencyUtil;
import com.jetbrains.performancePlugin.PerformanceTestSpan;
import io.opentelemetry.context.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.AsyncPromise;
import org.jetbrains.concurrency.Promise;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DelayTypeCommand extends KeyCodeTypeCommand {
public static final String PREFIX = CMD_PREFIX + "delayType";
public static final char END_CHAR = '#';
public static final String SPAN_NAME = "typing";
private final ScheduledExecutorService myExecutor = ConcurrencyUtil.newSingleScheduledThreadExecutor("Performance plugin delayed type");
public DelayTypeCommand(@NotNull String text, int line) {
super(text, line);
}
@Override
public @NotNull Promise<Object> _execute(final @NotNull PlaybackContext context) {
final AsyncPromise<Object> result = new AsyncPromise<>();
String input = extractCommandArgument(PREFIX);
String[] delayText = input.split("\\|");
final long delay = Integer.parseInt(delayText[0]);
final String text = delayText[1] + END_CHAR;
ApplicationManager.getApplication().executeOnPooledThread(Context.current().wrap(() -> {
TraceUtil.runWithSpanThrows(PerformanceTestSpan.TRACER, SPAN_NAME, span -> {
span.addEvent("Finding typing target");
try {
Waiter.checkCondition(() -> findTarget(context) != null).await(1, TimeUnit.MINUTES);
}
catch (InterruptedException e) {
span.recordException(e);
result.setError(e);
return;
}
CountDownLatch allScheduled = new CountDownLatch(1);
for (int i = 0; i < text.length(); i++) {
char currentChar = text.charAt(i);
myExecutor.schedule(() -> ApplicationManager.getApplication().invokeAndWait(Context.current().wrap(
() -> {
if (currentChar == END_CHAR) {
allScheduled.countDown();
myExecutor.shutdown();
}
else {
span.addEvent("Calling find target second time in DelayTypeCommand");
TypingTarget typingTarget = findTarget(context);
if (typingTarget != null) {
span.addEvent("Typing " + currentChar);
typingTarget.type(String.valueOf(currentChar));
}
}
})), i * delay, TimeUnit.MILLISECONDS);
}
try {
allScheduled.await();
myExecutor.awaitTermination(1, TimeUnit.MINUTES);
}
catch (InterruptedException e) {
result.setError(e);
}
});
result.setResult(null);
}));
return result;
}
}
@@ -0,0 +1,27 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.util.ActionCallback
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.annotations.NonNls
import java.nio.file.Paths
class DeleteFileCommand(text: String, line: Int) : AbstractFileCommand(text, line) {
@Throws(Exception::class)
override fun execute(callback: ActionCallback,
context: PlaybackContext) {
val input = extractCommandList(PREFIX, ",")
if (!isCommandParametersRight(input)) {
callback.reject("Command ${PREFIX} should have 2 non empty parameters (1 - path to directory, 2 - file name) with delimiter ','.")
return
}
LOG.info("Delete file " + input[1] + " " + input[0])
super.execute(Paths.get(input[0], input[1]), callback) { virtualFile: VirtualFile -> virtualFile.delete(null) }
}
companion object {
private val LOG = Logger.getInstance(DeleteFileCommand::class.java)
const val PREFIX: @NonNls String = CMD_PREFIX + "deleteFile"
}
}
@@ -0,0 +1,75 @@
package com.jetbrains.performancePlugin.commands;
import com.google.common.base.Stopwatch;
import com.intellij.codeInsight.daemon.impl.DefaultHighlightVisitorBasedInspection;
import com.intellij.diagnostic.telemetry.TraceUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.jetbrains.performancePlugin.PerformanceTestSpan;
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
import io.opentelemetry.context.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import static com.intellij.psi.PsiManager.getInstance;
public final class DoHighlighting extends PerformanceCommand {
public static final String NAME = "doHighlight";
public static final String PREFIX = CMD_PREFIX + NAME;
public static final String SPAN_NAME = "highlighting";
public DoHighlighting(@NotNull String text, int line) {
super(text, line);
}
@Override
protected String getName() {
return NAME;
}
@NotNull
@Override
protected Promise<Object> _execute(@NotNull PlaybackContext context) {
ActionCallback actionCallback = new ActionCallbackProfilerStopper();
ApplicationManager.getApplication().invokeAndWait(Context.current().wrap(() -> {
boolean highlightErrorElements = true;
boolean runAnnotators = true;
Project project = context.getProject();
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
if (editor == null) {
actionCallback.reject("Editor is not open");
return;
}
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (psiFile == null) {
actionCallback.reject("Missing PSI file in the editor");
return;
}
getInstance(project).dropPsiCaches();
ReadAction.nonBlocking(Context.current().wrap((Callable<Void>)() -> {
Stopwatch timer = Stopwatch.createStarted();
TraceUtil.runWithSpanThrows(PerformanceTestSpan.TRACER, SPAN_NAME, span -> {
DefaultHighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(psiFile, highlightErrorElements, runAnnotators);
span.setAttribute("lines", editor.getDocument().getLineCount());
span.setAttribute("timeToLines", timer.stop().elapsed(TimeUnit.MILLISECONDS) / (Math.max(1, editor.getDocument().getLineCount())));
});
actionCallback.setDone();
return null;
})).submit(AppExecutorUtil.getAppExecutorService());
}));
return Promises.toPromise(actionCallback);
}
}
@@ -0,0 +1,129 @@
package com.jetbrains.performancePlugin.commands;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TypingTarget;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.ui.playback.commands.AbstractCommand;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.messages.MessageBusConnection;
import com.jetbrains.performancePlugin.PerformanceTestSpan;
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
import java.util.List;
import static com.intellij.openapi.ui.playback.commands.AlphaNumericTypeCommand.findTarget;
import static com.intellij.psi.PsiManager.getInstance;
@SuppressWarnings("TestOnlyProblems")
public final class DoLocalInspection extends AbstractCommand implements Disposable {
public static final String PREFIX = CMD_PREFIX + "doLocalInspection";
public static final String SPAN_NAME = "localInspections";
public DoLocalInspection(@NotNull String text, int line) {
super(text, line);
}
@NotNull
@Override
protected Promise<Object> _execute(@NotNull PlaybackContext context) {
ActionCallback actionCallback = new ActionCallbackProfilerStopper();
Project project = context.getProject();
DumbService.getInstance(project).waitForSmartMode();
ApplicationManager.getApplication().invokeAndWait(() -> {
TypingTarget target = findTarget(context);
if (target == null) {
//actionCallback.reject("There is no focus in editor");
}
});
if (actionCallback.isRejected()) {
return Promises.toPromise(actionCallback);
}
MessageBusConnection busConnection = project.getMessageBus().connect();
SpanBuilder span = PerformanceTestSpan.TRACER.spanBuilder(SPAN_NAME).setParent(PerformanceTestSpan.getContext());
Ref<Span> spanRef = new Ref<>();
Ref<Scope> scopeRef = new Ref<>();
busConnection.subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC, new DaemonCodeAnalyzer.DaemonListener() {
@Override
public void daemonFinished() {
DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
assert editor != null;
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
assert psiFile != null;
if (myDaemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)) {
List<HighlightInfo> errorsOnHighlighting =
DaemonCodeAnalyzerImpl.getHighlights(editor.getDocument(), HighlightSeverity.ERROR, project);
List<HighlightInfo> warningsOnHighlighting =
DaemonCodeAnalyzerImpl.getHighlights(editor.getDocument(), HighlightSeverity.WARNING, project);
List<HighlightInfo> weakWarningsOnHighlighting =
DaemonCodeAnalyzerImpl.getHighlights(editor.getDocument(), HighlightSeverity.WEAK_WARNING, project);
StringBuilder finishMessage = new StringBuilder("Local inspections have been finished with: ");
span.setAttribute("Errors", errorsOnHighlighting.size());
if (errorsOnHighlighting.size() > 0) {
finishMessage.append("\n").append(new String("Errors: " + errorsOnHighlighting.size()));
}
for (HighlightInfo error : errorsOnHighlighting) {
finishMessage.append("\n").append(new String(error.getText() + ": " + error.getDescription()));
}
span.setAttribute("Warnings", warningsOnHighlighting.size());
if (warningsOnHighlighting.size() > 0) {
finishMessage.append("\n").append(new String("Warnings: " + warningsOnHighlighting.size()));
}
for (HighlightInfo warning : warningsOnHighlighting) {
finishMessage.append("\n").append(new String(warning.getText() + ": " + warning.getDescription()));
}
span.setAttribute("Weak Warnings", warningsOnHighlighting.size());
if (weakWarningsOnHighlighting.size() > 0) {
finishMessage.append("\n").append(new String("Weak Warnings: " + weakWarningsOnHighlighting.size()));
}
for (HighlightInfo weakWarning : weakWarningsOnHighlighting) {
finishMessage.append("\n").append(new String(weakWarning.getText() + ": " + weakWarning.getDescription()));
}
spanRef.get().end();
scopeRef.get().close();
busConnection.disconnect();
context.message(finishMessage.toString(), getLine());
actionCallback.setDone();
}
}
});
DumbService.getInstance(project).smartInvokeLater(() -> {
getInstance(project).dropPsiCaches();
context.message("Local inspections have been started", getLine());
spanRef.set(span.startSpan());
scopeRef.set(spanRef.get().makeCurrent());
DaemonCodeAnalyzer.getInstance(project).restart();
});
return Promises.toPromise(actionCallback);
}
@Override
public void dispose() {
}
}
@@ -0,0 +1,78 @@
package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.AbstractCommand
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.FileBasedIndexImpl
import com.intellij.util.indexing.diagnostic.dump.IndexContentDiagnosticDumper
import com.jetbrains.performancePlugin.PerformanceTestingBundle
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.toPromise
import java.io.IOException
import java.nio.file.Path
import java.nio.file.Paths
class DumpProjectFiles(text: String, line: Int) : AbstractCommand(text, line) {
override fun _execute(context: PlaybackContext): Promise<Any?> {
val actionCallback = ActionCallbackProfilerStopper()
val dumpProjectFilesDir = getOrCreateDirectoryToDumpFiles()
LOG.info("DumpFiles will be stored to $dumpProjectFilesDir")
val project = context.project
DumbService.getInstance(project).smartInvokeLater {
object : Task.Modal(project, PerformanceTestingBundle.message("dumping.project.files"), false) {
override fun run(indicator: ProgressIndicator) {
try {
dumpProjectFiles(dumpProjectFilesDir, indicator, project)
LOG.info("Project files has been successfully stored to $dumpProjectFilesDir")
actionCallback.setDone()
}
catch (e: Throwable) {
actionCallback.reject(e.message)
}
}
}.queue()
}
return actionCallback.toPromise()
}
private fun dumpProjectFiles(dumpDir: Path, indicator: ProgressIndicator, project: Project) {
runReadAction { (FileBasedIndex.getInstance() as FileBasedIndexImpl).flushIndexes() }
indicator.isIndeterminate = true
val contentDiagnostic = IndexContentDiagnosticDumper.getIndexContentDiagnosticForProject(project, indicator)
IndexContentDiagnosticDumper.writeTo(StoreIndices.getFileForDiagnostic(dumpDir), contentDiagnostic)
}
companion object {
const val PREFIX = CMD_PREFIX + "dumpProjectFiles"
private val LOG = Logger.getInstance(DumpProjectFiles::class.java)
private fun getOrCreateDirectoryToDumpFiles(): Path {
val property = System.getProperty("dump.project.files.directory")
return if (property != null) {
Paths.get(property)
}
else {
try {
FileUtil.createTempDirectory("projectFiles_dumps", null).toPath()
}
catch (e: IOException) {
throw RuntimeException(e)
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More