new plugin descriptor format - full support on all levels

GitOrigin-RevId: 718c9401f22900c30029ec62c23f60f6f22278ee
This commit is contained in:
Vladimir Krivosheev
2021-05-11 16:23:49 +02:00
committed by intellij-monorepo-bot
parent ee13959873
commit c2b2520994
112 changed files with 7120 additions and 2717 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.intellij.build
import groovy.transform.CompileStatic
@@ -111,10 +111,10 @@ abstract class BaseIdeaProperties extends JetBrainsProductProperties {
"plugins/gradle/lib/gradle-tooling-extension-api.jar" : "1.6",
"plugins/gradle/lib/gradle-tooling-extension-impl.jar" : "1.6",
"plugins/maven/lib/maven-server-api.jar" : "1.6",
"plugins/maven/lib/maven2-server-impl.jar" : "1.6",
"plugins/maven/lib/maven2-server.jar" : "1.6",
"plugins/maven/lib/maven3-server-common.jar" : "1.6",
"plugins/maven/lib/maven30-server-impl.jar" : "1.6",
"plugins/maven/lib/maven3-server-impl.jar" : "1.6",
"plugins/maven/lib/maven30-server.jar" : "1.6",
"plugins/maven/lib/maven3-server.jar" : "1.6",
"plugins/maven/lib/artifact-resolver-m2.jar" : "1.6",
"plugins/maven/lib/artifact-resolver-m3.jar" : "1.6",
"plugins/maven/lib/artifact-resolver-m31.jar" : "1.6",

View File

@@ -1,6 +1,6 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Repository("https://repo1.maven.org/maven2/")
@file:DependsOn("net.sourceforge.plantuml:plantuml:1.2020.17")
@file:DependsOn("net.sourceforge.plantuml:plantuml:1.2021.6")
import net.sourceforge.plantuml.FileFormat
import net.sourceforge.plantuml.FileFormatOption
@@ -9,44 +9,45 @@ import net.sourceforge.plantuml.error.PSystemError
import org.w3c.dom.*
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathExpression
import javax.xml.xpath.XPathFactory
val defaultFontSize = "14"
val noteBackgroundColor = "#ECECEC"
val noteBackgroundColor = "#EBEBEB"
var start = System.currentTimeMillis()
val outDir = Paths.get("out").toAbsolutePath()
val outDir: Path = Path.of("out").toAbsolutePath()
Files.createDirectories(outDir)
val svgFileFormat = FileFormatOption(FileFormat.SVG, /* withMetadata = */ false)
Files.newDirectoryStream(Paths.get(".").toAbsolutePath(), "*.puml").use { inFiles ->
Files.newDirectoryStream(Path.of(".").toAbsolutePath(), "*.puml").use { inFiles ->
for (inFile in inFiles) {
if (inFile.fileName.toString().contains("-theme.")) {
continue
}
val sourceFileReader = SourceFileReader(inFile.toFile(), outDir.toFile(), svgFileFormat)
val result = sourceFileReader.getGeneratedImages()
val result = sourceFileReader.generatedImages
if (result.size == 0) {
System.err.println("warning: no image in $inFile")
continue
}
for (s in sourceFileReader.getBlocks()) {
val diagram = s.getDiagram()
for (s in sourceFileReader.blocks) {
val diagram = s.diagram
if (diagram is PSystemError) {
System.err.println("status=ERROR")
System.err.println("lineNumber=" + diagram.getLineLocation().getPosition())
for (error in diagram.getErrorsUml()) {
System.err.println("label=" + error.getError())
System.err.println("lineNumber=" + diagram.lineLocation.position)
for (error in diagram.errorsUml) {
System.err.println("label=" + error.error)
}
}
}
@@ -56,16 +57,12 @@ Files.newDirectoryStream(Paths.get(".").toAbsolutePath(), "*.puml").use { inFile
println("Generate SVG in: ${System.currentTimeMillis() - start} ms")
start = System.currentTimeMillis()
// re-format SVG
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
val dbFactory = DocumentBuilderFactory.newDefaultInstance()
val xPathFactory = XPathFactory.newInstance()
val textFillXPath = xPathFactory.newXPath().compile("/svg/g/text")
val rectFillXPath = xPathFactory.newXPath().compile("/svg/g/rect")
val pathFillXPath = xPathFactory.newXPath().compile("/svg/g/path")
val dbFactory: DocumentBuilderFactory = DocumentBuilderFactory.newDefaultInstance()
val xPathFactory: XPathFactory = XPathFactory.newInstance()
val textFillXPath: XPathExpression = xPathFactory.newXPath().compile("/svg/g/text")
val rectFillXPath: XPathExpression = xPathFactory.newXPath().compile("/svg/g/rect")
val lineStyleXPath: XPathExpression = xPathFactory.newXPath().compile("/svg/g/line")
val pathFillXPath: XPathExpression = xPathFactory.newXPath().compile("/svg/g/path")
Files.newDirectoryStream(outDir, "*.svg").use { svgFiles ->
for (svgFile in svgFiles) {
@@ -75,11 +72,13 @@ Files.newDirectoryStream(outDir, "*.svg").use { svgFiles ->
println("Transform SVG in: ${System.currentTimeMillis() - start} ms")
fun transformSvg(svgFile: Path?) {
fun transformSvg(svgFile: Path) {
val dBuilder = dbFactory.newDocumentBuilder()
val document = Files.newInputStream(svgFile).buffered().use { dBuilder.parse(it) }
//document.documentElement.removeAttribute("xmlns:xlink")
val content = Files.readString(svgFile)
val document = dBuilder.parse(content.byteInputStream())
if (!content.contains("xlink:")) {
document.documentElement.removeAttribute("xmlns:xlink")
}
val classNameToBuilder = linkedMapOf<String, String>()
@@ -88,8 +87,9 @@ fun transformSvg(svgFile: Path?) {
val element = textNodes.item(i) as Element
val fill = element.getAttributeNode("fill") ?: continue
// not required - better to not modify glyph to ensure that font looks as expected
//element.removeAttribute("lengthAdjust")
if (element.getAttribute("lengthAdjust") == "spacing") {
element.removeAttribute("lengthAdjust")
}
if (fill.value == "#000000") {
element.removeAttributeNode(fill)
@@ -104,9 +104,27 @@ fun transformSvg(svgFile: Path?) {
val element = rectNodes.item(i) as Element
val style = element.getAttributeNode("style") ?: continue
val fill = element.getAttributeNode("fill") ?: continue
if (style.value == "stroke: #383838; stroke-width: 1.0;") {
if (style.value == "stroke:#383838;stroke-width:1.0;") {
applyBackgroundAndBorder("process", element, style, fill, classNameToBuilder)
}
else if (style.value == "stroke:#FEFECE;stroke-width:1.5;") {
applyBackgroundAndBorder("node", element, style, fill, classNameToBuilder)
}
}
val lineNodes = lineStyleXPath.evaluate(document, XPathConstants.NODESET) as NodeList
for (i in 0 until lineNodes.length) {
val element = lineNodes.item(i) as Element
val style = element.getAttributeNode("style") ?: continue
if (style.value == "stroke:#A80036;stroke-width:1.0;") {
classNameToBuilder.computeIfAbsent("arrow") {
"""
${style.value}
""".trimIndent()
}
element.removeAttributeNode(style)
element.setAttribute("class", "arrow")
}
}
val pathNodes = pathFillXPath.evaluate(document, XPathConstants.NODESET) as NodeList
@@ -121,8 +139,16 @@ fun transformSvg(svgFile: Path?) {
appendStyleElement(document, classNameToBuilder)
// re-format SVG
val transformer: Transformer = TransformerFactory.newDefaultInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
@Suppress("HttpUrlsUsage")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
// so, first node of insertAdjacentHTML result will be svg and not comment
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
Files.newOutputStream(svgFile).buffered().use { fileOut ->
transformer.transform(DOMSource(document), StreamResult(fileOut))
transformer.transform(DOMSource(document.documentElement), StreamResult(fileOut))
}
}
@@ -139,6 +165,7 @@ fun hasOnlyAttributes(list: NamedNodeMap, prefix: String, names: List<String>):
return nameSet.isEmpty()
}
@Suppress("JavaMapForEach")
fun appendStyleElement(document: Document, classNameToBuilder: Map<String, String>) {
val styleElement = document.createElement("style")
val builder = StringBuilder()
@@ -146,7 +173,7 @@ fun appendStyleElement(document: Document, classNameToBuilder: Map<String, Strin
builder.append(""" @import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&display=swap');""")
classNameToBuilder.forEach { name, content ->
builder.append("\n .").append(name).append(" {")
content.lineSequence().iterator().forEach { builder.append("\n ").append(it) }
content.lineSequence().iterator().forEach { builder.append("\n ").append(it.trim()) }
builder.append("\n }")
}
builder.append('\n')
@@ -177,7 +204,7 @@ fun extractFontStyle(element: Element, classNameToBuilder: MutableMap<String, St
throw UnsupportedOperationException("font combination is unknown (fontFamily=$fontFamily, lastUsedMonoFont=$lastUsedMonoFont)")
}
classNameToBuilder.getOrPut(className) {
classNameToBuilder.computeIfAbsent(className) {
"""
font-family: $fontFamily;
font-size: ${size.value}px;
@@ -191,11 +218,11 @@ fun extractFontStyle(element: Element, classNameToBuilder: MutableMap<String, St
}
fun applyBackgroundAndBorder(className: String, element: Element, style: Attr, fill: Attr, classNameToBuilder: MutableMap<String, String>) {
classNameToBuilder.getOrPut(className) {
classNameToBuilder.computeIfAbsent(className) {
"""
${style.value}
fill: ${fill.value};
""".trimIndent()
${style.value.removeSuffix(";").replace(";", ";\n")};
fill: ${fill.value};
""".trimIndent()
}
element.removeAttributeNode(style)
element.removeAttributeNode(fill)

15
docs/build.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
# Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
set -ex
# brew install graphviz
cd "$(dirname "$0")"
KOTLIN="/Applications/Idea.app/Contents/plugins/KotlinPlugin"
java -Dfile.encoding=UTF-8 -classpath $KOTLIN/kotlinc/lib/kotlin-compiler.jar:$KOTLIN/lib/kotlin-stdlib.jar:$KOTLIN/lib/kotlin-reflect.jar:$KOTLIN/kotlinc/lib/kotlin-main-kts.jar:$KOTLIN/kotlinc/lib/kotlin-stdlib.jar:$KOTLIN/kotlinc/lib/kotlin-reflect.jar:/Volumes/data/.ivy2/cache/net.sourceforge.plantuml/plantuml/jars/plantuml-1.2021.6.jar:$KOTLIN/kotlinc/lib/kotlin-script-runtime.jar org.jetbrains.kotlin.cli.jvm.K2JVMCompiler \
-kotlin-home $KOTLIN/kotlinc -jvm-target 1.8 -\
script ./build.main.kts

View File

@@ -2,6 +2,9 @@
skinparam monochrome true
skinparam shadowing true
' https://plantuml.com/component-diagram "Use rectangle notation (remove UML notation)"
skinparam componentStyle rectangle
skinparam DefaultFontName Roboto
skinparam DefaultMonospacedFontName "Roboto Mono"

View File

@@ -0,0 +1,166 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="956px" preserveAspectRatio="none" style="width:1184px;height:956px;background:#FFFFFF;" version="1.1" viewBox="0 0 1184 956" width="1184px" zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.process {
stroke:#383838;
stroke-width:1.0;
fill: #F8F8F8;
}
.note {
stroke:#383838;
stroke-width:1.0;
fill: #EBEBEB;
}
</style>
<defs>
<filter height="300%" id="f162mg05eaivl4" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<path class="note" d="M697.25,10 L697.25,43.8125 L677.25,47.8125 L697.25,51.8125 L697.25,85.625 A0,0 0 0 0 697.25,85.625 L994.25,85.625 A0,0 0 0 0 994.25,85.625 L994.25,20 L984.25,10 L697.25,10 A0,0 0 0 0 697.25,10 " filter="url(#f162mg05eaivl4)"/>
<path class="note" d="M984.25,10 L984.25,20 L994.25,20 L984.25,10 "/>
<text class="text" textLength="86" x="703.25" y="27.9883">In any thread.</text>
<text class="text" textLength="128" x="703.25" y="44.3945">Get on demand only.</text>
<text class="text" textLength="123" x="703.25" y="60.8008">Do not cache result.</text>
<text class="text" textLength="276" x="703.25" y="77.207">Do not request in constructor unless needed.</text>
<rect class="process" filter="url(#f162mg05eaivl4)" height="36.4063" rx="12.5" ry="12.5" width="84" x="593.25" y="29.6094"/>
<text class="text" textLength="64" x="603.25" y="52.5977">getService</text>
<rect class="process" filter="url(#f162mg05eaivl4)" height="36.4883" rx="12.5" ry="12.5" width="91" x="589.75" y="206.0313"/>
<text class="text" textLength="42" x="599.75" y="229.1016">Return</text>
<text class="code" textLength="26" x="644.75" y="229.5664">null</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="547.75,155.8281,722.75,155.8281,734.75,167.8281,722.75,179.8281,547.75,179.8281,535.75,167.8281,547.75,155.8281" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="639.25" y="192.8164">no</text>
<text class="text" textLength="11" x="547.75" y="172.6133">Is</text>
<a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" target="_top" title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" xlink:actuate="onRequest" xlink:href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" xlink:show="new" xlink:title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" xlink:type="simple">
<text fill="#1D1D1D" font-family="Roboto" font-size="14" lengthAdjust="spacing" text-decoration="underline" textLength="118" x="561.75" y="172.6133">Service Declaration</text>
</a>
<text class="text" textLength="40" x="682.75" y="172.6133">Found</text>
<text class="text" textLength="21" x="734.75" y="164.4102">yes</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="588.75,105.625,681.75,105.625,693.75,117.625,681.75,129.625,588.75,129.625,576.75,117.625,588.75,105.625" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="639.25" y="142.6133">no</text>
<text class="text" textLength="11" x="588.75" y="122.4102">Is</text>
<a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" target="_top" title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" xlink:actuate="onRequest" xlink:href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" xlink:show="new" xlink:title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" xlink:type="simple">
<text fill="#1D1D1D" font-family="Roboto" font-size="14" lengthAdjust="spacing" text-decoration="underline" textLength="79" x="602.75" y="122.4102">Light Service</text>
</a>
<text class="text" textLength="21" x="693.75" y="114.207">yes</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="635.25,284.5195,647.25,296.5195,635.25,308.5195,623.25,296.5195,635.25,284.5195" style="stroke:#383838;stroke-width:1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="573.75,378.7227,696.75,378.7227,708.75,390.7227,696.75,402.7227,573.75,402.7227,561.75,390.7227,573.75,378.7227" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="123" x="573.75" y="395.5078">Is Container Active?</text>
<text class="text" textLength="37" x="524.75" y="387.3047">active</text>
<text class="text" textLength="195" x="708.75" y="387.3047">disposed or dispose in progress</text>
<rect fill="#FFFFFF" filter="url(#f162mg05eaivl4)" height="426.5352" style="stroke:#000000;stroke-width:1.5;" width="867" x="11" y="412.7227"/>
<path d="M204,412.7227 L204,422.1289 L194,432.1289 L11,432.1289 " fill="none" style="stroke:#000000;stroke-width:1.5;"/>
<text class="text" textLength="183" x="14" y="426.7109">synchronized on service class</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="207.5,499.332,291.5,499.332,303.5,511.332,291.5,523.332,207.5,523.332,195.5,511.332,207.5,499.332" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="84" x="207.5" y="516.1172">Is Initializing?</text>
<text class="text" textLength="21" x="174.5" y="507.9141">yes</text>
<text class="text" textLength="16" x="303.5" y="507.9141">no</text>
<rect class="process" filter="url(#f162mg05eaivl4)" height="52.8945" rx="12.5" ry="12.5" width="182" x="23" y="533.332"/>
<text class="text" textLength="40" x="37" y="556.4023">Throw</text>
<text class="code" textLength="111" x="80" y="556.8672">PluginException</text>
<text class="text" textLength="162" x="33" y="572.8086">Cyclic Service Initialization</text>
<rect fill="#FFFFFF" filter="url(#f162mg05eaivl4)" height="249.9258" style="stroke:#000000;stroke-width:1.5;" width="611" x="225" y="533.332"/>
<path d="M328,533.332 L328,542.7383 L318,552.7383 L225,552.7383 " fill="none" style="stroke:#000000;stroke-width:1.5;"/>
<text class="text" textLength="93" x="228" y="547.3203">non cancelable</text>
<path class="note" d="M464,569.7383 L464,587.1445 L444,591.1445 L464,595.1445 L464,612.5508 A0,0 0 0 0 464,612.5508 L826,612.5508 A0,0 0 0 0 826,612.5508 L826,579.7383 L816,569.7383 L464,569.7383 A0,0 0 0 0 464,569.7383 " filter="url(#f162mg05eaivl4)"/>
<path class="note" d="M816,569.7383 L816,579.7383 L826,579.7383 L816,569.7383 "/>
<text class="text" textLength="341" x="470" y="587.7266">Avoid getting other services to reduce initialization tree.</text>
<text class="text" textLength="305" x="470" y="604.1328">As less dependencies, as more faster and reliable.</text>
<rect class="process" filter="url(#f162mg05eaivl4)" height="36.4063" width="118" x="326" y="572.9414"/>
<text class="text" textLength="98" x="336" y="595.9297">Create Instance</text>
<rect class="process" filter="url(#f162mg05eaivl4)" height="52.8945" width="300" x="235" y="645.4688"/>
<text class="text" textLength="280" x="245" y="668.457">Register to be Disposed on Container Dispose</text>
<text class="text" textLength="84" x="304" y="684.9453">if Implements</text>
<text class="code" textLength="75" x="391" y="685.4102">Disposable</text>
<rect class="process" filter="url(#f162mg05eaivl4)" height="52.8945" width="289" x="240.5" y="718.3633"/>
<text class="text" textLength="134" x="318" y="741.3516">Load Persistent State</text>
<text class="text" textLength="84" x="250.5" y="757.8398">if Implements</text>
<text class="code" textLength="182" x="337.5" y="758.3047">PersistentStateComponent</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="170,449.1289,329,449.1289,341,461.1289,329,473.1289,170,473.1289,158,461.1289,170,449.1289" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="253.5" y="486.1172">no</text>
<text class="text" textLength="159" x="170" y="465.9141">Is Created and Initialized?</text>
<text class="text" textLength="21" x="341" y="457.7109">yes</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="249.5,803.2578,261.5,815.2578,249.5,827.2578,237.5,815.2578,249.5,803.2578" style="stroke:#383838;stroke-width:1.0;"/>
<rect class="process" filter="url(#f162mg05eaivl4)" height="36.4883" rx="12.5" ry="12.5" width="246" x="898" y="412.7227"/>
<text class="text" textLength="40" x="908" y="435.793">Throw</text>
<text class="code" textLength="183" x="951" y="436.2578">ProcessCanceledException</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="555.75,328.5195,714.75,328.5195,726.75,340.5195,714.75,352.5195,555.75,352.5195,543.75,340.5195,555.75,328.5195" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="639.25" y="365.5078">no</text>
<text class="text" textLength="159" x="555.75" y="345.3047">Is Created and Initialized?</text>
<text class="text" textLength="21" x="726.75" y="337.1016">yes</text>
<polygon fill="#F8F8F8" filter="url(#f162mg05eaivl4)" points="635.25,859.2578,647.25,871.2578,635.25,883.2578,623.25,871.2578,635.25,859.2578" style="stroke:#383838;stroke-width:1.0;"/>
<rect class="process" filter="url(#f162mg05eaivl4)" height="36.4063" rx="12.5" ry="12.5" width="119" x="575.75" y="903.2578"/>
<text class="text" textLength="99" x="585.75" y="926.2461">Return Instance</text>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="179.8281" y2="206.0313"/>
<polygon fill="#383838" points="631.25,196.0313,635.25,206.0313,639.25,196.0313,635.25,200.0313" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="734.75" x2="746.75" y1="167.8281" y2="167.8281"/>
<polygon fill="#383838" points="742.75,211.2754,746.75,221.2754,750.75,211.2754,746.75,215.2754" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="746.75" x2="746.75" y1="167.8281" y2="264.5195"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="746.75" x2="635.25" y1="264.5195" y2="264.5195"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="264.5195" y2="284.5195"/>
<polygon fill="#383838" points="631.25,274.5195,635.25,284.5195,639.25,274.5195,635.25,278.5195" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="129.625" y2="155.8281"/>
<polygon fill="#383838" points="631.25,145.8281,635.25,155.8281,639.25,145.8281,635.25,149.8281" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="693.75" x2="756.75" y1="117.625" y2="117.625"/>
<polygon fill="#383838" points="752.75,205.2754,756.75,215.2754,760.75,205.2754,756.75,209.2754" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="756.75" x2="756.75" y1="117.625" y2="296.5195"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="756.75" x2="647.25" y1="296.5195" y2="296.5195"/>
<polygon fill="#383838" points="657.25,292.5195,647.25,296.5195,657.25,300.5195,653.25,296.5195" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="66.0156" y2="105.625"/>
<polygon fill="#383838" points="631.25,95.625,635.25,105.625,639.25,95.625,635.25,99.625" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="385" x2="385" y1="609.3477" y2="645.4688"/>
<polygon fill="#383838" points="381,635.4688,385,645.4688,389,635.4688,385,639.4688" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="385" x2="385" y1="698.3633" y2="718.3633"/>
<polygon fill="#383838" points="381,708.3633,385,718.3633,389,708.3633,385,712.3633" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="195.5" x2="114" y1="511.332" y2="511.332"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="114" x2="114" y1="511.332" y2="533.332"/>
<polygon fill="#383838" points="110,523.332,114,533.332,118,523.332,114,527.332" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="303.5" x2="385" y1="511.332" y2="511.332"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="385" x2="385" y1="511.332" y2="572.9414"/>
<polygon fill="#383838" points="381,562.9414,385,572.9414,389,562.9414,385,566.9414" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="385" x2="385" y1="771.2578" y2="788.2578"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="385" x2="249.5" y1="788.2578" y2="788.2578"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="249.5" x2="249.5" y1="788.2578" y2="803.2578"/>
<polygon fill="#383838" points="245.5,793.2578,249.5,803.2578,253.5,793.2578,249.5,797.2578" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="249.5" x2="249.5" y1="473.1289" y2="499.332"/>
<polygon fill="#383838" points="245.5,489.332,249.5,499.332,253.5,489.332,249.5,493.332" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="341" x2="856" y1="461.1289" y2="461.1289"/>
<polygon fill="#383838" points="852,644.7539,856,654.7539,860,644.7539,856,648.7539" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="856" x2="856" y1="461.1289" y2="815.2578"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="856" x2="261.5" y1="815.2578" y2="815.2578"/>
<polygon fill="#383838" points="271.5,811.2578,261.5,815.2578,271.5,819.2578,267.5,815.2578" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="561.75" x2="249.5" y1="390.7227" y2="390.7227"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="249.5" x2="249.5" y1="390.7227" y2="449.1289"/>
<polygon fill="#383838" points="245.5,439.1289,249.5,449.1289,253.5,439.1289,249.5,443.1289" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="708.75" x2="1021" y1="390.7227" y2="390.7227"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="1021" x2="1021" y1="390.7227" y2="412.7227"/>
<polygon fill="#383838" points="1017,402.7227,1021,412.7227,1025,402.7227,1021,406.7227" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="249.5" x2="249.5" y1="827.2578" y2="844.2578"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="249.5" x2="635.25" y1="844.2578" y2="844.2578"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="844.2578" y2="859.2578"/>
<polygon fill="#383838" points="631.25,849.2578,635.25,859.2578,639.25,849.2578,635.25,853.2578" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="352.5195" y2="378.7227"/>
<polygon fill="#383838" points="631.25,368.7227,635.25,378.7227,639.25,368.7227,635.25,372.7227" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="726.75" x2="1158" y1="340.5195" y2="340.5195"/>
<polygon fill="#383838" points="1154,622.5508,1158,632.5508,1162,622.5508,1158,626.5508" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="1158" x2="1158" y1="340.5195" y2="871.2578"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="1158" x2="647.25" y1="871.2578" y2="871.2578"/>
<polygon fill="#383838" points="657.25,867.2578,647.25,871.2578,657.25,875.2578,653.25,871.2578" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="308.5195" y2="328.5195"/>
<polygon fill="#383838" points="631.25,318.5195,635.25,328.5195,639.25,318.5195,635.25,322.5195" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="635.25" x2="635.25" y1="883.2578" y2="903.2578"/>
<polygon fill="#383838" points="631.25,893.2578,635.25,903.2578,639.25,893.2578,635.25,897.2578" style="stroke:#383838;stroke-width:1.0;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,186 @@
<svg xmlns="http://www.w3.org/2000/svg" contentScriptType="application/ecmascript" contentStyleType="text/css" height="969px" preserveAspectRatio="none" style="width:919px;height:969px;background:#FFFFFF;" version="1.1" viewBox="0 0 919 969" width="919px" zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.process {
stroke:#383838;
stroke-width:1.0;
fill: #F8F8F8;
}
.note {
stroke:#383838;
stroke-width:1.0;
fill: #EBEBEB;
}
</style>
<defs>
<filter height="300%" id="f1e9hdx0p78zzs" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<path class="note" d="M359,15.959 L359,25.2031 L339,29.2031 L359,33.2031 L359,42.4473 A0,0 0 0 0 359,42.4473 L670,42.4473 A0,0 0 0 0 670,42.4473 L670,25.959 L660,15.959 L359,15.959 A0,0 0 0 0 359,15.959 " filter="url(#f1e9hdx0p78zzs)"/>
<path class="note" d="M660,15.959 L660,25.959 L670,25.959 L660,15.959 "/>
<text class="text" textLength="151" x="365" y="34.0293">Externally called method</text>
<text class="code" textLength="136" x="519" y="34.4941">IconLoader.findIcon</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" rx="12.5" ry="12.5" width="74" x="265" y="11"/>
<text class="text" textLength="54" x="275" y="33.9883">find-icon</text>
<path class="note" d="M374.5,64.1211 L374.5,81.6094 L354.5,85.6094 L374.5,89.6094 L374.5,107.0977 A0,0 0 0 0 374.5,107.0977 L907.5,107.0977 A0,0 0 0 0 907.5,107.0977 L907.5,74.1211 L897.5,64.1211 L374.5,64.1211 A0,0 0 0 0 374.5,64.1211 " filter="url(#f1e9hdx0p78zzs)"/>
<path class="note" d="M897.5,64.1211 L897.5,74.1211 L907.5,74.1211 L897.5,64.1211 "/>
<text class="text" textLength="148" x="380.5" y="82.1914">Internally called method</text>
<text class="code" textLength="198" x="531.5" y="82.6563">CachedImageIcon.loadImage</text>
<text class="text" textLength="4" x="729.5" y="82.1914">.</text>
<text class="text" textLength="164" x="380.5" y="98.6797">Called numerous times per</text>
<text class="code" textLength="122" x="547.5" y="99.1445">CachedImageIcon</text>
<text class="text" textLength="220" x="672.5" y="98.6797">instance — to compute a new scale.</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" rx="12.5" ry="12.5" width="105" x="249.5" y="67.4063"/>
<text class="text" textLength="85" x="259.5" y="90.3945">find-icon-load</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="281.5,127.0977,322.5,127.0977,334.5,139.0977,322.5,151.0977,281.5,151.0977,269.5,139.0977,281.5,127.0977" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="41" x="281.5" y="143.8828">Is SVG</text>
<text class="text" textLength="21" x="248.5" y="135.6797">yes</text>
<text class="text" textLength="16" x="334.5" y="135.6797">no</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="73" x="218.5" y="161.0977"/>
<text class="text" textLength="53" x="228.5" y="184.0859">svg-load</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="75" x="311.5" y="161.0977"/>
<text class="text" textLength="55" x="321.5" y="184.0859">png-load</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="302,203.5039,314,215.5039,302,227.5039,290,215.5039,302,203.5039" style="stroke:#383838;stroke-width:1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="222,297.707,382,297.707,394,309.707,382,321.707,222,321.707,210,309.707,222,297.707" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="160" x="222" y="314.4922">Is resourceClass Provided</text>
<text class="text" textLength="21" x="189" y="306.2891">yes</text>
<text class="text" textLength="16" x="394" y="306.2891">no</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="139" x="100.5" y="331.707"/>
<text class="text" textLength="119" x="110.5" y="354.6953">load-from-resource</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="85,388.1133,255,388.1133,267,400.1133,255,412.1133,85,412.1133,73,400.1133,85,388.1133" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="170" x="85" y="404.8984">Is prebuiltCacheId Provided</text>
<text class="text" textLength="21" x="52" y="396.6953">yes</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="93" x="16.5" y="422.1133"/>
<text class="text" textLength="73" x="26.5" y="445.1016">svg-prebuilt</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" rx="12.5" ry="12.5" width="104" x="11" y="493.5195"/>
<text class="text" textLength="84" x="21" y="516.5078">Return Image</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4883" rx="12.5" ry="12.5" width="91" x="231.5" y="472.3164"/>
<text class="text" textLength="42" x="241.5" y="495.3867">Return</text>
<text class="code" textLength="26" x="286.5" y="495.8516">null</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="224.5,422.1133,329.5,422.1133,341.5,434.1133,329.5,446.1133,224.5,446.1133,212.5,434.1133,224.5,422.1133" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="281" y="459.1016">no</text>
<text class="text" textLength="105" x="224.5" y="438.8984">Is Resource Exist</text>
<text class="text" textLength="21" x="341.5" y="430.6953">yes</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="101" x="383.5" y="331.707"/>
<text class="text" textLength="81" x="393.5" y="354.6953">load-from-url</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="302,551.0078,314,563.0078,302,575.0078,290,563.0078,302,551.0078" style="stroke:#383838;stroke-width:1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="281.5,601.4668,322.5,601.4668,334.5,613.4668,322.5,625.4668,281.5,625.4668,269.5,613.4668,281.5,601.4668" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="41" x="281.5" y="618.252">Is SVG</text>
<text class="text" textLength="21" x="248.5" y="610.0488">yes</text>
<text class="text" textLength="16" x="334.5" y="610.0488">no</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="115" x="177.5" y="635.4668"/>
<text class="text" textLength="95" x="187.5" y="658.4551">svg-cache-read</text>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="91" x="189.5" y="742.0762"/>
<text class="text" textLength="71" x="199.5" y="765.0645">svg-decode</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="204.5,691.873,265.5,691.873,277.5,703.873,265.5,715.873,204.5,715.873,192.5,703.873,204.5,691.873" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="239" y="728.8613">no</text>
<text class="text" textLength="61" x="204.5" y="708.6582">Is Cached</text>
<text class="text" textLength="21" x="277.5" y="700.4551">yes</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="235,798.4824,247,810.4824,235,822.4824,223,810.4824,235,798.4824" style="stroke:#383838;stroke-width:1.0;"/>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" width="93" x="322.5" y="635.4668"/>
<text class="text" textLength="73" x="332.5" y="658.4551">png-decode</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="302,828.4824,314,840.4824,302,852.4824,290,840.4824,302,828.4824" style="stroke:#383838;stroke-width:1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="271.5,247.5039,332.5,247.5039,344.5,259.5039,332.5,271.5039,271.5,271.5039,259.5,259.5039,271.5,247.5039" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="16" x="306" y="284.4922">no</text>
<text class="text" textLength="61" x="271.5" y="264.2891">Is Cached</text>
<text class="text" textLength="21" x="344.5" y="256.0859">yes</text>
<polygon fill="#F8F8F8" filter="url(#f1e9hdx0p78zzs)" points="302,872.4824,314,884.4824,302,896.4824,290,884.4824,302,872.4824" style="stroke:#383838;stroke-width:1.0;"/>
<rect class="process" filter="url(#f1e9hdx0p78zzs)" height="36.4063" rx="12.5" ry="12.5" width="104" x="250" y="916.4824"/>
<text class="text" textLength="84" x="260" y="939.4707">Return Image</text>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="47.4063" y2="67.4063"/>
<polygon fill="#383838" points="298,57.4063,302,67.4063,306,57.4063,302,61.4063" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="269.5" x2="255" y1="139.0977" y2="139.0977"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="255" x2="255" y1="139.0977" y2="161.0977"/>
<polygon fill="#383838" points="251,151.0977,255,161.0977,259,151.0977,255,155.0977" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="334.5" x2="349" y1="139.0977" y2="139.0977"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="349" x2="349" y1="139.0977" y2="161.0977"/>
<polygon fill="#383838" points="345,151.0977,349,161.0977,353,151.0977,349,155.0977" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="255" x2="255" y1="197.5039" y2="215.5039"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="255" x2="290" y1="215.5039" y2="215.5039"/>
<polygon fill="#383838" points="280,211.5039,290,215.5039,280,219.5039,284,215.5039" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="349" x2="349" y1="197.5039" y2="215.5039"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="349" x2="314" y1="215.5039" y2="215.5039"/>
<polygon fill="#383838" points="324,211.5039,314,215.5039,324,219.5039,320,215.5039" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="103.8125" y2="127.0977"/>
<polygon fill="#383838" points="298,117.0977,302,127.0977,306,117.0977,302,121.0977" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="63" x2="63" y1="458.5195" y2="493.5195"/>
<polygon fill="#383838" points="59,483.5195,63,493.5195,67,483.5195,63,487.5195" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="277" x2="277" y1="446.1133" y2="472.3164"/>
<polygon fill="#383838" points="273,462.3164,277,472.3164,281,462.3164,277,466.3164" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="341.5" x2="353.5" y1="434.1133" y2="434.1133"/>
<polygon fill="#383838" points="349.5,480.5605,353.5,490.5605,357.5,480.5605,353.5,484.5605" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="353.5" x2="353.5" y1="434.1133" y2="546.0078"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="353.5" x2="170" y1="546.0078" y2="546.0078"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="170" x2="170" y1="546.0078" y2="563.0078"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="170" x2="290" y1="563.0078" y2="563.0078"/>
<polygon fill="#383838" points="280,559.0078,290,563.0078,280,567.0078,284,563.0078" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="73" x2="63" y1="400.1133" y2="400.1133"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="63" x2="63" y1="400.1133" y2="422.1133"/>
<polygon fill="#383838" points="59,412.1133,63,422.1133,67,412.1133,63,416.1133" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="267" x2="277" y1="400.1133" y2="400.1133"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="277" x2="277" y1="400.1133" y2="422.1133"/>
<polygon fill="#383838" points="273,412.1133,277,422.1133,281,412.1133,277,416.1133" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="170" x2="170" y1="368.1133" y2="388.1133"/>
<polygon fill="#383838" points="166,378.1133,170,388.1133,174,378.1133,170,382.1133" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="210" x2="170" y1="309.707" y2="309.707"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="170" x2="170" y1="309.707" y2="331.707"/>
<polygon fill="#383838" points="166,321.707,170,331.707,174,321.707,170,325.707" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="394" x2="434" y1="309.707" y2="309.707"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="434" x2="434" y1="309.707" y2="331.707"/>
<polygon fill="#383838" points="430,321.707,434,331.707,438,321.707,434,325.707" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="434" x2="434" y1="368.1133" y2="563.0078"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="434" x2="314" y1="563.0078" y2="563.0078"/>
<polygon fill="#383838" points="324,559.0078,314,563.0078,324,567.0078,320,563.0078" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="235" x2="235" y1="715.873" y2="742.0762"/>
<polygon fill="#383838" points="231,732.0762,235,742.0762,239,732.0762,235,736.0762" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="277.5" x2="290.5" y1="703.873" y2="703.873"/>
<polygon fill="#383838" points="286.5,750.2793,290.5,760.2793,294.5,750.2793,290.5,754.2793" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="290.5" x2="290.5" y1="703.873" y2="810.4824"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="290.5" x2="247" y1="810.4824" y2="810.4824"/>
<polygon fill="#383838" points="257,806.4824,247,810.4824,257,814.4824,253,810.4824" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="235" x2="235" y1="778.4824" y2="798.4824"/>
<polygon fill="#383838" points="231,788.4824,235,798.4824,239,788.4824,235,792.4824" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="235" x2="235" y1="671.873" y2="691.873"/>
<polygon fill="#383838" points="231,681.873,235,691.873,239,681.873,235,685.873" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="269.5" x2="235" y1="613.4668" y2="613.4668"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="235" x2="235" y1="613.4668" y2="635.4668"/>
<polygon fill="#383838" points="231,625.4668,235,635.4668,239,625.4668,235,629.4668" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="334.5" x2="369" y1="613.4668" y2="613.4668"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="369" x2="369" y1="613.4668" y2="635.4668"/>
<polygon fill="#383838" points="365,625.4668,369,635.4668,373,625.4668,369,629.4668" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="235" x2="235" y1="822.4824" y2="840.4824"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="235" x2="290" y1="840.4824" y2="840.4824"/>
<polygon fill="#383838" points="280,836.4824,290,840.4824,280,844.4824,284,840.4824" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="369" x2="369" y1="671.873" y2="840.4824"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="369" x2="314" y1="840.4824" y2="840.4824"/>
<polygon fill="#383838" points="324,836.4824,314,840.4824,324,844.4824,320,840.4824" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="575.0078" y2="601.4668"/>
<polygon fill="#383838" points="298,591.4668,302,601.4668,306,591.4668,302,595.4668" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="271.5039" y2="297.707"/>
<polygon fill="#383838" points="298,287.707,302,297.707,306,287.707,302,291.707" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="344.5" x2="504.5" y1="259.5039" y2="259.5039"/>
<polygon fill="#383838" points="500.5,571.4668,504.5,581.4668,508.5,571.4668,504.5,575.4668" style="stroke:#383838;stroke-width:1.5;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="504.5" x2="504.5" y1="259.5039" y2="884.4824"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="504.5" x2="314" y1="884.4824" y2="884.4824"/>
<polygon fill="#383838" points="324,880.4824,314,884.4824,324,888.4824,320,884.4824" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="852.4824" y2="872.4824"/>
<polygon fill="#383838" points="298,862.4824,302,872.4824,306,862.4824,302,866.4824" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="227.5039" y2="247.5039"/>
<polygon fill="#383838" points="298,237.5039,302,247.5039,306,237.5039,302,241.5039" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="302" x2="302" y1="896.4824" y2="916.4824"/>
<polygon fill="#383838" points="298,906.4824,302,916.4824,306,906.4824,302,910.4824" style="stroke:#383838;stroke-width:1.0;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

81
docs/out/index.html Normal file
View File

@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IntelliJ Platform Activity Diagrams</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css" integrity="sha256-O8SsQwDg1R10WnKJNyYgd9J3rlom+YSVcGbEF5RmfFk=" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/@panzoom/panzoom@4.4.0/dist/panzoom.min.js" integrity="sha256-+djd9iOn6UHGwEA/IKKJL4nUsY9LaSoc8YYtbFhZSPw=" crossorigin="anonymous"></script>
</head>
<body>
<script>
let panzoom = null
function applyItem(item) {
document.getElementById("diagramSelector").value = item.value
fetch(`./${item.value}.svg`)
.then(response => response.text())
.then(svgContent => {
const title = `${item.label} — IntelliJ Platform Activity Diagrams`
document.title = title
const id = item.value
history.pushState(null, title, `#${id}`)
localStorage.setItem("selectedDiagram", id)
if (panzoom != null) {
panzoom.destroy()
}
const parent = document.getElementById("svgContainer")
while (parent.firstChild != null) {
parent.removeChild(parent.lastChild)
}
parent.insertAdjacentHTML("afterbegin", svgContent)
panzoom = Panzoom(parent.firstChild, {
disableZoom: true,
handleStartEvent: (event) => {
event.stopPropagation()
// otherwise text is not selectable (at least, works in Safari, doesn't work in Chrome in any case)
// event.preventDefault()
},
})
})
}
function diagramChanged(event) {
applyItem(event.target.options[event.target.selectedIndex])
}
</script>
<section class="section">
<div class="select is-pulled-right panzoom-exclude">
<label>
<select id="diagramSelector" onchange="diagramChanged(event)">
<option value="getting-service">Getting Service</option>
<option value="projectClose-dispose-flow">Close Project Flow</option>
<option value="icon-loading-stat">Icon Loading Stats</option>
<option value="plugin-model">Plugin Model V2</option>
<option value="plugin-graph">Plugin graph (ultimate)</option>
</select>
</label>
</div>
<!--suppress CheckTagEmptyBody -->
<div id="svgContainer" style="width: 100%; height: 100%;">
</div>
</section>
<script>
(function () {
const urlPath = window.location.hash
const selectedId = urlPath != null && urlPath.length > 1 ? urlPath.substring(1) : localStorage.getItem("selectedDiagram")
if (selectedId == null) {
applyItem(document.getElementById("diagramSelector").options[0])
}
else {
applyItem(Array.from(document.getElementById("diagramSelector").options).find(it => it.value === selectedId))
}
})()
</script>
</body>
</html>

3369
docs/out/plugin-graph.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 334 KiB

118
docs/out/plugin-model.svg Normal file
View File

@@ -0,0 +1,118 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="579px" preserveAspectRatio="none" style="width:1917px;height:579px;background:#FFFFFF;" version="1.1" viewBox="0 0 1917 579" width="1917px" zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.note {
stroke:#383838;
stroke-width:1.0;
fill: #EBEBEB;
}
</style>
<defs>
<filter height="300%" id="f1shekoa8o9m49" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<!--MD5=[1085852dcf5b25a180ffe435ef773747]
cluster PluginContent-->
<polygon fill="#FFFFFF" filter="url(#f1shekoa8o9m49)" points="185,139,240,139,247,161.4063,1615,161.4063,1615,572,185,572,185,139" style="stroke:#000000;stroke-width:1.5;"/>
<line style="stroke:#000000;stroke-width:1.5;" x1="185" x2="247" y1="161.4063" y2="161.4063"/>
<text font-family="Roboto" font-size="14" font-weight="bold" textLength="49" x="189" y="153.9883">content</text>
<!--MD5=[8643c3a2e8ffb0c77ba1cfb35bcb9aeb]
cluster ModuleDependencies-->
<polygon fill="#FFFFFF" filter="url(#f1shekoa8o9m49)" points="383,308,479,308,486,330.4063,1127,330.4063,1127,532,383,532,383,308" style="stroke:#000000;stroke-width:1.5;"/>
<line style="stroke:#000000;stroke-width:1.5;" x1="383" x2="486" y1="330.4063" y2="330.4063"/>
<text font-family="Roboto" font-size="14" font-weight="bold" textLength="90" x="387" y="322.9883">dependencies</text>
<!--MD5=[7544cd838908b9c69ef913353da45cfc]
cluster PluginDependencies-->
<polygon fill="#FFFFFF" filter="url(#f1shekoa8o9m49)" points="1655,137,1751,137,1758,159.4063,1900,159.4063,1900,414,1655,414,1655,137" style="stroke:#000000;stroke-width:1.5;"/>
<line style="stroke:#000000;stroke-width:1.5;" x1="1655" x2="1758" y1="159.4063" y2="159.4063"/>
<text font-family="Roboto" font-size="14" font-weight="bold" textLength="90" x="1659" y="151.9883">dependencies</text>
<!--MD5=[c72e14692f063a08d1023b5b76acc947]
entity M-->
<rect fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" height="36.4063" style="stroke:#000000;stroke-width:1.5;" width="159" x="771.5" y="182"/>
<text class="text" textLength="139" x="781.5" y="204.9883">module name="…" 0…N</text>
<!--MD5=[2e7f0958813e0c469f8dc1b0f09fcd25]
entity M_N-->
<rect fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" height="36.4063" style="stroke:#000000;stroke-width:1.5;" width="142" x="217" y="354.5"/>
<text class="text" textLength="122" x="227" y="377.4883">module name="…" N</text>
<path class="note" d="M966,187 L966,196 L930.74,200 L966,204 L966,213.4883 A0,0 0 0 0 966,213.4883 L1332,213.4883 A0,0 0 0 0 1332,213.4883 L1332,197 L1322,187 L966,187 A0,0 0 0 0 966,187 " filter="url(#f1shekoa8o9m49)"/>
<path class="note" d="M1322,187 L1322,197 L1332,197 L1322,187 "/>
<text class="code" textLength="52" x="972" y="205.5352">content</text>
<text class="text" textLength="290" x="1027" y="205.0703">is not allowed and is not supported for module.</text>
<path class="note" d="M1151.5,469 L1151.5,511.8125 L1590.5,511.8125 L1590.5,479 L1580.5,469 L1151.5,469 " filter="url(#f1shekoa8o9m49)"/>
<path class="note" d="M1580.5,469 L1580.5,479 L1590.5,479 L1580.5,469 "/>
<text class="text" textLength="242" x="1157.5" y="486.9883">Module dependency is always optional.</text>
<text class="text" textLength="418" x="1157.5" y="503.3945">If the module depends on an unavailable plugin, it will not be loaded.</text>
<ellipse cx="495.9225" cy="372.6845" fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" rx="88.9225" ry="20.1845" style="stroke:#383838;stroke-width:1.5;"/>
<text class="text" textLength="139" x="426.24" y="375.5368">module name="…" 0…N</text>
<ellipse cx="496.2383" cy="490.5477" fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" rx="75.7383" ry="17.5477" style="stroke:#383838;stroke-width:1.5;"/>
<text class="text" textLength="107" x="439.7383" y="494.2918">plugin id="…" 0…N</text>
<path class="note" d="M620,351 L620,368.5 L585,372.5 L620,376.5 L620,393.8125 A0,0 0 0 0 620,393.8125 L1084,393.8125 A0,0 0 0 0 1084,393.8125 L1084,361 L1074,351 L620,351 A0,0 0 0 0 620,351 " filter="url(#f1shekoa8o9m49)"/>
<path class="note" d="M1074,351 L1074,361 L1084,361 L1074,351 "/>
<text class="text" textLength="443" x="626" y="368.9883">The dependency is specified in a module descriptor itself in a new model,</text>
<text class="text" textLength="220" x="626" y="385.3945">not where the module is referenced.</text>
<ellipse cx="1786.9225" cy="200.1845" fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" rx="88.9225" ry="20.1845" style="stroke:#383838;stroke-width:1.5;"/>
<text class="text" textLength="139" x="1717.24" y="203.0368">module name="…" 0…N</text>
<ellipse cx="1787.2383" cy="372.5477" fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" rx="75.7383" ry="17.5477" style="stroke:#383838;stroke-width:1.5;"/>
<text class="text" textLength="107" x="1730.7383" y="376.2918">plugin id="…" 0…N</text>
<!--MD5=[626ad08abde0879284d52f51a0eb42aa]
entity P-->
<rect fill="#F8F8F8" filter="url(#f1shekoa8o9m49)" height="36.4063" style="stroke:#000000;stroke-width:1.5;" width="58" x="822" y="17.5"/>
<a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-impl/src/com/intellij/ide/plugins/readme.md" target="_top" title="https://github.com/JetBrains/intellij-community/blob/master/platform/core-impl/src/com/intellij/ide/plugins/readme.md" xlink:actuate="onRequest" xlink:href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-impl/src/com/intellij/ide/plugins/readme.md" xlink:show="new" xlink:title="https://github.com/JetBrains/intellij-community/blob/master/platform/core-impl/src/com/intellij/ide/plugins/readme.md" xlink:type="simple">
<text fill="#1D1D1D" font-family="Roboto" font-size="14" lengthAdjust="spacing" text-decoration="underline" textLength="38" x="832" y="40.4883">plugin</text>
</a>
<path class="note" d="M6,6 L6,65.2188 L380,65.2188 L380,16 L370,6 L6,6 " filter="url(#f1shekoa8o9m49)"/>
<path class="note" d="M370,6 L370,16 L380,16 L370,6 "/>
<text class="text" textLength="353" x="12" y="23.9883">Every plugin is a module, but not every module is a plugin.</text>
<text class="text" textLength="313" x="12" y="40.3945">A plugin is a group of related modules and for now,</text>
<text class="text" textLength="240" x="12" y="56.8008">it is the only way to distribute modules.</text>
<path class="note" d="M1588,22.5 L1588,48.9883 L1738,48.9883 L1738,32.5 L1728,22.5 L1588,22.5 " filter="url(#f1shekoa8o9m49)"/>
<path class="note" d="M1728,22.5 L1728,32.5 L1738,32.5 L1728,22.5 "/>
<text class="text" textLength="74" x="1594" y="40.5703">Same as for</text>
<text class="code" textLength="52" x="1671" y="41.0352">module</text>
<!--MD5=[18548e575689829418d8810be97b2069]
link M to M_N-->
<path d="M777.26,218.03 C774.14,218.7 771.04,219.36 768,220 C601.05,255.04 546.55,221.97 391,292 C357.01,307.3 324.05,335.9 304.91,354.31 " fill="none" id="M-M_N" style="stroke:#383838;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
<text class="text" textLength="295" x="517" y="263.9883">a sibling cannot access classes from each other</text>
<text class="text" textLength="205" x="563.5" y="280.3945">unless specified as a dependency</text>
<!--MD5=[efba78f2832f0953bcc7b785fff38546]
link MDM to MDP-->
<!--MD5=[0ddd819f72541fbbce14226ed6701871]
link ModuleDependencies to GMN17-->
<path d="M1127.0064,377.1829 C1127.1423,377.2454 1127.2798,377.3087 1127.4188,377.3727 C1127.975,377.6287 1128.5563,377.8963 1129.1621,378.1752 C1130.3736,378.7329 1131.6829,379.3356 1133.0841,379.9806 C1135.8866,381.2706 1139.0568,382.7298 1142.548,384.3369 C1156.5128,390.765 1175.6144,399.5575 1196.8663,409.34 C1239.37,428.905 1290.475,452.43 1326.29,468.92 " fill="none" id="ModuleDependencies-GMN17" style="stroke:#383838;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
<!--MD5=[0eaf612125eeae4d4d1a7850cb3c676f]
link M to P-->
<path d="M851,181.66 C851,151.93 851,91.29 851,58.58 " fill="none" id="M-to-P" style="stroke:#383838;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
<polygon fill="#383838" points="851,53.58,847,62.58,851,58.58,855,62.58,851,53.58" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="332" x="852" y="108.9883">a module can access classes from a containing plugin</text>
<!--MD5=[e4fb0c60299c8c044b5242684082b752]
link M to ModuleDependencies-->
<path d="M918.85,218.03 C983.8,235.65 1075.44,264.42 1100,292 C1102.925,295.285 1105.4202,298.9402 1107.5467,302.8277 C1108.0783,303.7995 1108.5869,304.7859 1109.0734,305.7847 C1109.3167,306.2841 1109.5544,306.7865 1109.7867,307.2918 C1109.8448,307.4182 1109.9026,307.5447 1109.96,307.6713 " fill="none" id="M-ModuleDependencies" style="stroke:#383838;stroke-width:1.0;"/>
<!--MD5=[711d33e56cf2a4ef8c19a038acc32917]
link GMN22 to PluginContent-->
<path d="M193,65.18 C193,76.66 193,90.5944 193,105.0798 C193,112.3226 193,119.7031 193,126.9835 C193,130.6236 193,134.2388 193,137.7992 C193,138.0217 193,138.244 193,138.4661 " fill="none" id="GMN22-PluginContent" style="stroke:#383838;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
<!--MD5=[9121b255e8a8b7161fd4a52f9019ee5b]
link PDM to PDP-->
<!--MD5=[5d0d5117ce265536cc3f9fbd2158cb1f]
link GMN29 to PluginDependencies-->
<path d="M1663,48.71 C1663,67.31 1663,104.0075 1663,136.4825 " fill="none" id="GMN29-PluginDependencies" style="stroke:#383838;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
<!--MD5=[311b6aca1abd1a6ced7ad906cda1d4cc]
link P to PluginContent-->
<path d="M821.66,39.3 C697.79,51.23 223.07,98.32 201,121 C198.0563,124.0238 195.7081,127.4589 193.8545,131.1615 C192.9277,133.0128 192.1246,134.931 191.4324,136.898 C191.2594,137.3897 191.0933,137.8846 190.9339,138.3821 C190.894,138.5065 190.8546,138.6311 190.8156,138.7558 " fill="none" id="P-PluginContent" style="stroke:#383838;stroke-width:1.0;"/>
<!--MD5=[4533ecc32d705aa5493b3e100c20ed9c]
link P to PluginDependencies-->
<path d="M880.35,36.65 C1003.8,37.76 1480.35,46.84 1607,121 C1624.84,131.445 1638.535,150.0725 1647.915,166.505 C1650.26,170.6131 1652.3353,174.5841 1654.1383,178.2557 C1654.3637,178.7146 1654.5848,179.1689 1654.8016,179.6182 C1654.8558,179.7305 1654.9098,179.8425 1654.9635,179.9542 " fill="none" id="P-PluginDependencies" style="stroke:#383838;stroke-width:1.0;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,175 @@
<svg xmlns="http://www.w3.org/2000/svg" contentScriptType="application/ecmascript" contentStyleType="text/css" height="1160px" preserveAspectRatio="none" style="width:1194px;height:1160px;background:#FFFFFF;" version="1.1" viewBox="0 0 1194 1160" width="1194px" zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.process {
stroke:#383838;
stroke-width:1.0;
fill: #F8F8F8;
}
.note {
stroke:#383838;
stroke-width:1.0;
fill: #EBEBEB;
}
</style>
<defs>
<filter height="300%" id="f1ripw5coh8idk" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<path class="note" d="M881.5,16 L881.5,25.2031 L861.5,29.2031 L881.5,33.2031 L881.5,42.4063 A0,0 0 0 0 881.5,42.4063 L1182.5,42.4063 A0,0 0 0 0 1182.5,42.4063 L1182.5,26 L1172.5,16 L881.5,16 A0,0 0 0 0 881.5,16 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M1172.5,16 L1172.5,26 L1182.5,26 L1172.5,16 "/>
<text class="text" textLength="280" x="887.5" y="33.9883">Project closing executed in a dispatch thread.</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" rx="12.5" ry="12.5" width="96" x="765.5" y="11"/>
<text class="text" textLength="76" x="775.5" y="33.9883">closeProject</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="52.8945" width="197" x="715" y="67.4063"/>
<text class="text" textLength="57" x="785" y="90.3945">canClose</text>
<text class="code" textLength="177" x="725" y="107.3477">(ep=ProjectCloseHandler)</text>
<polygon fill="#F8F8F8" filter="url(#f1ripw5coh8idk)" points="719,140.3008,908,140.3008,920,156.748,908,173.1953,719,173.1953,707,156.748,719,140.3008" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="57" x="785" y="153.2891">canClose</text>
<text class="text" textLength="0" x="731" y="169.7773"/>
<text class="code" textLength="177" x="731" y="170.2422">(ep=ProjectCloseHandler)</text>
<text class="text" textLength="21" x="686" y="153.3301">yes</text>
<text class="text" textLength="16" x="920" y="153.3301">no</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="166" x="472.5" y="183.1953"/>
<text class="text" textLength="146" x="482.5" y="206.1836">Stop Service Preloading</text>
<path class="note" d="M705,244.6836 L705,262.0898 L685,266.0898 L705,270.0898 L705,287.4961 A0,0 0 0 0 705,287.4961 L993,287.4961 A0,0 0 0 0 993,287.4961 L993,254.6836 L983,244.6836 L705,244.6836 A0,0 0 0 0 705,244.6836 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M983,244.6836 L983,254.6836 L993,254.6836 L983,244.6836 "/>
<text class="text" textLength="196" x="711" y="262.6719">In unit test mode in a light tests,</text>
<text class="text" textLength="267" x="711" y="279.0781">light project is not closed and not disposed.</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="52.9766" width="259" x="426" y="239.6016"/>
<text class="text" textLength="23" x="436" y="262.6719">Fire</text>
<text class="code" textLength="175" x="462" y="263.1367">projectClosingBeforeSave</text>
<text class="text" textLength="35" x="640" y="262.6719">Event</text>
<text class="code" textLength="186" x="462.5" y="279.625">(l=ProjectManagerListener)</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="128" x="491.5" y="312.5781"/>
<text class="text" textLength="108" x="501.5" y="335.5664">Save Project Files</text>
<polygon fill="#F8F8F8" filter="url(#f1ripw5coh8idk)" points="490,368.9844,621,368.9844,633,380.9844,621,392.9844,490,392.9844,478,380.9844,490,368.9844" style="stroke:#383838;stroke-width:1.0;"/>
<text class="text" textLength="131" x="490" y="385.7695">Save Project Settings</text>
<text class="text" textLength="178" x="300" y="377.5664">Successfully or Error Ignored</text>
<text class="text" textLength="91" x="633" y="377.5664">Error Occurred</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="52.9766" width="206" x="106.5" y="402.9844"/>
<text class="text" textLength="23" x="127.5" y="426.0547">Fire</text>
<text class="code" textLength="100" x="153.5" y="426.5195">projectClosing</text>
<text class="text" textLength="35" x="256.5" y="426.0547">Event</text>
<text class="code" textLength="186" x="116.5" y="443.0078">(l=ProjectManagerListener)</text>
<rect fill="#FFFFFF" filter="url(#f1ripw5coh8idk)" height="677.334" style="stroke:#000000;stroke-width:1.5;" width="812" x="11" y="465.9609"/>
<path d="M94,465.9609 L94,475.3672 L84,485.3672 L11,485.3672 " fill="none" style="stroke:#000000;stroke-width:1.5;"/>
<text class="text" textLength="73" x="14" y="479.9492">write action</text>
<path class="note" d="M418,495.3672 L418,521.0586 L398,525.0586 L418,529.0586 L418,554.75 A0,0 0 0 0 418,554.75 L813,554.75 A0,0 0 0 0 813,554.75 L813,505.3672 L803,495.3672 L418,495.3672 A0,0 0 0 0 418,495.3672 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M803,495.3672 L803,505.3672 L813,505.3672 L803,495.3672 "/>
<text class="text" textLength="217" x="424" y="513.4375">If you incorrectly specify project for</text>
<text class="code" textLength="151" x="644" y="513.9023">MessageBus.connect()</text>
<text class="text" textLength="3" x="795" y="513.4375">,</text>
<text class="text" textLength="214" x="424" y="529.8438">it will be disconnected on this step.</text>
<text class="text" textLength="88" x="424" y="546.332">Do not specify</text>
<text class="code" textLength="120" x="515" y="546.7969">parentDisposable</text>
<text class="text" textLength="92" x="638" y="546.332">unless needed.</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="377" x="21" y="506.8555"/>
<text class="text" textLength="357" x="31" y="529.8438">Dispose everything that uses Project as parent disposable</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="52.8125" width="296" x="61.5" y="574.75"/>
<text class="text" textLength="263" x="78" y="597.7383">Dispose Project Message Bus Connections</text>
<text class="text" textLength="276" x="71.5" y="614.1445">without explicitly specified parent disposable</text>
<path class="note" d="M378.5,637.5625 L378.5,671.416 L358.5,675.416 L378.5,679.416 L378.5,713.2695 A0,0 0 0 0 378.5,713.2695 L790.5,713.2695 A0,0 0 0 0 790.5,713.2695 L790.5,647.5625 L780.5,637.5625 L378.5,637.5625 A0,0 0 0 0 378.5,637.5625 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M780.5,637.5625 L780.5,647.5625 L790.5,647.5625 L780.5,637.5625 "/>
<text class="text" textLength="294" x="384.5" y="655.5508">Getting services and publishing to message bus</text>
<text class="text" textLength="162" x="384.5" y="671.957">is prohibited from now on.</text>
<text class="code" textLength="125" x="384.5" y="688.9102">Project.isDisposed</text>
<text class="text" textLength="45" x="512.5" y="688.4453">returns</text>
<text class="code" textLength="28" x="560.5" y="688.9102">true</text>
<text class="text" textLength="184" x="591.5" y="688.4453">(not changed in a read action,</text>
<text class="text" textLength="232" x="384.5" y="704.8516">because state is set in a write action).</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4883" width="298" x="60.5" y="657.1719"/>
<text class="text" textLength="119" x="70.5" y="680.2422">Set Project State to</text>
<text class="code" textLength="156" x="192.5" y="680.707">DISPOSE_IN_PROGRESS</text>
<path class="note" d="M403,738.2695 L403,747.4727 L383,751.4727 L403,755.4727 L403,764.6758 A0,0 0 0 0 403,764.6758 L761,764.6758 A0,0 0 0 0 761,764.6758 L761,748.2695 L751,738.2695 L403,738.2695 A0,0 0 0 0 403,738.2695 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M751,738.2695 L751,748.2695 L761,748.2695 L751,738.2695 "/>
<text class="text" textLength="337" x="409" y="756.2578">Connecting to message bus is prohibited from now on.</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="347" x="36" y="733.2695"/>
<text class="text" textLength="327" x="46" y="756.2578">Dispose Project Message Bus Connection Disposable</text>
<path class="note" d="M351,786.4316 L351,803.8789 L331,807.8789 L351,811.8789 L351,829.3262 A0,0 0 0 0 351,829.3262 L664,829.3262 A0,0 0 0 0 664,829.3262 L664,796.4316 L654,786.4316 L351,786.4316 A0,0 0 0 0 351,786.4316 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M654,786.4316 L654,796.4316 L664,796.4316 L654,786.4316 "/>
<text class="text" textLength="55" x="357" y="804.502">Result of</text>
<text class="code" textLength="234" x="415" y="804.9668">ProjectManager.getOpenProjects()</text>
<text class="text" textLength="173" x="357" y="820.9082">is valid only in a read action.</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="243" x="88" y="789.6758"/>
<text class="text" textLength="223" x="98" y="812.6641">Remove Project from List of Opened</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="52.9766" width="206" x="106.5" y="849.3262"/>
<text class="text" textLength="23" x="130" y="872.3965">Fire</text>
<text class="code" textLength="95" x="156" y="872.8613">projectClosed</text>
<text class="text" textLength="35" x="254" y="872.3965">Event</text>
<text class="code" textLength="186" x="116.5" y="889.3496">(l=ProjectManagerListener)</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4883" width="209" x="105" y="922.3027"/>
<text class="text" textLength="119" x="115" y="945.373">Set Project State to</text>
<text class="code" textLength="67" x="237" y="945.8379">DISPOSED</text>
<path class="note" d="M346,975.5879 L346,992.9941 L326,996.9941 L346,1000.9941 L346,1018.4004 A0,0 0 0 0 346,1018.4004 L591,1018.4004 A0,0 0 0 0 591,1018.4004 L591,985.5879 L581,975.5879 L346,975.5879 A0,0 0 0 0 346,975.5879 " filter="url(#f1ripw5coh8idk)"/>
<path class="note" d="M581,975.5879 L581,985.5879 L591,985.5879 L581,975.5879 "/>
<text class="text" textLength="170" x="352" y="993.5762">First created, last disposed.</text>
<text class="text" textLength="224" x="352" y="1009.9824">Children are disposed before parent.</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="233" x="93" y="978.791"/>
<text class="text" textLength="213" x="103" y="1001.7793">Dispose Services and Components</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" width="155" x="132" y="1038.4004"/>
<text class="text" textLength="135" x="142" y="1061.3887">Dispose Message Bus</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4883" rx="12.5" ry="12.5" width="289" x="65" y="1094.8066"/>
<text class="text" textLength="119" x="75" y="1117.877">Set Project State to</text>
<text class="code" textLength="147" x="197" y="1118.3418">DISPOSE_COMPLETED</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" rx="12.5" ry="12.5" width="117" x="843" y="402.9844"/>
<text class="text" textLength="97" x="853" y="425.9727">Close Cancelled</text>
<rect class="process" filter="url(#f1ripw5coh8idk)" height="36.4063" rx="12.5" ry="12.5" width="117" x="1013" y="183.1953"/>
<text class="text" textLength="97" x="1023" y="206.1836">Close Cancelled</text>
<line style="stroke:#383838;stroke-width:1.5;" x1="813.5" x2="813.5" y1="47.4063" y2="67.4063"/>
<polygon fill="#383838" points="809.5,57.4063,813.5,67.4063,817.5,57.4063,813.5,61.4063" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="555.5" x2="555.5" y1="219.6016" y2="239.6016"/>
<polygon fill="#383838" points="551.5,229.6016,555.5,239.6016,559.5,229.6016,555.5,233.6016" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="555.5" x2="555.5" y1="292.5781" y2="312.5781"/>
<polygon fill="#383838" points="551.5,302.5781,555.5,312.5781,559.5,302.5781,555.5,306.5781" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="543.2617" y2="574.75"/>
<polygon fill="#383838" points="205.5,564.75,209.5,574.75,213.5,564.75,209.5,568.75" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="627.5625" y2="657.1719"/>
<polygon fill="#383838" points="205.5,647.1719,209.5,657.1719,213.5,647.1719,209.5,651.1719" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="693.6602" y2="733.2695"/>
<polygon fill="#383838" points="205.5,723.2695,209.5,733.2695,213.5,723.2695,209.5,727.2695" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="769.6758" y2="789.6758"/>
<polygon fill="#383838" points="205.5,779.6758,209.5,789.6758,213.5,779.6758,209.5,783.6758" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="826.082" y2="849.3262"/>
<polygon fill="#383838" points="205.5,839.3262,209.5,849.3262,213.5,839.3262,209.5,843.3262" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="902.3027" y2="922.3027"/>
<polygon fill="#383838" points="205.5,912.3027,209.5,922.3027,213.5,912.3027,209.5,916.3027" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="958.791" y2="978.791"/>
<polygon fill="#383838" points="205.5,968.791,209.5,978.791,213.5,968.791,209.5,972.791" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="1015.1973" y2="1038.4004"/>
<polygon fill="#383838" points="205.5,1028.4004,209.5,1038.4004,213.5,1028.4004,209.5,1032.4004" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="1074.8066" y2="1094.8066"/>
<polygon fill="#383838" points="205.5,1084.8066,209.5,1094.8066,213.5,1084.8066,209.5,1088.8066" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="455.9609" y2="506.8555"/>
<polygon fill="#383838" points="205.5,496.8555,209.5,506.8555,213.5,496.8555,209.5,500.8555" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="478" x2="209.5" y1="380.9844" y2="380.9844"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="209.5" x2="209.5" y1="380.9844" y2="402.9844"/>
<polygon fill="#383838" points="205.5,392.9844,209.5,402.9844,213.5,392.9844,209.5,396.9844" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="633" x2="901.5" y1="380.9844" y2="380.9844"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="901.5" x2="901.5" y1="380.9844" y2="402.9844"/>
<polygon fill="#383838" points="897.5,392.9844,901.5,402.9844,905.5,392.9844,901.5,396.9844" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="555.5" x2="555.5" y1="348.9844" y2="368.9844"/>
<polygon fill="#383838" points="551.5,358.9844,555.5,368.9844,559.5,358.9844,555.5,362.9844" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="707" x2="555.5" y1="156.748" y2="156.748"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="555.5" x2="555.5" y1="156.748" y2="183.1953"/>
<polygon fill="#383838" points="551.5,173.1953,555.5,183.1953,559.5,173.1953,555.5,177.1953" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="920" x2="1071.5" y1="156.748" y2="156.748"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="1071.5" x2="1071.5" y1="156.748" y2="183.1953"/>
<polygon fill="#383838" points="1067.5,173.1953,1071.5,183.1953,1075.5,173.1953,1071.5,177.1953" style="stroke:#383838;stroke-width:1.0;"/>
<line style="stroke:#383838;stroke-width:1.5;" x1="813.5" x2="813.5" y1="120.3008" y2="140.3008"/>
<polygon fill="#383838" points="809.5,130.3008,813.5,140.3008,817.5,130.3008,813.5,134.3008" style="stroke:#383838;stroke-width:1.0;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

732
docs/plugin-graph.puml Normal file
View File

@@ -0,0 +1,732 @@
@startjson
{
"intellij.angularJS" : {
"name" : "intellij.angularJS",
"package" : null,
"descriptor" : "contrib/AngularJS/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.angularJS/diagram",
"package" : "org.angularjs.diagram",
"descriptor" : "contrib/AngularJS/resources/intellij.angularJS.diagram.xml",
"dependencies" : [ {
"plugin" : "com.intellij.diagram"
}, {
"module" : "intellij.javascript.impl/diagrams"
} ]
} ]
},
"intellij.cloudFormation" : {
"name" : "intellij.cloudFormation",
"package" : "com.intellij.aws.cloudformation",
"descriptor" : "contrib/CloudFormation/src/main/resources/META-INF/plugin.xml"
},
"intellij.clouds.docker.impl" : {
"name" : "intellij.clouds.docker.impl",
"package" : "com.intellij.docker",
"descriptor" : "plugins/Docker/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.clouds.docker.java",
"package" : "com.intellij.docker.java",
"descriptor" : "plugins/Docker/Docker-java-impl/resources/intellij.clouds.docker.java.xml",
"dependencies" : [ {
"plugin" : "com.intellij.java"
} ]
}, {
"name" : "intellij.clouds.docker.compose",
"package" : "com.intellij.docker.composeFile",
"descriptor" : "plugins/Docker/Docker-compose/resources/intellij.clouds.docker.compose.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.yaml"
}, {
"plugin" : "Docker"
} ]
}, {
"name" : "intellij.clouds.docker.file",
"package" : "com.intellij.docker.dockerFile",
"descriptor" : "plugins/Docker/Docker-file/resources/intellij.clouds.docker.file.xml"
}, {
"name" : "intellij.clouds.docker.remoteRun",
"package" : "com.intellij.docker.remote",
"descriptor" : "plugins/Docker/Docker-remote-run/resources/intellij.clouds.docker.remoteRun.xml"
} ],
"dependencies" : [ {
"plugin" : "com.intellij.modules.lang"
}, {
"plugin" : "com.intellij.modules.json"
}, {
"plugin" : "com.intellij.modules.remoteServers"
} ]
},
"intellij.clouds.kubernetes" : {
"name" : "intellij.clouds.kubernetes",
"package" : "com.intellij.kubernetes",
"descriptor" : "plugins/kubernetes/resources/META-INF/plugin.xml",
"dependencies" : [ {
"module" : "intellij.clouds.docker.compose"
} ]
},
"intellij.coffeescript" : {
"name" : "intellij.coffeescript",
"package" : "org.coffeescript",
"descriptor" : "plugins/coffeescript/coffeescript-core/resources/META-INF/plugin.xml"
},
"intellij.configurationScript" : {
"name" : "intellij.configurationScript",
"package" : "com.intellij.configurationScript",
"descriptor" : "community/plugins/configuration-script/resources/META-INF/plugin.xml"
},
"intellij.diagram.impl" : {
"name" : "intellij.diagram.impl",
"package" : "com.intellij.uml",
"descriptor" : "plugins/uml/resources/META-INF/plugin.xml"
},
"intellij.fileWatcher" : {
"name" : "intellij.fileWatcher",
"package" : "com.intellij.plugins.watcher",
"descriptor" : "plugins/fileWatcher/resources/META-INF/plugin.xml"
},
"intellij.freemarker" : {
"name" : "intellij.freemarker",
"package" : "com.intellij.freemarker",
"descriptor" : "plugins/freemarker/core/resources/META-INF/plugin.xml"
},
"intellij.go.ide" : {
"name" : "intellij.go.ide",
"package" : "com.goide.ide",
"descriptor" : "goland/intellij-go/ide/resources/META-INF/plugin.xml"
},
"intellij.go.plugin" : {
"name" : "intellij.go.plugin",
"package" : "com.goide",
"descriptor" : "goland/intellij-go/plugin/resources/META-INF/plugin.xml"
},
"intellij.go.sharedIndexes.bundled" : {
"name" : "intellij.go.sharedIndexes.bundled",
"package" : "com.goide.index.shared.bundled",
"descriptor" : "goland/intellij-go/go-shared-indexes-bundled/resources/META-INF/plugin.xml"
},
"intellij.go.template" : {
"name" : "intellij.go.template",
"package" : "com.goide.template",
"descriptor" : "goland/intellij-go/template/resources/META-INF/plugin.xml"
},
"intellij.gradle.ext" : {
"name" : "intellij.gradle.ext",
"package" : null,
"descriptor" : "plugins/gradle-ext/src/main/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.gradle.ext/profiler",
"package" : "org.jetbrains.idea.gradle.ext.profiler",
"descriptor" : "plugins/gradle-ext/src/main/resources/intellij.gradle.ext.profiler.xml",
"dependencies" : [ {
"module" : "intellij.profiler.ultimate"
} ]
} ]
},
"intellij.gradle.javaee" : {
"name" : "intellij.gradle.javaee",
"package" : "com.intellij.javaee.gradle",
"descriptor" : "plugins/javaee/gradle-integration/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.gradle.javaee/javaee",
"package" : "com.intellij.javaee.gradle.javaee",
"descriptor" : "plugins/javaee/gradle-integration/resources/intellij.gradle.javaee.javaee.xml",
"dependencies" : [ {
"plugin" : "com.intellij.javaee"
} ]
}, {
"name" : "intellij.gradle.javaee/javaee.web",
"package" : "com.intellij.javaee.gradle.javaeeWeb",
"descriptor" : "plugins/javaee/gradle-integration/resources/intellij.gradle.javaee.javaee.web.xml",
"dependencies" : [ {
"plugin" : "com.intellij.javaee.web"
} ]
} ],
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.gradle"
}, {
"plugin" : "com.intellij.javaee"
} ]
},
"intellij.grazie" : {
"name" : "intellij.grazie",
"package" : "com.intellij.grazie",
"descriptor" : "community/plugins/grazie/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.grazie.java",
"package" : "com.intellij.grazie.ide.language.java",
"descriptor" : "community/plugins/grazie/java/src/main/resources/intellij.grazie.java.xml",
"dependencies" : [ {
"plugin" : "com.intellij.java"
} ]
}, {
"name" : "intellij.grazie.json",
"package" : "com.intellij.grazie.ide.language.json",
"descriptor" : "community/plugins/grazie/json/src/main/resources/intellij.grazie.json.xml",
"dependencies" : [ {
"plugin" : "com.intellij.modules.json"
} ]
}, {
"name" : "intellij.grazie.markdown",
"package" : "com.intellij.grazie.ide.language.markdown",
"descriptor" : "community/plugins/grazie/markdown/src/main/resources/intellij.grazie.markdown.xml",
"dependencies" : [ {
"plugin" : "org.intellij.plugins.markdown"
} ]
}, {
"name" : "intellij.grazie.properties",
"package" : "com.intellij.grazie.ide.language.properties",
"descriptor" : "community/plugins/grazie/properties/src/main/resources/intellij.grazie.properties.xml",
"dependencies" : [ {
"plugin" : "com.intellij.properties"
} ]
}, {
"name" : "intellij.grazie.xml",
"package" : "com.intellij.grazie.ide.language.xml",
"descriptor" : "community/plugins/grazie/xml/main/resources/intellij.grazie.xml.xml",
"dependencies" : [ {
"plugin" : "com.intellij.modules.xml"
} ]
}, {
"name" : "intellij.grazie.yaml",
"package" : "com.intellij.grazie.ide.language.yaml",
"descriptor" : "community/plugins/grazie/yaml/main/resources/intellij.grazie.yaml.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.yaml"
} ]
} ]
},
"intellij.groovy" : {
"name" : "intellij.groovy",
"package" : "org.jetbrains.plugins.groovy",
"descriptor" : "community/plugins/groovy/src/META-INF/plugin.xml"
},
"intellij.grpc" : {
"name" : "intellij.grpc",
"package" : "com.intellij.grpc",
"descriptor" : "plugins/frameworks/grpc/resources/META-INF/plugin.xml"
},
"intellij.guice" : {
"name" : "intellij.guice",
"package" : "com.intellij.guice",
"descriptor" : "plugins/frameworks/Guice/resources/META-INF/plugin.xml"
},
"intellij.haml" : {
"name" : "intellij.haml",
"package" : "org.jetbrains.plugins.haml",
"descriptor" : "plugins/haml/resources/META-INF/plugin.xml"
},
"intellij.helidon" : {
"name" : "intellij.helidon",
"package" : "com.intellij.helidon",
"descriptor" : "plugins/frameworks/helidon/resources/META-INF/plugin.xml"
},
"intellij.jade" : {
"name" : "intellij.jade",
"package" : "com.jetbrains.plugins.jade",
"descriptor" : "plugins/Jade/resources/META-INF/plugin.xml"
},
"intellij.java.plugin" : {
"name" : "intellij.java.plugin",
"package" : null,
"descriptor" : "community/java/plugin/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.profiler.ultimate",
"package" : "com.intellij.profiler.ultimate",
"descriptor" : "plugins/profiler/ultimate/resources/intellij.profiler.ultimate.xml",
"dependencies" : [ {
"plugin" : "com.intellij.modules.profiler"
} ]
} ]
},
"intellij.javaFX.css" : {
"name" : "intellij.javaFX.css",
"package" : "org.jetbrains.plugins.javaFX",
"descriptor" : "plugins/javaFX-CSS/src/META-INF/plugin.xml"
},
"intellij.javaHttpClients" : {
"name" : "intellij.javaHttpClients",
"package" : "com.intellij.javahttp",
"descriptor" : "plugins/frameworks/java-http-clients/resources/META-INF/plugin.xml"
},
"intellij.javaee.app.servers.impl" : {
"name" : "intellij.javaee.app.servers.impl",
"package" : null,
"descriptor" : "plugins/javaee/core/javaee-app-server-integration/app-server-integration-impl/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.javaee.app.servers.impl/profiler",
"package" : "com.intellij.javaee.appServers.run.profiler",
"descriptor" : "plugins/javaee/core/javaee-app-server-integration/app-server-integration-impl/resources/intellij.javaee.app.servers.impl.profiler.xml",
"dependencies" : [ {
"module" : "intellij.profiler.ultimate"
} ]
} ]
},
"intellij.javaee.appServers.tomcat" : {
"name" : "intellij.javaee.appServers.tomcat",
"package" : null,
"descriptor" : "plugins/tomcat/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.javaee.appServers.tomcat/profiler",
"package" : "org.jetbrains.idea.tomcat.profiler",
"descriptor" : "plugins/tomcat/resources/intellij.javaee.appServers.tomcat.profiler.xml",
"dependencies" : [ {
"module" : "intellij.profiler.ultimate"
} ]
} ]
},
"intellij.javaee.beanValidation" : {
"name" : "intellij.javaee.beanValidation",
"package" : "com.intellij.beanValidation",
"descriptor" : "plugins/javaee/bean-validation/bv-core/resources/META-INF/plugin.xml"
},
"intellij.javaee.cdi" : {
"name" : "intellij.javaee.cdi",
"package" : "com.intellij.cdi",
"descriptor" : "plugins/javaee/cdi/cdi-core/resources/META-INF/plugin.xml"
},
"intellij.javaee.ejb.impl" : {
"name" : "intellij.javaee.ejb.impl",
"package" : "com.intellij.javaee.ejb",
"descriptor" : "plugins/javaee/ejb/ejb-impl/resources/META-INF/plugin.xml"
},
"intellij.javaee.jax.rs" : {
"name" : "intellij.javaee.jax.rs",
"package" : "com.intellij.ws.rest",
"descriptor" : "plugins/javaee/web-services/rs/resources/META-INF/plugin.xml"
},
"intellij.javaee.jax.ws" : {
"name" : "intellij.javaee.jax.ws",
"package" : "com.intellij.ws.xml",
"descriptor" : "plugins/javaee/web-services/ws/resources/META-INF/plugin.xml"
},
"intellij.javaee.jpa.impl" : {
"name" : "intellij.javaee.jpa.impl",
"package" : "com.intellij.jpa",
"descriptor" : "plugins/javaee/jpa/jpa-impl/resources/META-INF/plugin.xml"
},
"intellij.javaee.jsf" : {
"name" : "intellij.javaee.jsf",
"package" : "com.intellij.jsf",
"descriptor" : "plugins/javaee/jsf/jsf-core/src/META-INF/plugin.xml"
},
"intellij.javaee.web.impl" : {
"name" : "intellij.javaee.web.impl",
"package" : "com.intellij.javaee.web",
"descriptor" : "plugins/javaee/web/web-impl/resources/META-INF/plugin.xml"
},
"intellij.javascript.impl" : {
"name" : "intellij.javascript.impl",
"package" : null,
"descriptor" : "plugins/JavaScriptLanguage/src/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.javascript.impl/diagrams",
"package" : "com.intellij.lang.javascript.modules.diagram",
"descriptor" : "plugins/JavaScriptLanguage/src/intellij.javascript.impl.diagrams.xml",
"dependencies" : [ {
"plugin" : "com.intellij.diagram"
} ]
} ]
},
"intellij.jboss.arquillian" : {
"name" : "intellij.jboss.arquillian",
"package" : "com.intellij.plugins.jboss.arquillian",
"descriptor" : "plugins/frameworks/jboss/arquillian/arquillian-core/src/META-INF/plugin.xml"
},
"intellij.jboss.jbpm" : {
"name" : "intellij.jboss.jbpm",
"package" : "com.intellij.jboss.bpmn",
"descriptor" : "plugins/frameworks/jboss/bpmn/jbpm/resources/META-INF/plugin.xml"
},
"intellij.less" : {
"name" : "intellij.less",
"package" : "org.jetbrains.plugins.less",
"descriptor" : "plugins/less/resources/META-INF/plugin.xml"
},
"intellij.maven.ext" : {
"name" : "intellij.maven.ext",
"package" : null,
"descriptor" : "plugins/maven-ext/src/main/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.maven.ext/profiler",
"package" : "org.jetbrains.idea.maven.ext.profiler",
"descriptor" : "plugins/maven-ext/src/main/resources/intellij.maven.ext.profiler.xml",
"dependencies" : [ {
"module" : "intellij.profiler.ultimate"
} ]
} ]
},
"intellij.micronaut" : {
"name" : "intellij.micronaut",
"package" : "com.intellij.micronaut",
"descriptor" : "plugins/frameworks/micronaut/micronaut-core/resources/META-INF/plugin.xml"
},
"intellij.nodeJS.remoteInterpreter" : {
"name" : "intellij.nodeJS.remoteInterpreter",
"package" : "com.jetbrains.nodejs.remote",
"descriptor" : "plugins/NodeJS/node-remote-interpreter/resources/META-INF/plugin.xml"
},
"intellij.php.docker" : {
"name" : "intellij.php.docker",
"package" : "com.jetbrains.php.remote.docker",
"descriptor" : "phpstorm/phpstorm-docker/resources/META-INF/plugin.xml",
"dependencies" : [ {
"module" : "intellij.clouds.docker.remoteRun"
} ]
},
"intellij.phpspec" : {
"name" : "intellij.phpspec",
"package" : "com.jetbrains.php.phpspec",
"descriptor" : "phpstorm/phpspec/resources/META-INF/plugin.xml"
},
"intellij.play" : {
"name" : "intellij.play",
"package" : "com.intellij.play",
"descriptor" : "plugins/frameworks/play/resources/META-INF/plugin.xml"
},
"intellij.properties" : {
"name" : "intellij.properties",
"package" : "com.intellij.lang.properties",
"descriptor" : "community/plugins/properties/src/META-INF/plugin.xml"
},
"intellij.protoeditor" : {
"name" : "intellij.protoeditor",
"package" : "com.intellij.protobuf",
"descriptor" : "contrib/protobuf/resources/META-INF/plugin.xml"
},
"intellij.puppet" : {
"name" : "intellij.puppet",
"package" : "com.intellij.lang.puppet",
"descriptor" : "plugins/puppet/resources/META-INF/plugin.xml"
},
"intellij.quarkus" : {
"name" : "intellij.quarkus",
"package" : "com.intellij.quarkus",
"descriptor" : "plugins/frameworks/quarkus/quarkus-core/resources/META-INF/plugin.xml"
},
"intellij.reactivestreams.core" : {
"name" : "intellij.reactivestreams.core",
"package" : "com.intellij.reactivestreams",
"descriptor" : "plugins/frameworks/reactive/reactive-streams-core/resources/META-INF/plugin.xml"
},
"intellij.remoteRun" : {
"name" : "intellij.remoteRun",
"package" : "com.jetbrains.plugins.remotesdk",
"descriptor" : "plugins/remote-run/resources/META-INF/plugin.xml"
},
"intellij.ruby.plugin" : {
"name" : "intellij.ruby.plugin",
"package" : null,
"descriptor" : "ruby/pluginResources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.ruby.copyright",
"package" : "org.jetbrains.plugins.ruby.copyright",
"descriptor" : "ruby/ruby-copyright/resources/intellij.ruby.copyright.xml",
"dependencies" : [ {
"plugin" : "com.intellij.copyright"
} ]
}, {
"name" : "intellij.ruby.coverage",
"package" : "org.jetbrains.plugins.ruby.coverage",
"descriptor" : "ruby/coverage/ruby-coverage-common/resources/intellij.ruby.coverage.xml",
"dependencies" : [ {
"plugin" : "com.intellij.modules.coverage"
} ]
}, {
"name" : "intellij.ruby.database",
"package" : "org.jetbrains.plugins.ruby.rails.database.impl",
"descriptor" : "ruby/ruby-database/resources/intellij.ruby.database.xml",
"dependencies" : [ {
"plugin" : "com.intellij.modules.database"
} ]
}, {
"name" : "intellij.ruby.puppet.shared",
"package" : "com.intellij.lang.puppet.rubyShared",
"descriptor" : "ruby/ruby-puppet-shared/src/intellij.ruby.puppet.shared.xml"
}, {
"name" : "intellij.ruby.java",
"package" : "org.jetbrains.plugins.ruby.java",
"descriptor" : "ruby/ruby-java/src/intellij.ruby.java.xml",
"dependencies" : [ {
"plugin" : "com.intellij.java"
} ]
}, {
"name" : "intellij.ruby.puppet.java",
"package" : "com.intellij.lang.puppet.ruby.java.ide",
"descriptor" : "ruby/ruby-puppet-java/resources/intellij.ruby.puppet.java.xml",
"dependencies" : [ {
"plugin" : "com.intellij.java"
}, {
"plugin" : "com.intellij.lang.puppet"
}, {
"module" : "intellij.ruby.puppet.shared"
} ]
}, {
"name" : "intellij.ruby.cucumber",
"package" : "org.jetbrains.plugins.ruby.cucumber",
"descriptor" : "ruby/ruby-cucumber/resources/intellij.ruby.cucumber.xml",
"dependencies" : [ {
"plugin" : "gherkin"
} ]
}, {
"name" : "intellij.ruby.uml",
"package" : "com.intellij.diagram.ruby",
"descriptor" : "ruby/ruby-uml/resources/intellij.ruby.uml.xml",
"dependencies" : [ {
"plugin" : "com.intellij.diagram"
} ]
}, {
"name" : "intellij.ruby.wsl",
"package" : "com.intellij.wsl.remote.ruby",
"descriptor" : "ruby/ruby-wsl/resources/intellij.ruby.wsl.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.wsl"
}, {
"module" : "intellij.ruby.remoteInterpreter"
} ]
}, {
"name" : "intellij.ruby.remoteInterpreter",
"package" : "org.jetbrains.plugins.ruby.remote.impl",
"descriptor" : "ruby/remote-interpreter/resources/intellij.ruby.remoteInterpreter.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.remote-run"
} ]
}, {
"name" : "intellij.ruby.docker",
"package" : "com.intellij.docker.remote.ruby",
"descriptor" : "ruby/ruby-docker/resources/intellij.ruby.docker.xml",
"dependencies" : [ {
"plugin" : "Docker"
}, {
"module" : "intellij.ruby.remoteInterpreter"
}, {
"module" : "intellij.ruby.coverage"
} ]
}, {
"name" : "intellij.ruby.terminal",
"package" : "org.jetbrains.plugins.ruby.terminal",
"descriptor" : "ruby/ruby-terminal/resources/intellij.ruby.terminal.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.terminal"
} ]
}, {
"name" : "intellij.ruby.puppet",
"package" : "com.intellij.lang.puppet.ruby",
"descriptor" : "ruby/ruby-puppet/resources/intellij.ruby.puppet.xml",
"dependencies" : [ {
"plugin" : "com.intellij.lang.puppet"
}, {
"module" : "intellij.ruby.puppet.shared"
} ]
}, {
"name" : "intellij.ruby.performanceTesting",
"package" : "org.jetbrains.ruby.performanceTesting",
"descriptor" : "ruby/performanceTesting/src/intellij.ruby.performanceTesting.xml",
"dependencies" : [ {
"plugin" : "com.jetbrains.performancePlugin"
} ]
}, {
"name" : "intellij.ruby.intelliLang",
"package" : "org.jetbrains.plugins.ruby.ruby.intelliLang",
"descriptor" : "ruby/ruby-intelliLang/resources/intellij.ruby.intelliLang.xml",
"dependencies" : [ {
"plugin" : "org.intellij.intelliLang"
} ]
}, {
"name" : "intellij.ruby.featuresTrainer",
"package" : "org.jetbrains.ruby.ift",
"descriptor" : "contrib/ide-features-trainer/ruby-features-trainer/resources/intellij.ruby.featuresTrainer.xml",
"dependencies" : [ {
"plugin" : "training"
} ]
}, {
"name" : "intellij.ruby.haml",
"package" : "org.jetbrains.plugins.haml.ruby",
"descriptor" : "ruby/ruby-haml/resources/intellij.ruby.haml.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.haml"
} ]
}, {
"name" : "intellij.ruby.slim",
"package" : "org.jetbrains.plugins.slim.ruby",
"descriptor" : "ruby/ruby-slim/resources/intellij.ruby.slim.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.plugins.slim"
} ]
} ]
},
"intellij.safe.push" : {
"name" : "intellij.safe.push",
"package" : "com.intellij.safepush",
"descriptor" : "plugins/safe-push/resources/META-INF/plugin.xml"
},
"intellij.selenium" : {
"name" : "intellij.selenium",
"package" : "com.intellij.selenium",
"descriptor" : "plugins/frameworks/selenium/selenium/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.selenium.jvm",
"package" : "com.intellij.selenium.jvm",
"descriptor" : "plugins/frameworks/selenium/selenium-jvm/resources/intellij.selenium.jvm.xml",
"dependencies" : [ {
"plugin" : "com.intellij.java"
}, {
"plugin" : "com.intellij.modules.ultimate"
}, {
"plugin" : "com.intellij.modules.json"
}, {
"plugin" : "org.intellij.intelliLang"
}, {
"plugin" : "com.intellij.properties"
} ]
}, {
"name" : "intellij.selenium.docker",
"package" : "com.intellij.selenium.docker",
"descriptor" : "plugins/frameworks/selenium/selenium-docker/resources/intellij.selenium.docker.xml",
"dependencies" : [ {
"plugin" : "Docker"
} ]
}, {
"name" : "intellij.selenium.python",
"package" : "com.intellij.selenium.python",
"descriptor" : "plugins/frameworks/selenium/selenium-python/resources/intellij.selenium.python.xml"
} ]
},
"intellij.sh" : {
"name" : "intellij.sh",
"package" : "com.intellij.sh",
"descriptor" : "community/plugins/sh/resources/META-INF/plugin.xml"
},
"intellij.slim" : {
"name" : "intellij.slim",
"package" : "org.jetbrains.plugins.slim",
"descriptor" : "plugins/slim-lang/resources/META-INF/plugin.xml"
},
"intellij.space" : {
"name" : "intellij.space",
"package" : "com.intellij.space",
"descriptor" : "plugins/space/src/main/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.space.kotlin",
"package" : "com.intellij.space.kotlin",
"descriptor" : "plugins/space/kotlin/resources/intellij.space.kotlin.xml",
"dependencies" : [ {
"plugin" : "org.jetbrains.kotlin"
} ]
}, {
"name" : "intellij.space.index",
"package" : "com.intellij.space.index",
"descriptor" : "plugins/space/index/resources/intellij.space.index.xml",
"dependencies" : [ {
"plugin" : "intellij.indexing.shared"
} ]
} ]
},
"intellij.spring.batch" : {
"name" : "intellij.spring.batch",
"package" : "com.intellij.spring.batch",
"descriptor" : "plugins/spring/spring-batch/resources/META-INF/plugin.xml"
},
"intellij.spring.boot.core" : {
"name" : "intellij.spring.boot.core",
"package" : "com.intellij.spring.boot",
"descriptor" : "plugins/spring/spring-boot/spring-boot-core/resources/META-INF/plugin.xml"
},
"intellij.spring.core" : {
"name" : "intellij.spring.core",
"package" : "com.intellij.spring",
"descriptor" : "plugins/spring/spring-framework/spring-core/resources/META-INF/plugin.xml"
},
"intellij.spring.data" : {
"name" : "intellij.spring.data",
"package" : "com.intellij.spring.data",
"descriptor" : "plugins/spring/spring-data/resources/META-INF/plugin.xml"
},
"intellij.spring.integration.core" : {
"name" : "intellij.spring.integration.core",
"package" : "com.intellij.spring.integration",
"descriptor" : "plugins/spring/spring-integration/spring-integration-core/resources/META-INF/plugin.xml"
},
"intellij.spring.mvc.impl" : {
"name" : "intellij.spring.mvc.impl",
"package" : "com.intellij.spring.mvc",
"descriptor" : "plugins/spring/spring-mvc-support/spring-mvc/resources/META-INF/plugin.xml"
},
"intellij.spring.security" : {
"name" : "intellij.spring.security",
"package" : "com.intellij.spring.security",
"descriptor" : "plugins/spring/spring-security/resources/META-INF/plugin.xml"
},
"intellij.spring.webflow" : {
"name" : "intellij.spring.webflow",
"package" : "com.intellij.spring.webflow",
"descriptor" : "plugins/spring/spring-webflow/resources/META-INF/plugin.xml"
},
"intellij.spring.websocket" : {
"name" : "intellij.spring.websocket",
"package" : "com.intellij.spring.websocket",
"descriptor" : "plugins/spring/spring-websocket/resources/META-INF/plugin.xml"
},
"intellij.struts2" : {
"name" : "intellij.struts2",
"package" : "com.intellij.struts2",
"descriptor" : "contrib/struts2/plugin/resources/META-INF/plugin.xml"
},
"intellij.swagger" : {
"name" : "intellij.swagger",
"package" : "com.intellij.swagger",
"descriptor" : "plugins/frameworks/swagger/swagger-core/resources/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.swagger/endpoints",
"package" : "com.intellij.swagger.providers.endpoints",
"descriptor" : "plugins/frameworks/swagger/swagger-core/resources/intellij.swagger.endpoints.xml",
"dependencies" : [ {
"plugin" : "com.intellij.microservices.ui"
} ]
} ]
},
"intellij.thymeleaf" : {
"name" : "intellij.thymeleaf",
"package" : "com.intellij.thymeleaf",
"descriptor" : "plugins/frameworks/thymeleaf/thymeleaf-core/resources/META-INF/plugin.xml"
},
"intellij.vcs.git" : {
"name" : "intellij.vcs.git",
"package" : "git4idea",
"descriptor" : "community/plugins/git4idea/resources/META-INF/plugin.xml"
},
"intellij.vcs.github" : {
"name" : "intellij.vcs.github",
"package" : "org.jetbrains.plugins.github",
"descriptor" : "community/plugins/github/resources/META-INF/plugin.xml"
},
"intellij.vuejs" : {
"name" : "intellij.vuejs",
"package" : "org.jetbrains.vuejs",
"descriptor" : "contrib/vuejs/resources/META-INF/plugin.xml"
},
"intellij.w3validators" : {
"name" : "intellij.w3validators",
"package" : "org.jetbrains.w3validators",
"descriptor" : "plugins/w3validators/src/META-INF/plugin.xml"
},
"intellij.wsl" : {
"name" : "intellij.wsl",
"package" : "com.intellij.wsl",
"descriptor" : "plugins/WSL/resources/META-INF/plugin.xml"
},
"intellij.wsl.fs.helper" : {
"name" : "intellij.wsl.fs.helper",
"package" : "com.intellij.wsl.fs",
"descriptor" : "plugins/wsl-file-system-helper/resources/META-INF/plugin.xml"
},
"intellij.yaml" : {
"name" : "intellij.yaml",
"package" : "org.jetbrains.yaml",
"descriptor" : "community/plugins/yaml/resources/META-INF/plugin.xml",
"dependencies" : [ {
"plugin" : "com.intellij.modules.lang"
} ]
}
}
@endjson

58
docs/plugin-model.puml Normal file
View File

@@ -0,0 +1,58 @@
@startuml
!include jb-plantuml-theme.puml
' https://www.augmentedmind.de/2021/01/17/plantuml-layout-tutorial-styles/
component "[[https://github.com/JetBrains/intellij-community/blob/master/platform/core-impl/src/com/intellij/ide/plugins/readme.md plugin]]" as P
folder content as PluginContent {
[module name="…" 0…N] as M
[module name="…" N] as M_N
M .. M_N : a sibling cannot access classes from each other\n unless specified as a dependency
note right of M : ""content"" is not allowed and is not supported for module.
folder dependencies as ModuleDependencies {
(module name="…" 0…N) as MDM
(plugin id="…" 0…N) as MDP
note right of MDM
The dependency is specified in a module descriptor itself in a new model,
not where the module is referenced.
end note
' force PlantUML to place `module` below of `plugin`
MDM -[hidden]d- MDP
}
note bottom of ModuleDependencies
Module dependency is always optional.
If the module depends on an unavailable plugin, it will not be loaded.
end note
M .> P : a module can access classes from a containing plugin
M -down- ModuleDependencies
}
note top of PluginContent
Every plugin is a module, but not every module is a plugin.
A plugin is a group of related modules and for now,
it is the only way to distribute modules.
end note
folder dependencies as PluginDependencies {
(module name="…" 0…N) as PDM
(plugin id="…" 0…N) as PDP
PDM -[hidden]d- PDP
}
note top of PluginDependencies : Same as for ""module""
P -down- PluginContent
P -down- PluginDependencies
@enduml

View File

@@ -4,7 +4,7 @@ Currently, IJ IDEA module is not reflected in any way in a plugin — only as pa
It is going to be changed — IJ Module will be integral part of plugin subsystem.
Plugin descriptor will reference all modules that forms its content, and plugin descriptor itself it is a specific form of module descriptor.
Every plugin it is a module, but not every module it is a plugin.
Every plugin is a module, but not every module is a plugin.
Term "optional descriptor" in a new format is equivalent to module dependency of plugin.
## The `package` attribute
@@ -22,7 +22,7 @@ The `content` element determines content of the plugin. Plugin consists of modul
```xml
<content>
<module name="intellij.clouds.docker.file" package="com.intellij.docker.dockerFile"/>
<module name="intellij.clouds.docker.file" />
</content>
```
@@ -34,7 +34,6 @@ The `content` element determines content of the plugin. Plugin consists of modul
| Attribute | Type | Use | Description |
|-----------|--------|----------|-------------------------------------------------------------------------------------------------------------------------------------------|
| name | string | required | The name of the module. |
| package | string | required | The package of the module. Duplicates the `package` specified in the referenced module, but for now it is required for technical reasons. |
There is an important difference between content specified for a plugin and for a module.
* For a plugin, the referenced module _is not added_ to classpath but just forms plugin content. Still, if some another plugin depends on this plugin, it can use classloader of that module. For time being, included into plugin module, doesnt have own classloader, but it will be changed in the future and explicit dependency on plugins module must be added if needed.
@@ -54,39 +53,25 @@ The `dependencies` element determines dependencies of the module.
| module | 0 | unbounded | A module upon which a dependency should be added. |
| plugin | 0 | unbounded | A plugin upon which a dependency should be added. |
### The `dependencies.plugin` element
| Attribute | Type | Use | Description |
|-----------|--------|----------|-----------------------|
| id | string | required | The id of the plugin. |
Not used for now and not supported. [Marketplace](https://github.com/JetBrains/intellij-plugin-verifier/tree/master/intellij-plugin-structure) is not yet ready to support a new format.
Old format
```xml
<depends>Docker</depends>
```
is still used.
### The `dependencies.module` element
| Attribute | Type | Use | Description |
|-----------|--------|----------|-------------------------|
| name | string | required | The name of the module. |
The module must have descriptor file with the same name as module, e.g. for module `intellij.clouds.docker.compose` must be a descriptor file `intellij.clouds.docker.compose.xml` in the module root.
The module must have descriptor file with the same name as module, e.g., for module `intellij.clouds.docker.compose` must be a descriptor file `intellij.clouds.docker.compose.xml` in the module root.
Module dependency is always optional. If module depends on an unavailable plugin, it will be not loaded.
Module dependency is always optional. If the module depends on an unavailable module, it will not be loaded.
In this meaning for now module dependency makes sense only for plugins, but not for modules. It will be changed in the future, when all plugins will act as a module.
The module added to classpath of dependent module but dependency itself is not able to access the dependent module (as it was earlier). Currently, it is packaged into the same JAR, but packaging it is implementation details that are hidden and maybe changed in any moment or maybe different in a different execution environment. The same classloader configuration is guaranteed, but not packaging, and independence from packaging it is a one of the goal of a new descriptor format.
In the old plugin descriptor format:
* tag `depends` with `config-file` it is `dependency.module`.
* tag `depends` without `optional` it is `dependency.plugin`.
### The `dependencies.plugin` element
| Attribute | Type | Use | Description |
|-----------|--------|----------|-----------------------|
| id | string | required | The id of the plugin. |
Note: for now you also must specify dependency in an old format.
The `dependencies.plugin` element determines dependency on a plugin.
## InternalIgnoreDependencyViolation

7
docs/readme.md Normal file
View File

@@ -0,0 +1,7 @@
IntelliJ Platform SDK [docs](https://plugins.jetbrains.com/docs/intellij/) sources located on [GitHub](https://github.com/JetBrains/intellij-sdk-docs).
This directory contains activity or model diagrams sources, design docs.
### How to write diagram in PlantUML
Install [PlantUML integration](https://plugins.jetbrains.com/plugin/7017-plantuml-integration/) plugin to add PlantUML support to IntelliJ IDEA.
See [PlantUML](https://plantuml.com) docs.

View File

@@ -14,29 +14,29 @@
~ limitations under the License.
-->
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="/META-INF/PlatformLangPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/PlatformLangPlugin.xml"/>
<module value="com.intellij.modules.all"/>
<module value="com.intellij.modules.jsp.base"/>
<xi:include href="/META-INF/RemoteServers.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/META-INF/RemoteServers.xml">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/DesignerCorePlugin.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/META-INF/DesignerCorePlugin.xml">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/XmlPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/JsonPlugin.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/META-INF/XmlPlugin.xml"/>
<xi:include href="intellij.json.xml">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/RegExpPlugin.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/META-INF/RegExpPlugin.xml">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/SpellCheckerPlugin.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/META-INF/SpellCheckerPlugin.xml">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/structuralsearch.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/META-INF/structuralsearch.xml">
<xi:fallback/>
</xi:include>

View File

@@ -3,6 +3,7 @@ package com.intellij.facet.mock;
import com.intellij.framework.detection.FacetBasedFrameworkDetector;
import com.intellij.framework.detection.FileContentPattern;
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.roots.ModifiableRootModel;
@@ -20,6 +21,7 @@ import java.util.List;
import static com.intellij.patterns.StandardPatterns.string;
@InternalIgnoreDependencyViolation
public final class MockFacetDetector extends FacetBasedFrameworkDetector<MockFacet, MockFacetConfiguration> {
public static final String ROOT_TAG_NAME = "root";
public static final String ROOT_TAG = "<" + ROOT_TAG_NAME + "/>";

View File

@@ -6,6 +6,7 @@ import com.intellij.facet.FacetConfiguration;
import com.intellij.facet.FacetType;
import com.intellij.framework.detection.FacetBasedFrameworkDetector;
import com.intellij.framework.detection.FileContentPattern;
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.util.Pair;
@@ -20,7 +21,8 @@ import java.util.Set;
import static com.intellij.patterns.StandardPatterns.string;
public class MockSubFacetDetector extends FacetBasedFrameworkDetector<Facet, MockFacetConfiguration> {
@InternalIgnoreDependencyViolation
public final class MockSubFacetDetector extends FacetBasedFrameworkDetector<Facet, MockFacetConfiguration> {
public MockSubFacetDetector() {
super("mock-sub-facet-detector");
}

View File

@@ -7,6 +7,7 @@ import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.codeInsight.daemon.impl.MarkerType;
import com.intellij.ide.plugins.DynamicPluginsTestUtil;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.impl.LibraryScopeCache;
import com.intellij.openapi.util.Disposer;
@@ -39,7 +40,6 @@ import java.util.Map;
* For this test, we will enlarge the use scope to include libraries.
*/
public class LibraryUseSearchUsingScopeEnlargerTest extends JavaCodeInsightFixtureTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -103,7 +103,8 @@ public class LibraryUseSearchUsingScopeEnlargerTest extends JavaCodeInsightFixtu
return false;
}
public static class LibraryUseScopeEnlarger extends UseScopeEnlarger {
@InternalIgnoreDependencyViolation
final static class LibraryUseScopeEnlarger extends UseScopeEnlarger {
@Override
public @Nullable SearchScope getAdditionalUseScope(@NotNull PsiElement element) {
return LibraryScopeCache.getInstance(element.getProject()).getLibrariesOnlyScope();

View File

@@ -2,6 +2,7 @@
package com.intellij.util.indexing
import com.intellij.ide.plugins.loadExtensionWithText
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.util.Disposer
@@ -50,9 +51,10 @@ class BrokenPluginIndexingTest : JavaCodeInsightFixtureTestCase() {
}
}
class BrokenPluginException : RuntimeException()
internal class BrokenPluginException : RuntimeException()
class BrokenFileBasedIndexExtension : ScalarIndexExtension<Int>() {
@InternalIgnoreDependencyViolation
internal class BrokenFileBasedIndexExtension : ScalarIndexExtension<Int>() {
override fun getIndexer() = DataIndexer<Int, Void, FileContent> { throw BrokenPluginException() }
override fun getName() = INDEX_ID
override fun getKeyDescriptor() = EnumeratorIntegerDescriptor.INSTANCE!!

View File

@@ -1,12 +1,14 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.indexing
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.io.EnumeratorIntegerDescriptor
import com.intellij.util.io.KeyDescriptor
import java.util.concurrent.atomic.AtomicInteger
class CountingFileBasedIndexExtension : ScalarIndexExtension<Int>() {
@InternalIgnoreDependencyViolation
internal class CountingFileBasedIndexExtension : ScalarIndexExtension<Int>() {
override fun getIndexer(): DataIndexer<Int, Void, FileContent> {
return DataIndexer {
COUNTER.incrementAndGet()

View File

@@ -3,6 +3,7 @@ package com.intellij.util.indexing
import com.intellij.ide.plugins.loadExtensionWithText
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.psi.stubs.StubIndexKey
@@ -43,6 +44,7 @@ class IndexInfrastructureExtensionTest : LightJavaCodeInsightFixtureTestCase() {
const val testInfraExtensionFile = "_test_extension"
@InternalIgnoreDependencyViolation
class TestIndexInfrastructureExtension : FileBasedIndexInfrastructureExtension {
override fun createFileIndexingStatusProcessor(project: Project): FileBasedIndexInfrastructureExtension.FileIndexingStatusProcessor? =
null

View File

@@ -13,7 +13,7 @@
<depends>com.intellij.modules.java-capable</depends>
<depends optional="true" config-file="java-copyright.xml">com.intellij.copyright</depends>
<xi:include href="/META-INF/JavaPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/JavaPlugin.xml"/>
<!-- todo: these files are present in Java plugin for IDEA Ultimate only, they should be extract to a different plugin.xml -->
<xi:include href="intellij.java.duplicates.analysis.xml">
@@ -26,7 +26,10 @@
<xi:fallback/>
</xi:include>
<depends optional="true" config-file="profiler-java.xml">com.intellij.modules.profiler</depends>
<content>
<module name="intellij.profiler.ultimate"/>
</content>
<depends optional="true" config-file="intellij.java.structuralSearch.xml">com.intellij.modules.structuralsearch</depends>
<depends optional="true" config-file="intellij.java.remoteServers.impl.xml">com.intellij.modules.remoteServers</depends>
<depends optional="true" config-file="intellij.java.featuresTrainer.xml">training</depends>

View File

@@ -52,7 +52,10 @@ public abstract class LightFixtureCompletionTestCase extends LightJavaCodeInsigh
protected void tearDown() throws Exception {
try {
myItems = null;
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = CodeInsightSettings.FIRST_LETTER;
CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance();
if (codeInsightSettings != null) {
codeInsightSettings.setCompletionCaseSensitive(CodeInsightSettings.FIRST_LETTER);
}
}
catch (Throwable e) {
addSuppressedException(e);

View File

@@ -1,5 +1,4 @@
<idea-plugin>
<module value="com.intellij.modules.json"/>
<actions>
@@ -191,5 +190,4 @@
<extensionPoint qualifiedName="com.intellij.json.catalog.exclusion" interface="com.jetbrains.jsonSchema.remote.JsonSchemaCatalogExclusion" dynamic="true"/>
<extensionPoint qualifiedName="com.intellij.json.customStructureViewFactory" interface="com.intellij.json.structureView.JsonCustomStructureViewFactory" dynamic="true"/>
</extensionPoints>
</idea-plugin>

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.intellij.build
import com.intellij.openapi.util.io.FileUtil
@@ -19,7 +19,7 @@ final class CommunityRepositoryModules {
static List<PluginLayout> COMMUNITY_REPOSITORY_PLUGINS = [
plugin("intellij.ant") {
mainJarName = "antIntegration.jar"
withModule("intellij.ant.jps")
withModule("intellij.ant.jps", "ant-jps.jar")
},
plugin("intellij.laf.macos") {
bundlingRestrictions.supportedOs = [OsFamily.MACOS]
@@ -72,12 +72,12 @@ final class CommunityRepositoryModules {
},
plugin("intellij.maven") {
withModule("intellij.maven.jps")
withModule("intellij.maven.server")
withModule("intellij.maven.server.m2.impl")
withModule("intellij.maven.server.m3.common")
withModule("intellij.maven.server.m30.impl")
withModule("intellij.maven.server.m3.impl")
withModule("intellij.maven.server.m36.impl")
withModule("intellij.maven.server", "maven-server-api.jar")
withModule("intellij.maven.server.m2.impl", "maven2-server.jar")
withModule("intellij.maven.server.m3.common", "maven3-server-common.jar")
withModule("intellij.maven.server.m30.impl", "maven30-server.jar")
withModule("intellij.maven.server.m3.impl", "maven3-server.jar")
withModule("intellij.maven.server.m36.impl", "maven36-server.jar")
withModule("intellij.maven.errorProne.compiler")
withModule("intellij.maven.artifactResolver.m2", "artifact-resolver-m2.jar")
withModule("intellij.maven.artifactResolver.common", "artifact-resolver-m2.jar")
@@ -103,8 +103,8 @@ final class CommunityRepositoryModules {
},
plugin("intellij.gradle") {
withModule("intellij.gradle.common")
withModule("intellij.gradle.toolingExtension")
withModule("intellij.gradle.toolingExtension.impl")
withModule("intellij.gradle.toolingExtension", "gradle-tooling-extension-api.jar")
withModule("intellij.gradle.toolingExtension.impl", "gradle-tooling-extension-impl.jar")
withModule("intellij.gradle.toolingProxy")
withProjectLibrary("Gradle")
},
@@ -205,10 +205,10 @@ final class CommunityRepositoryModules {
withModule("intellij.errorProne.jps", "jps/errorProne-jps.jar")
},
plugin("intellij.cucumber.java") {
withModule("intellij.cucumber.jvmFormatter")
withModule("intellij.cucumber.jvmFormatter3")
withModule("intellij.cucumber.jvmFormatter4")
withModule("intellij.cucumber.jvmFormatter5")
withModule("intellij.cucumber.jvmFormatter", "cucumber-jvmFormatter.jar")
withModule("intellij.cucumber.jvmFormatter3", "cucumber-jvmFormatter3.jar")
withModule("intellij.cucumber.jvmFormatter4", "cucumber-jvmFormatter4.jar")
withModule("intellij.cucumber.jvmFormatter5", "cucumber-jvmFormatter5.jar")
},
plugin("intellij.cucumber.groovy") {
},
@@ -407,9 +407,9 @@ final class CommunityRepositoryModules {
withModule("intellij.groovy.psi", mainJarName)
withModule("intellij.groovy.structuralSearch", mainJarName)
excludeFromModule("intellij.groovy.psi", "standardDsls/**")
withModule("intellij.groovy.jps")
withModule("intellij.groovy.rt")
withModule("intellij.groovy.constants.rt")
withModule("intellij.groovy.jps", "groovy-jps.jar")
withModule("intellij.groovy.rt", "groovy-rt.jar")
withModule("intellij.groovy.constants.rt", "groovy-constants-rt.jar")
withResource("groovy-psi/resources/standardDsls", "lib/standardDsls")
withResource("hotswap/gragent.jar", "lib/agent")
withResource("groovy-psi/resources/conf", "lib")

View File

@@ -23,7 +23,7 @@ class BaseLayoutSpec {
}
/**
* Register an additional module to be included into the plugin distribution into a separate JAR file. Module-level libraries from
* Register an additional module to be included into the plugin distribution. Module-level libraries from
* {@code moduleName} with scopes 'Compile' and 'Runtime' will be also copied to the 'lib' directory of the plugin.
*/
void withModule(String moduleName) {

View File

@@ -3,6 +3,7 @@ package org.jetbrains.intellij.build.impl
import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.containers.MultiMap
import com.jetbrains.plugin.blockmap.core.BlockMap
@@ -882,6 +883,7 @@ final class DistributionJARsBuilder {
modulePatches(List.of(moduleName))
module(moduleName) {
ant.exclude(name: "**/icon-robots.txt")
ant.exclude(name: ".unmodified")
for (String exclude in layout.moduleExcludes.get(moduleName)) {
//noinspection GrUnresolvedAccess
@@ -935,11 +937,11 @@ final class DistributionJARsBuilder {
}
if (layoutSpec.copyFiles) {
for (ModuleResourceData resourceData in layout.resourcePaths) {
String path = FileUtil.toSystemIndependentName(new File(basePath(buildContext, resourceData.moduleName),
resourceData.resourcePath).absolutePath)
String path = FileUtilRt.toSystemIndependentName(new File(basePath(buildContext, resourceData.moduleName),
resourceData.resourcePath).absolutePath)
if (resourceData.packToZip) {
zip(resourceData.relativeOutputPath) {
if (Files.isRegularFile(Paths.get(path))) {
if (Files.isRegularFile(Path.of(path))) {
ant.fileset(file: path)
}
else {
@@ -949,7 +951,7 @@ final class DistributionJARsBuilder {
}
else {
dir(resourceData.relativeOutputPath) {
if (Files.isRegularFile(Paths.get(path))) {
if (Files.isRegularFile(Path.of(path))) {
ant.fileset(file: path)
}
else {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.intellij.build.impl
import com.intellij.openapi.util.Pair
@@ -89,7 +89,18 @@ final class PluginLayout extends BaseLayout {
mainJarName = "${convertModuleNameToFileName(layout.mainModule)}.jar"
}
/**
@Override
void withModule(String moduleName) {
if (moduleName.endsWith(".jps") || moduleName.endsWith(".rt")) {
// must be in a separate JAR
super.withModule(moduleName)
}
else {
layout.moduleJars.putValue(mainJarName, moduleName)
}
}
/**
* Custom name of the directory (under 'plugins' directory) where the plugin should be placed. By default the main module name is used
* (with stripped {@code intellij} prefix and dots replaced by dashes).
* <strong>Don't set this property for new plugins</strong>; it is temporary added to keep layout of old plugins unchanged.

View File

@@ -1,8 +1,9 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.messages.impl;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.openapi.extensions.ExtensionNotApplicableException;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.ArrayUtilRt;
@@ -74,7 +75,7 @@ class CompositeMessageBus extends MessageBusImpl implements MessageBusEx {
}
@Override
final <L> @NotNull MessagePublisher<L> createPublisher(@NotNull Topic<L> topic, @NotNull BroadcastDirection direction) {
final @NotNull <L> MessagePublisher<L> createPublisher(@NotNull Topic<L> topic, @NotNull BroadcastDirection direction) {
if (direction == BroadcastDirection.TO_PARENT) {
return new ToParentMessagePublisher<>(topic, this);
}
@@ -168,11 +169,11 @@ class CompositeMessageBus extends MessageBusImpl implements MessageBusEx {
}
// use linked hash map for repeatable results
Map<PluginId, List<L>> listenerMap = new LinkedHashMap<>();
Map<PluginDescriptor, List<L>> listenerMap = new LinkedHashMap<>();
for (ListenerDescriptor listenerDescriptor : listenerDescriptors) {
try {
//noinspection unchecked
listenerMap.computeIfAbsent(listenerDescriptor.pluginDescriptor.getPluginId(), __ -> new ArrayList<>())
listenerMap.computeIfAbsent(listenerDescriptor.pluginDescriptor, __ -> new ArrayList<>())
.add((L)owner.createListener(listenerDescriptor));
}
catch (ExtensionNotApplicableException ignore) {
@@ -252,9 +253,9 @@ class CompositeMessageBus extends MessageBusImpl implements MessageBusEx {
}
@Override
public final void unsubscribeLazyListeners(@NotNull PluginId pluginId, @NotNull List<ListenerDescriptor> listenerDescriptors) {
public final void unsubscribeLazyListeners(@NotNull IdeaPluginDescriptor module, @NotNull List<ListenerDescriptor> listenerDescriptors) {
topicClassToListenerDescriptor.values().removeIf(descriptors -> {
if (descriptors.removeIf(descriptor -> descriptor.pluginDescriptor.getPluginId().equals(pluginId))) {
if (descriptors.removeIf(descriptor -> descriptor.pluginDescriptor == module)) {
return descriptors.isEmpty();
}
return false;
@@ -279,7 +280,7 @@ class CompositeMessageBus extends MessageBusImpl implements MessageBusEx {
//noinspection unchecked
DescriptorBasedMessageBusConnection<Object> connection = (DescriptorBasedMessageBusConnection<Object>)holder;
if (!pluginId.equals(connection.pluginId)) {
if (module != connection.module) {
continue;
}
@@ -299,7 +300,7 @@ class CompositeMessageBus extends MessageBusImpl implements MessageBusEx {
if (newSubscribers == null) {
newSubscribers = new ArrayList<>();
}
newSubscribers.add(new DescriptorBasedMessageBusConnection<>(pluginId, connection.topic, newHandlers));
newSubscribers.add(new DescriptorBasedMessageBusConnection<>(module, connection.topic, newHandlers));
}
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.messages.impl;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.util.messages.Topic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,14 +13,14 @@ import java.util.Set;
import java.util.function.Predicate;
final class DescriptorBasedMessageBusConnection<L> implements MessageBusImpl.MessageHandlerHolder {
final @NotNull PluginId pluginId;
final @NotNull PluginDescriptor module;
final @NotNull Topic<L> topic;
final @NotNull List<? extends L> handlers;
DescriptorBasedMessageBusConnection(@NotNull PluginId pluginId,
DescriptorBasedMessageBusConnection(@NotNull PluginDescriptor module,
@NotNull Topic<L> topic,
@NotNull List<? extends L> handlers) {
this.pluginId = pluginId;
this.module = module;
this.topic = topic;
this.handlers = handlers;
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.messages.impl;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.util.messages.ListenerDescriptor;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.ApiStatus;
@@ -16,7 +16,7 @@ import java.util.function.Predicate;
public interface MessageBusEx extends MessageBus {
void clearPublisherCache();
void unsubscribeLazyListeners(@NotNull PluginId pluginId, @NotNull List<ListenerDescriptor> listenerDescriptors);
void unsubscribeLazyListeners(@NotNull IdeaPluginDescriptor module, @NotNull List<ListenerDescriptor> listenerDescriptors);
/**
* Must be called only on a root bus.

View File

@@ -25,9 +25,4 @@ final class CachingSemiGraph<Node> implements InboundSemiGraph<Node> {
List<Node> inNodes = in.get(n);
return inNodes == null ? Collections.emptyIterator() : inNodes.iterator();
}
public @NotNull List<Node> getInList(@NotNull Node n) {
List<Node> inNodes = in.get(n);
return inNodes == null ? Collections.emptyList() : inNodes;
}
}

View File

@@ -3,16 +3,18 @@ package com.intellij.ide.plugins;
import com.intellij.openapi.extensions.PluginId;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Set;
@ApiStatus.Internal
public final class ClassLoaderConfigurationData {
static final boolean SEPARATE_CLASSLOADER_FOR_SUB = Boolean.parseBoolean(System.getProperty("idea.classloader.per.descriptor", "true"));
private static final Set<PluginId> SEPARATE_CLASSLOADER_FOR_SUB_ONLY = new ReferenceOpenHashSet<>();
private static final Set<PluginId> SEPARATE_CLASSLOADER_FOR_SUB_EXCLUDE = ReferenceOpenHashSet.of(
private ClassLoaderConfigurationData() {
}
static final Set<PluginId> SEPARATE_CLASSLOADER_FOR_SUB_EXCLUDE = new ReferenceOpenHashSet<>(new PluginId[]{
PluginId.getId("org.jetbrains.kotlin"),
PluginId.getId("com.intellij.java"),
PluginId.getId("com.intellij.spring.batch"),
@@ -27,33 +29,10 @@ public final class ClassLoaderConfigurationData {
PluginId.getId("com.intellij.spring.data"),
PluginId.getId("com.intellij.spring.boot.run.tests"),
PluginId.getId("com.intellij.spring.boot"),
PluginId.getId("com.intellij.spring")
);
static {
String property = System.getProperty("idea.classloader.per.descriptor.only");
String[] pluginIds = property == null ?
new String[]{
"org.jetbrains.plugins.ruby",
"PythonCore",
"com.jetbrains.rubymine.customization",
"JavaScript",
"Docker",
"com.intellij.diagram"
} :
property.split(",");
for (String idString : pluginIds) {
SEPARATE_CLASSLOADER_FOR_SUB_ONLY.add(PluginId.getId(idString));
}
}
PluginId.getId("com.intellij.spring"),
});
public static boolean isClassloaderPerDescriptorEnabled(@NotNull PluginId pluginId, @Nullable String packagePrefix) {
if (!SEPARATE_CLASSLOADER_FOR_SUB || SEPARATE_CLASSLOADER_FOR_SUB_EXCLUDE.contains(pluginId)) {
return false;
}
return packagePrefix != null ||
SEPARATE_CLASSLOADER_FOR_SUB_ONLY.isEmpty() ||
SEPARATE_CLASSLOADER_FOR_SUB_ONLY.contains(pluginId);
return packagePrefix != null && !SEPARATE_CLASSLOADER_FOR_SUB_EXCLUDE.contains(pluginId);
}
}

View File

@@ -1,14 +1,12 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty")
package com.intellij.ide.plugins
import com.intellij.diagnostic.PluginException
import com.intellij.ide.plugins.cl.PluginClassLoader
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.util.lang.ClassPath
import com.intellij.util.lang.UrlClassLoader
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import org.jetbrains.annotations.ApiStatus
import java.io.IOException
import java.lang.invoke.MethodHandles
@@ -20,20 +18,17 @@ import java.util.*
@ApiStatus.Internal
class ClassLoaderConfigurator(
private val usePluginClassLoader: Boolean /* grab classes from platform loader only if nothing is found in any of plugin dependencies */,
private val coreLoader: ClassLoader,
val idMap: Map<PluginId, IdeaPluginDescriptorImpl>,
private val additionalLayoutMap: Map<String, Array<String>>) {
val pluginSet: PluginSet,
private val coreLoader: ClassLoader = ClassLoaderConfigurator::class.java.classLoader,
private val usePluginClassLoader: Boolean = true, /* grab classes from platform loader only if nothing is found in any of plugin dependencies */
) {
private var javaDep: Optional<IdeaPluginDescriptorImpl>? = null
// temporary set to produce arrays (avoid allocation for each plugin)
// set to remove duplicated classloaders
private val loaders = LinkedHashSet<ClassLoader>()
// temporary list to produce arrays (avoid allocation for each plugin)
private val packagePrefixes = ArrayList<String>()
private val hasAllModules = idMap.containsKey(PluginManagerCore.ALL_MODULES_MARKER)
private val hasAllModules = pluginSet.isPluginEnabled(PluginManagerCore.ALL_MODULES_MARKER)
private val urlClassLoaderBuilder = UrlClassLoader.build().useCache()
// todo for dynamic reload this guard doesn't contain all used plugin prefixes
@@ -56,252 +51,176 @@ class ClassLoaderConfigurator(
}
}
fun configureDependenciesIfNeeded(mainToSub: Map<IdeaPluginDescriptorImpl, List<IdeaPluginDescriptorImpl>>,
fun configureDependenciesIfNeeded(mainToModule: Map<IdeaPluginDescriptorImpl, List<IdeaPluginDescriptorImpl>>,
dependencyPlugin: IdeaPluginDescriptorImpl) {
for ((mainDependent, value) in mainToSub) {
for ((mainDependent, modules) in mainToModule) {
val mainDependentClassLoader = mainDependent.classLoader as PluginClassLoader
if (isClassloaderPerDescriptorEnabled(mainDependent)) {
for (dependency in mainDependent.pluginDependencies) {
urlClassLoaderBuilder.files(mainDependentClassLoader.files)
for (subDescriptor in value) {
if (subDescriptor === dependency.subDescriptor) {
configureSubPlugin(dependency, mainDependentClassLoader, mainDependent)
break
}
}
if (ClassLoaderConfigurationData.isClassloaderPerDescriptorEnabled(mainDependent.id, mainDependent.packagePrefix)) {
urlClassLoaderBuilder.files(mainDependentClassLoader.files)
for (module in modules) {
assert(module.packagePrefix != null)
configureModule(module, mainDependentClassLoader)
}
}
else {
mainDependentClassLoader.attachParent(dependencyPlugin.classLoader!!)
for (subDescriptor in value) {
subDescriptor.classLoader = mainDependentClassLoader
for (module in modules) {
module.classLoader = mainDependentClassLoader
}
}
}
loaders.clear()
urlClassLoaderBuilder.files(emptyList())
urlClassLoaderBuilder.files(Collections.emptyList())
}
fun configure(mainDependent: IdeaPluginDescriptorImpl) {
val pluginPackagePrefix = mainDependent.packagePrefix
if (pluginPackagePrefix != null && !pluginPackagePrefixUniqueGuard.add(pluginPackagePrefix)) {
throw PluginException("Package prefix $pluginPackagePrefix is already used", mainDependent.pluginId)
}
@JvmOverloads
fun configure(plugin: IdeaPluginDescriptorImpl, fallbackClassLoader: ClassLoader? = null) {
checkPackagePrefixUniqueness(plugin)
if (mainDependent.pluginId == PluginManagerCore.CORE_ID || mainDependent.isUseCoreClassLoader) {
setPluginClassLoaderForMainAndSubPlugins(mainDependent, coreLoader)
if (plugin.pluginId == PluginManagerCore.CORE_ID || plugin.isUseCoreClassLoader) {
setPluginClassLoaderForMainAndSubPlugins(plugin, coreLoader)
return
}
else if (!usePluginClassLoader) {
setPluginClassLoaderForMainAndSubPlugins(mainDependent, null)
if (!usePluginClassLoader) {
setPluginClassLoaderForMainAndSubPlugins(plugin, fallbackClassLoader)
}
loaders.clear()
// first, set class loader for main descriptor
if (hasAllModules) {
val implicitDependency = PluginManagerCore.getImplicitDependency(mainDependent) {
val implicitDependency = PluginManagerCore.getImplicitDependency(plugin) {
// first, set class loader for main descriptor
if (javaDep == null) {
javaDep = Optional.ofNullable(idMap.get(PluginManagerCore.JAVA_PLUGIN_ID))
javaDep = Optional.ofNullable(pluginSet.findEnabledPlugin(PluginManagerCore.JAVA_PLUGIN_ID))
}
javaDep!!.orElse(null)
}
implicitDependency?.let { addLoaderOrLogError(mainDependent, it, loaders) }
implicitDependency?.let { addLoaderOrLogError(plugin, it, loaders) }
}
var classPath = mainDependent.jarFiles
var classPath = plugin.jarFiles
if (classPath == null) {
classPath = collectClassPath(mainDependent)
classPath = collectClassPath(plugin)
}
else {
mainDependent.jarFiles = null
plugin.jarFiles = null
}
urlClassLoaderBuilder.files(classPath)
val pluginDependencies = mainDependent.pluginDependencies
if (pluginDependencies.isEmpty()) {
assert(!mainDependent.isUseIdeaClassLoader)
mainDependent.classLoader = createPluginClassLoader(mainDependent)
return
}
var oldActiveSubModules: MutableList<IdeaPluginDescriptorImpl>? = null
for (dependency in plugin.pluginDependencies) {
val p = pluginSet.findEnabledPlugin(dependency.pluginId) ?: continue
val loader = p.classLoader
if (loader == null) {
log.error(PluginLoadingError.formatErrorMessage(plugin, "requires missing class loader for $p"))
}
else if (loader !== coreLoader) {
loaders.add(loader)
}
for (dependency in pluginDependencies) {
if (!dependency.isDisabledOrBroken && (!isClassloaderPerDescriptorEnabled(mainDependent) || dependency.subDescriptor == null)) {
addClassloaderIfDependencyEnabled(dependency.pluginId, mainDependent)
dependency.subDescriptor?.let {
if (oldActiveSubModules == null) {
oldActiveSubModules = ArrayList()
}
oldActiveSubModules!!.add(it)
}
}
// new format
for (dependency in mainDependent.dependencyDescriptor.plugins) {
addClassloaderIfDependencyEnabled(dependency.id, mainDependent)
for (item in plugin.dependencies.modules) {
val descriptor = (pluginSet.findEnabledModule(item.name) ?: continue).requireDescriptor()
if (descriptor.classLoader !== coreLoader) {
loaders.add(descriptor.classLoader!!)
}
}
for (item in plugin.dependencies.plugins) {
val descriptor = pluginSet.findEnabledPlugin(item.id) ?: continue
if (descriptor.classLoader !== coreLoader) {
loaders.add(descriptor.classLoader!!)
}
}
val mainDependentClassLoader = if (mainDependent.isUseIdeaClassLoader) {
configureUsingIdeaClassloader(classPath, mainDependent)
val mainDependentClassLoader = if (plugin.isUseIdeaClassLoader) {
configureUsingIdeaClassloader(classPath, plugin)
}
else {
createPluginClassLoader(mainDependent)
createPluginClassLoader(plugin)
}
// second, set class loaders for sub descriptors
if (usePluginClassLoader && isClassloaderPerDescriptorEnabled(mainDependent)) {
mainDependent.classLoader = mainDependentClassLoader
for (dependencyInfo in pluginDependencies) {
configureSubPlugin(dependencyInfo, mainDependentClassLoader, mainDependent)
if (usePluginClassLoader) {
plugin.classLoader = mainDependentClassLoader
for (module in plugin.content.modules) {
configureModule(module.requireDescriptor(), mainDependentClassLoader)
}
for (subDescriptor in (oldActiveSubModules ?: Collections.emptyList())) {
// classLoader must be set - otherwise sub descriptor considered as inactive
subDescriptor.classLoader = mainDependentClassLoader
}
}
else {
setPluginClassLoaderForMainAndSubPlugins(mainDependent, mainDependentClassLoader)
setPluginClassLoaderForMainAndSubPlugins(plugin, mainDependentClassLoader)
}
// reset to ensure that stalled data will be not reused somehow later
loaders.clear()
urlClassLoaderBuilder.files(emptyList())
urlClassLoaderBuilder.files(Collections.emptyList())
}
private fun checkPackagePrefixUniqueness(module: IdeaPluginDescriptorImpl) {
val packagePrefix = module.packagePrefix
if (packagePrefix != null && !pluginPackagePrefixUniqueGuard.add(packagePrefix)) {
throw PluginException("Package prefix $packagePrefix is already used (module=$module)", module.pluginId)
}
}
private fun createPluginClassLoader(descriptor: IdeaPluginDescriptorImpl): PluginClassLoader {
val parentLoaders = if (loaders.isEmpty()) PluginClassLoader.EMPTY_CLASS_LOADER_ARRAY
else loaders.toArray(PluginClassLoader.EMPTY_CLASS_LOADER_ARRAY)
return createPluginClassLoader(parentLoaders, descriptor, urlClassLoaderBuilder, coreLoader, resourceFileFactory)
}
private fun configureSubPlugin(dependencyInfo: PluginDependency,
mainDependentClassLoader: ClassLoader,
parentDescriptor: IdeaPluginDescriptorImpl) {
val dependent = (if (dependencyInfo.isDisabledOrBroken) null else dependencyInfo.subDescriptor) ?: return
assert(!dependent.isUseIdeaClassLoader)
val pluginPackagePrefix = dependent.packagePrefix
if (pluginPackagePrefix == null) {
if (parentDescriptor.packagePrefix != null) {
throw PluginException("Sub descriptor must specify package if it is specified for main plugin descriptor " +
"(descriptorFile=${dependent.descriptorPath}, parentPackagePrefix=${parentDescriptor.packagePrefix})",
parentDescriptor.id)
}
val parentLoaders = if (loaders.isEmpty()) {
PluginClassLoader.EMPTY_CLASS_LOADER_ARRAY
}
else {
if (pluginPackagePrefix == parentDescriptor.packagePrefix) {
throw PluginException("Sub descriptor must not specify the same package as main plugin descriptor", parentDescriptor.id)
}
loaders.toArray(arrayOfNulls(loaders.size))
}
return createPluginClassLoader(parentLoaders = parentLoaders,
descriptor = descriptor,
urlClassLoaderBuilder = urlClassLoaderBuilder,
coreLoader = coreLoader,
resourceFileFactory = resourceFileFactory,
pluginSet = pluginSet)
}
if (parentDescriptor.packagePrefix == null) {
val parentId = parentDescriptor.id.idString
if (!(parentId == "Docker" ||
parentId == "org.jetbrains.plugins.ruby" ||
parentId == "org.intellij.grails" ||
parentId == "JavaScript")) {
throw PluginException("Sub descriptor must not specify package if one is not specified for main plugin descriptor",
parentDescriptor.id)
}
}
if (!pluginPackagePrefixUniqueGuard.add(pluginPackagePrefix)) {
throw PluginException("Package prefix $pluginPackagePrefix is already used", parentDescriptor.id)
}
private fun configureModule(module: IdeaPluginDescriptorImpl, mainDependentClassLoader: ClassLoader) {
if (module.packagePrefix == null) {
throw PluginException("Package is not specified (module=$module)", module.pluginId)
}
val dependency = idMap.get(dependencyInfo.pluginId)
if (dependency == null || !dependency.isEnabled) {
return
}
if (pluginPackagePrefix == null) {
packagePrefixes.clear()
collectPackagePrefixes(dependent, packagePrefixes)
// no package prefixes if only bean extension points are configured
if (packagePrefixes.isEmpty()) {
log.debug(
"Optional descriptor $dependencyInfo contains only bean extension points or light services")
}
}
checkPackagePrefixUniqueness(module)
loaders.clear()
// must be before main descriptor classloader
// only first level is supported - N level is not supported for a new model (several requirements maybe specified instead)
if (parentDescriptor.descriptorPath == null) {
addSiblingClassloaderIfNeeded(dependent, parentDescriptor)
for (item in module.dependencies.modules) {
// Module dependency is always optional. If the module depends on an unavailable plugin, it will not be loaded.
val descriptor = (pluginSet.findEnabledModule(item.name) ?: return).requireDescriptor()
if (descriptor.classLoader !== coreLoader) {
loaders.add(descriptor.classLoader!!)
}
}
for (item in module.dependencies.plugins) {
val descriptor = pluginSet.findEnabledPlugin(item.id) ?: return
if (descriptor.classLoader !== coreLoader) {
loaders.add(descriptor.classLoader!!)
}
}
// add main descriptor classloader as parent
loaders.add(mainDependentClassLoader)
addLoaderOrLogError(dependent, dependency, loaders)
val pluginDependencies = dependent.pluginDependencies
// add config-less dependencies to classloader parents
for (subDependency in pluginDependencies) {
if (!subDependency.isDisabledOrBroken && subDependency.subDescriptor == null) {
addClassloaderIfDependencyEnabled(subDependency.pluginId, dependent)
}
}
val subClassloader = if (pluginPackagePrefix == null) {
SubPluginClassLoader(dependent,
urlClassLoaderBuilder,
loaders.toTypedArray(),
packagePrefixes.toTypedArray(),
coreLoader, resourceFileFactory)
}
else {
createPluginClassLoader(dependent)
}
dependent.classLoader = subClassloader
for (subDependency in pluginDependencies) {
configureSubPlugin(subDependency, subClassloader, dependent)
}
}
private fun addSiblingClassloaderIfNeeded(dependent: IdeaPluginDescriptorImpl, parentDescriptor: IdeaPluginDescriptorImpl) {
if (!ClassLoaderConfigurationData.SEPARATE_CLASSLOADER_FOR_SUB) {
return
}
for (dependentModuleDependency in dependent.dependencyDescriptor.modules) {
if (parentDescriptor.contentDescriptor.findModuleByName(dependentModuleDependency.name) == null) {
// todo what about dependency on a module that contained in another plugin?
throw PluginException(
"Main descriptor $parentDescriptor must list module in content if it is specified as dependency in sub descriptor " +
"(descriptorFile=${dependent.descriptorPath})", parentDescriptor.id
)
}
for (dependencyPluginDependency in parentDescriptor.pluginDependencies) {
if (!dependencyPluginDependency.isDisabledOrBroken && dependencyPluginDependency.subDescriptor != null &&
dependentModuleDependency.packageName == dependencyPluginDependency.subDescriptor!!.packagePrefix) {
val classLoader = dependencyPluginDependency.subDescriptor!!.classLoader
?: throw PluginException("Classloader is null for sibling. " +
"Please ensure that content entry in the main plugin specifies module with package `" +
dependentModuleDependency.packageName +
"` before module with package `${dependent.packagePrefix}`" +
"(descriptorFile=${dependent.descriptorPath})", parentDescriptor.id)
loaders.add(classLoader)
}
}
}
}
private fun addClassloaderIfDependencyEnabled(dependencyId: PluginId, dependent: IdeaPluginDescriptorImpl) {
val dependency = idMap.get(dependencyId) ?: return
// must be first to ensure that it is used first to search classes (very important if main plugin descriptor doesn't have package prefix)
// check dependencies between optional descriptors (aka modules in a new model) from different plugins
if (ClassLoaderConfigurationData.SEPARATE_CLASSLOADER_FOR_SUB && !dependency.pluginDependencies.isEmpty()) {
for (dependentModuleDependency in dependent.dependencyDescriptor.modules) {
if (dependency.contentDescriptor.findModuleByName(dependentModuleDependency.name) != null) {
for (pluginDependency in dependency.pluginDependencies) {
if (!pluginDependency.isDisabledOrBroken && pluginDependency.subDescriptor != null &&
dependentModuleDependency.packageName == pluginDependency.subDescriptor!!.packagePrefix) {
loaders.add(pluginDependency.subDescriptor!!.classLoader!!)
}
}
break
}
}
}
val loader = dependency.classLoader
if (loader == null) {
log.error(PluginLoadingError.formatErrorMessage(dependent, "requires missing class loader for '${dependency.name}'"))
}
else if (loader !== coreLoader) {
loaders.add(loader)
}
assert(module.pluginDependencies.isEmpty())
val subClassloader = createPluginClassLoader(module)
module.classLoader = subClassloader
}
private fun addLoaderOrLogError(dependent: IdeaPluginDescriptorImpl,
@@ -320,18 +239,32 @@ class ClassLoaderConfigurator(
rootDescriptor.classLoader = classLoader
for (dependency in rootDescriptor.pluginDependencies) {
if (dependency.subDescriptor != null) {
val descriptor = idMap.get(dependency.pluginId)
if (descriptor != null && descriptor.isEnabled) {
val descriptor = pluginSet.findEnabledPlugin(dependency.pluginId)
if (descriptor != null) {
setPluginClassLoaderForMainAndSubPlugins(dependency.subDescriptor!!, classLoader)
}
}
}
m@ for (item in rootDescriptor.content.modules) {
val module = item.requireDescriptor()
// skip if some dependency is not available
for (dependency in module.dependencies.modules) {
pluginSet.findEnabledModule(dependency.name) ?: continue@m
}
for (dependency in module.dependencies.plugins) {
pluginSet.findEnabledPlugin(dependency.id) ?: continue@m
}
setPluginClassLoaderForMainAndSubPlugins(module, classLoader)
}
}
private fun collectClassPath(descriptor: IdeaPluginDescriptorImpl): List<Path> {
val pluginPath = descriptor.path
if (!Files.isDirectory(pluginPath)) {
return listOf(pluginPath)
return Collections.singletonList(pluginPath)
}
val result = ArrayList<Path>()
@@ -343,13 +276,6 @@ class ClassLoaderConfigurator(
val productionDirectory = pluginPath.parent
if (productionDirectory.endsWith("production")) {
result.add(pluginPath)
val moduleName = pluginPath.fileName.toString()
val additionalPaths = additionalLayoutMap.get(moduleName)
if (additionalPaths != null) {
for (path in additionalPaths) {
result.add(productionDirectory.resolve(path))
}
}
}
}
try {
@@ -376,12 +302,6 @@ class ClassLoaderConfigurator(
}
}
// this list doesn't duplicate of PluginXmlFactory.CLASS_NAMES - interface related must be not here
private val IMPL_CLASS_NAMES = ReferenceOpenHashSet(arrayOf(
"implementation", "implementationClass", "builderClass",
"serviceImplementation", "class", "className",
"instance", "implementation-class"))
// do not use class reference here
@Suppress("SSBasedInspection")
private val log: Logger
@@ -392,7 +312,8 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
descriptor: IdeaPluginDescriptorImpl,
urlClassLoaderBuilder: UrlClassLoader.Builder,
coreLoader: ClassLoader,
resourceFileFactory: ClassPath.ResourceFileFactory?): PluginClassLoader {
resourceFileFactory: ClassPath.ResourceFileFactory?,
pluginSet: PluginSet): PluginClassLoader {
// main plugin descriptor
if (descriptor.descriptorPath == null) {
when (descriptor.id.idString) {
@@ -431,14 +352,9 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
}
}
}
if (descriptor.packagePrefix == null) {
return PluginClassLoader(urlClassLoaderBuilder, parentLoaders, descriptor, descriptor.pluginPath, coreLoader, null, null,
resourceFileFactory)
}
}
else {
if (!descriptor.contentDescriptor.modules.isEmpty()) {
if (!descriptor.content.modules.isEmpty()) {
// see "The `content.module` element" section about content handling for a module
return createPluginClassloader(parentLoaders = parentLoaders,
descriptor = descriptor,
@@ -460,8 +376,14 @@ private fun createPluginClassLoader(parentLoaders: Array<ClassLoader>,
}
}
return createPluginClassloader(parentLoaders, descriptor, urlClassLoaderBuilder, coreLoader, resourceFileFactory,
createPluginDependencyAndContentBasedScope(descriptor))
return createPluginClassloader(
parentLoaders = parentLoaders,
descriptor = descriptor,
urlClassLoaderBuilder = urlClassLoaderBuilder,
coreLoader = coreLoader,
resourceFileFactory = resourceFileFactory,
resolveScopeManager = createPluginDependencyAndContentBasedScope(descriptor = descriptor, pluginSet = pluginSet)
)
}
private fun createPluginClassloader(parentLoaders: Array<ClassLoader>,
@@ -496,9 +418,14 @@ private fun createPluginClassLoaderWithExtraPackage(parentLoaders: Array<ClassLo
// package of module is not taken in account to support resolving of module libraries -
// instead, only classes from plugin's modules (content or dependencies) are excluded.
private fun createPluginDependencyAndContentBasedScope(descriptor: IdeaPluginDescriptorImpl): PluginClassLoader.ResolveScopeManager {
private fun createPluginDependencyAndContentBasedScope(descriptor: IdeaPluginDescriptorImpl,
pluginSet: PluginSet): PluginClassLoader.ResolveScopeManager? {
val contentPackagePrefixes = getContentPackagePrefixes(descriptor)
val dependencyPackagePrefixes = getDependencyPackagePrefixes(descriptor)
val dependencyPackagePrefixes = getDependencyPackagePrefixes(descriptor, pluginSet)
if (contentPackagePrefixes.isEmpty() && dependencyPackagePrefixes.isEmpty()) {
return null
}
val pluginId = descriptor.pluginId.idString
return PluginClassLoader.ResolveScopeManager { name, _, force ->
if (force) {
@@ -511,6 +438,7 @@ private fun createPluginDependencyAndContentBasedScope(descriptor: IdeaPluginDes
return@ResolveScopeManager true
}
}
for (prefix in dependencyPackagePrefixes) {
if (name.startsWith(prefix)) {
return@ResolveScopeManager true
@@ -522,29 +450,28 @@ private fun createPluginDependencyAndContentBasedScope(descriptor: IdeaPluginDes
}
private fun getContentPackagePrefixes(descriptor: IdeaPluginDescriptorImpl): List<String> {
var result: MutableList<String>? = null
for (item in descriptor.contentDescriptor.modules) {
if (item.isInjected) {
continue
}
val packagePrefix = item.packageName ?: continue
if (result == null) {
result = ArrayList(descriptor.contentDescriptor.modules.size)
}
result.add("$packagePrefix.")
val modules = descriptor.content.modules
if (modules.isEmpty()) {
return Collections.emptyList()
}
return result ?: emptyList()
val result = Array(modules.size) {
val module = modules.get(it).requireDescriptor()
"${module.packagePrefix ?: throw PluginException("Package is not specified (module=$module)", module.pluginId)}."
}
@Suppress("ReplaceJavaStaticMethodWithKotlinAnalog")
return Arrays.asList(*result)
}
private fun getDependencyPackagePrefixes(descriptor: IdeaPluginDescriptorImpl): List<String> {
if (descriptor.dependencyDescriptor.modules.isEmpty()) {
return emptyList()
private fun getDependencyPackagePrefixes(descriptor: IdeaPluginDescriptorImpl, pluginSet: PluginSet): List<String> {
val dependencies = descriptor.dependencies.modules
if (dependencies.isEmpty()) {
return Collections.emptyList()
}
val result = ArrayList<String>(descriptor.dependencyDescriptor.modules.size)
for (item in descriptor.dependencyDescriptor.modules) {
val packagePrefix = item.packageName
val result = ArrayList<String>(dependencies.size)
for (item in dependencies) {
val packagePrefix = (pluginSet.findEnabledModule(item.name) ?: continue).requireDescriptor().packagePrefix
// intellij.platform.commercial.verifier is injected
if (packagePrefix != null && item.name != "intellij.platform.commercial.verifier") {
result.add("$packagePrefix.")
@@ -554,11 +481,9 @@ private fun getDependencyPackagePrefixes(descriptor: IdeaPluginDescriptorImpl):
}
private fun createModuleContentBasedScope(descriptor: IdeaPluginDescriptorImpl): PluginClassLoader.ResolveScopeManager {
val packagePrefixes = ArrayList<String>(descriptor.contentDescriptor.modules.size)
for (item in descriptor.contentDescriptor.modules) {
item.packageName?.let {
packagePrefixes.add("$it.")
}
val packagePrefixes = ArrayList<String>(descriptor.content.modules.size)
for (item in descriptor.content.modules) {
packagePrefixes.add("${item.requireDescriptor().packagePrefix!!}.")
}
// force flag is ignored for module - e.g. RailsViewLineMarkerProvider is referenced as extension implementation in several modules
@@ -579,91 +504,6 @@ private fun createModuleContentBasedScope(descriptor: IdeaPluginDescriptorImpl):
}
}
private fun isClassloaderPerDescriptorEnabled(descriptor: IdeaPluginDescriptorImpl): Boolean {
return ClassLoaderConfigurationData.isClassloaderPerDescriptorEnabled(descriptor.id, descriptor.packagePrefix)
}
private fun collectPackagePrefixes(dependent: IdeaPluginDescriptorImpl, packagePrefixes: MutableList<String>) {
// from extensions
dependent.unsortedEpNameToExtensionElements.values.forEach { extensionDescriptors ->
for (extensionDescriptor in extensionDescriptors) {
if (extensionDescriptor.implementation != null) {
addPackageByClassNameIfNeeded(extensionDescriptor.implementation!!, packagePrefixes)
continue
}
val element = extensionDescriptor.element ?: continue
if (!element.attributes.isEmpty()) {
continue
}
for (attributeName in IMPL_CLASS_NAMES) {
val className = element.getAttributeValue(attributeName)
if (className != null && !className.isEmpty()) {
addPackageByClassNameIfNeeded(className, packagePrefixes)
break
}
}
}
}
// from services
collectFromServices(dependent.appContainerDescriptor, packagePrefixes)
collectFromServices(dependent.projectContainerDescriptor, packagePrefixes)
collectFromServices(dependent.moduleContainerDescriptor, packagePrefixes)
}
private fun addPackageByClassNameIfNeeded(name: String, packagePrefixes: MutableList<String>) {
for (packagePrefix in packagePrefixes) {
if (name.startsWith(packagePrefix)) {
return
}
}
// for classes like com.intellij.thymeleaf.lang.ThymeleafParserDefinition$SPRING_SECURITY_EXPRESSIONS
// we must not try to load the containing package
if (name.indexOf('$') != -1) {
packagePrefixes.add(name)
return
}
val lastPackageDot = name.lastIndexOf('.')
if (lastPackageDot > 0 && lastPackageDot != name.length) {
addPackagePrefixIfNeeded(packagePrefixes, name.substring(0, lastPackageDot + 1))
}
}
private fun addPackagePrefixIfNeeded(packagePrefixes: MutableList<String>, packagePrefix: String) {
for (i in packagePrefixes.indices) {
val existingPackagePrefix = packagePrefixes.get(i)
if (packagePrefix.startsWith(existingPackagePrefix)) {
return
}
else if (existingPackagePrefix.startsWith(packagePrefix) && existingPackagePrefix.indexOf('$') == -1) {
packagePrefixes.set(i, packagePrefix)
for (j in packagePrefixes.size - 1 downTo i + 1) {
if (packagePrefixes.get(j).startsWith(packagePrefix)) {
packagePrefixes.removeAt(j)
}
}
return
}
}
packagePrefixes.add(packagePrefix)
}
private fun collectFromServices(containerDescriptor: ContainerDescriptor, packagePrefixes: MutableList<String>) {
for (service in containerDescriptor.services) {
// testServiceImplementation is ignored by intention
service.serviceImplementation?.let {
addPackageByClassNameIfNeeded(it, packagePrefixes)
}
service.headlessImplementation?.let {
addPackageByClassNameIfNeeded(it, packagePrefixes)
}
}
}
private fun configureUsingIdeaClassloader(classPath: List<Path?>, descriptor: IdeaPluginDescriptorImpl): ClassLoader {
log.warn("${descriptor.pluginId} uses deprecated `use-idea-classloader` attribute")
val loader = ClassLoaderConfigurator::class.java.classLoader

View File

@@ -2,8 +2,11 @@
package com.intellij.ide.plugins
import com.intellij.platform.util.plugins.DataLoader
import com.intellij.platform.util.plugins.LocalFsDataLoader
import java.nio.file.Files
import java.nio.file.NoSuchFileException
internal class ClassPathXmlPathResolver(private val classLoader: ClassLoader) : PathResolver {
internal class ClassPathXmlPathResolver(private val classLoader: ClassLoader, private val isRunningFromSources: Boolean) : PathResolver {
override val isFlat: Boolean
get() = true
@@ -23,6 +26,33 @@ internal class ClassPathXmlPathResolver(private val classLoader: ClassLoader) :
return true
}
override fun resolveModuleFile(readContext: ReadModuleContext,
dataLoader: DataLoader,
path: String,
readInto: RawPluginDescriptor?): RawPluginDescriptor {
var resource = classLoader.getResourceAsStream(path)
if (resource == null) {
if (isRunningFromSources && path.startsWith("intellij.") && dataLoader is LocalFsDataLoader) {
try {
resource = Files.newInputStream(dataLoader.basePath.parent.resolve("${path.substring(0, path.length - 4)}/$path"))
}
catch (e: NoSuchFileException) {
}
}
if (resource == null) {
throw RuntimeException("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader)")
}
}
return readModuleDescriptor(inputStream = resource,
readContext = readContext,
pathResolver = this,
dataLoader = dataLoader,
includeBase = null,
readInto = readInto,
locationSource = dataLoader.toString())
}
override fun resolvePath(readContext: ReadModuleContext,
dataLoader: DataLoader,
relativePath: String,

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.intellij.AbstractBundle
@@ -85,8 +85,8 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
@JvmField val projectContainerDescriptor = raw.projectContainerDescriptor
@JvmField val moduleContainerDescriptor = raw.moduleContainerDescriptor
@JvmField internal val contentDescriptor = raw.contentDescriptor
@JvmField internal val dependencyDescriptor = raw.dependencyDescriptor
@JvmField val content = raw.content
@JvmField val dependencies = raw.dependencies
@JvmField val modules: List<PluginId> = raw.modules ?: Collections.emptyList()
private val descriptionChildText = raw.description
@@ -120,7 +120,11 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
override fun getPluginPath() = path
fun createSub(raw: RawPluginDescriptor, descriptorPath: String): IdeaPluginDescriptorImpl {
private fun createSub(raw: RawPluginDescriptor,
descriptorPath: String,
pathResolver: PathResolver,
context: DescriptorListLoadingContext,
dataLoader: DataLoader): IdeaPluginDescriptorImpl {
raw.name = name
@Suppress("TestOnlyProblems")
val result = IdeaPluginDescriptorImpl(raw, path = path, isBundled = isBundled, id = id)
@@ -128,6 +132,8 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
result.vendor = vendor
result.version = version
result.resourceBundleBaseName = resourceBundleBaseName
result.readExternal(raw = raw, pathResolver = pathResolver, context = context, isSub = true, dataLoader = dataLoader)
return result
}
@@ -139,21 +145,17 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
// include module file descriptor if not specified as `depends` (old way - xi:include)
// must be first because merged into raw descriptor
if (!isSub) {
moduleLoop@ for (module in contentDescriptor.modules) {
val descriptorFile = module.configFile ?: "${module.name}.xml"
val oldDepends = raw.depends
if (oldDepends != null) {
for (dependency in oldDepends) {
if (descriptorFile == dependency.configFile) {
// ok, it is specified in old way as depends tag - skip it
continue@moduleLoop
}
}
}
pathResolver.resolvePath(context, dataLoader, descriptorFile, raw)
?: throw RuntimeException("Plugin $this misses optional descriptor $descriptorFile")
module.isInjected = true
for (module in content.modules) {
val subDescriptorFile = module.configFile ?: "${module.name}.xml"
val subDescriptor = createSub(raw = pathResolver.resolveModuleFile(readContext = context,
dataLoader = dataLoader,
path = subDescriptorFile,
readInto = null),
descriptorPath = subDescriptorFile,
pathResolver = pathResolver,
context = context,
dataLoader = dataLoader)
module.descriptor = subDescriptor
}
}
@@ -179,7 +181,7 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
markAsIncomplete(context, null, null)
}
else {
for (pluginDependency in dependencyDescriptor.plugins) {
for (pluginDependency in dependencies.plugins) {
if (context.isPluginDisabled(pluginDependency.id)) {
markAsIncomplete(context, pluginDependency.id, shortMessage = "plugin.loading.error.short.depends.on.disabled.plugin")
}
@@ -213,7 +215,6 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
if (!dependency.isOptional && !isIncomplete) {
markAsIncomplete(context, dependency.pluginId, "plugin.loading.error.short.depends.on.disabled.plugin")
}
dependency.isDisabledOrBroken = true
}
else if (context.result.isBroken(dependency.pluginId)) {
if (!dependency.isOptional && !isIncomplete) {
@@ -222,7 +223,6 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
shortMessage = "plugin.loading.error.short.depends.on.broken.plugin",
pluginId = dependency.pluginId)
}
dependency.isDisabledOrBroken = true
}
// because of https://youtrack.jetbrains.com/issue/IDEA-206274, configFile maybe not only for optional dependencies
@@ -260,10 +260,12 @@ class IdeaPluginDescriptorImpl(raw: RawPluginDescriptor,
checkCycle(descriptor, configFile, visitedFiles)
val subDescriptor = descriptor.createSub(raw, configFile)
visitedFiles.add(configFile)
subDescriptor.readExternal(raw = raw, pathResolver = pathResolver, context = context, isSub = true, dataLoader = dataLoader)
val subDescriptor = descriptor.createSub(raw = raw,
descriptorPath = configFile,
pathResolver = pathResolver,
context = context,
dataLoader = dataLoader)
dependency.subDescriptor = subDescriptor
visitedFiles.clear()
}

View File

@@ -1,71 +1,42 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty")
package com.intellij.ide.plugins
import com.intellij.openapi.extensions.PluginId
import org.jetbrains.annotations.ApiStatus
import java.util.*
internal class ModuleDependenciesDescriptor(@JvmField val modules: List<ModuleItem>, @JvmField val plugins: List<PluginItem>) {
class ModuleDependenciesDescriptor(@JvmField val modules: List<ModuleReference>, @JvmField val plugins: List<PluginItem>) {
companion object {
@JvmField
val EMPTY = ModuleDependenciesDescriptor(emptyList(), emptyList())
@JvmField val EMPTY = ModuleDependenciesDescriptor(Collections.emptyList(), Collections.emptyList())
}
fun findModuleByName(name: String): ModuleItem? {
for (module in modules) {
if (module.name == name) {
return module
}
}
return null
class ModuleReference(@JvmField val name: String) {
override fun toString() = "ModuleItem(name=$name)"
}
internal class ModuleItem(@JvmField val name: String, @JvmField val packageName: String?) {
init {
if (packageName != null && packageName.endsWith(".")) {
throw RuntimeException("packageName must not ends with dot: $packageName")
}
}
override fun toString(): String {
return "ModuleItem(name=$name, packageName=$packageName)"
}
class PluginItem(@JvmField val id: PluginId) {
override fun toString() = "PluginItem(id=$id)"
}
internal class PluginItem(@JvmField val id: PluginId) {
override fun toString(): String {
return "PluginItem(id=$id)"
}
}
override fun toString(): String {
return "ModuleDependenciesDescriptor(modules=$modules, plugins=$plugins)"
}
override fun toString() = "ModuleDependenciesDescriptor(modules=$modules, plugins=$plugins)"
}
internal class PluginContentDescriptor(@JvmField val modules: List<ModuleItem>) {
@ApiStatus.Internal
class PluginContentDescriptor(@JvmField val modules: List<ModuleItem>) {
companion object {
@JvmField
val EMPTY = PluginContentDescriptor(emptyList())
@JvmField val EMPTY = PluginContentDescriptor(Collections.emptyList())
}
fun findModuleByName(name: String): ModuleItem? {
for (module in modules) {
if (module.name == name) {
return module
}
}
return null
}
@ApiStatus.Internal
class ModuleItem(@JvmField val name: String,
@JvmField val configFile: String?) {
@JvmField internal var descriptor: IdeaPluginDescriptorImpl? = null
internal class ModuleItem(@JvmField val name: String, @JvmField val packageName: String?, @JvmField val configFile: String?) {
// <module name="intellij.clouds.docker.file" package="com.intellij.docker.dockerFile"/> - xi-include without classloader at all
@JvmField
var isInjected = false
fun requireDescriptor() = descriptor ?: throw IllegalStateException("Descriptor is not set for $this")
init {
if (packageName != null && packageName.endsWith(".")) {
throw RuntimeException("packageName must not ends with dot: $packageName")
}
override fun toString(): String {
return "ModuleItem(name=$name, descriptor=$descriptor, configFile=$configFile)"
}
}

View File

@@ -19,4 +19,10 @@ interface PathResolver {
dataLoader: DataLoader,
relativePath: String,
readInto: RawPluginDescriptor?): RawPluginDescriptor?
// module in a new file name format must be always resolved
fun resolveModuleFile(readContext: ReadModuleContext,
dataLoader: DataLoader,
path: String,
readInto: RawPluginDescriptor?): RawPluginDescriptor
}

View File

@@ -7,8 +7,7 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
class PluginDependency internal constructor(override val pluginId: PluginId,
configFile: String?,
isOptional: Boolean,
@JvmField @field:Transient var isDisabledOrBroken: Boolean) : IdeaPluginDependency {
isOptional: Boolean) : IdeaPluginDependency {
var configFile = configFile
internal set
@@ -23,7 +22,6 @@ class PluginDependency internal constructor(override val pluginId: PluginId,
"pluginId=" + pluginId +
", isOptional=" + isOptional +
", configFile=" + configFile +
", isDisabledOrBroken=" + isDisabledOrBroken +
", subDescriptor=" + subDescriptor +
')'
}

View File

@@ -394,7 +394,7 @@ private fun loadBundledDescriptorsAndDescriptorsFromDir(context: DescriptorListL
val platformPrefix = PlatformUtils.getPlatformPrefix()
// should be the only plugin in lib (only for Ultimate and WebStorm for now)
val pathResolver = ClassPathXmlPathResolver(classLoader)
val pathResolver = ClassPathXmlPathResolver(classLoader, isRunningFromSources = isRunningFromSources)
if ((platformPrefix == PlatformUtils.IDEA_PREFIX || platformPrefix == PlatformUtils.WEB_PREFIX) &&
(java.lang.Boolean.getBoolean("idea.use.dev.build.server") || (!isUnitTestMode && !isRunningFromSources))) {
val dataLoader = object : DataLoader {
@@ -577,7 +577,7 @@ fun getDescriptorsToMigrate(dir: Path,
fun createPathResolverForPlugin(descriptor: IdeaPluginDescriptorImpl, checkPluginJarFiles: Boolean): PathResolver {
if (PluginManagerCore.isRunningFromSources() && descriptor.pluginPath.fileSystem == FileSystems.getDefault() &&
descriptor.pluginPath.toString().contains("out/classes")) {
return ClassPathXmlPathResolver(descriptor.pluginClassLoader)
return ClassPathXmlPathResolver(descriptor.pluginClassLoader, isRunningFromSources = false)
}
else if (checkPluginJarFiles) {
val pluginJarFiles = ArrayList<Path>()
@@ -598,7 +598,7 @@ fun testLoadDescriptorsFromClassPath(loader: ClassLoader): List<IdeaPluginDescri
result = PluginLoadingResult(brokenPluginVersions = emptyMap(),
productBuildNumber = Supplier { buildNumber },
checkModuleDependencies = false))
LoadDescriptorsFromClassPathAction(urlsFromClassPath, context, null, ClassPathXmlPathResolver(loader)).compute()
LoadDescriptorsFromClassPathAction(urlsFromClassPath, context, null, ClassPathXmlPathResolver(loader, isRunningFromSources = false)).compute()
context.result.finishLoading()
return context.result.getEnabledPlugins()
}
@@ -677,13 +677,10 @@ private class LoadDescriptorsFromClassPathAction(private val urls: Map<URL, Stri
val result = context.result
ForkJoinTask.invokeAll(tasks)
val usePluginClassLoader = PluginManagerCore.usePluginClassLoader
for (task in tasks) {
task.rawResult?.let {
if (!usePluginClassLoader) {
it.setUseCoreClassLoader()
}
result.add(it, /* overrideUseIfCompatible = */false)
it.setUseCoreClassLoader()
result.add(it, overrideUseIfCompatible = false)
}
}
}
@@ -721,9 +718,7 @@ private class LoadDescriptorsFromClassPathAction(private val urls: Map<URL, Stri
isBundled = true,
isEssential = isEssential,
pluginPath = file.parent.parent)
if (descriptor != null) {
descriptor.jarFiles = null
}
descriptor?.jarFiles = null
return descriptor
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins;
import com.intellij.ide.plugins.cl.PluginAwareClassLoader;
@@ -8,15 +8,14 @@ import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.ComponentManager;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphAlgorithms;
import com.intellij.util.graph.GraphGenerator;
import com.intellij.util.graph.InboundSemiGraph;
import com.intellij.util.lang.Java11Shim;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -186,19 +185,16 @@ public final class PluginManager {
}
@ApiStatus.Internal
public void setPlugins(@NotNull List<IdeaPluginDescriptor> descriptors) {
@SuppressWarnings("SuspiciousToArrayCall")
IdeaPluginDescriptorImpl[] list = descriptors.toArray(new IdeaPluginDescriptorImpl[0]);
PluginManagerCore.doSetPlugins(list);
public void setPlugins(@NotNull List<IdeaPluginDescriptorImpl> descriptors) {
PluginManagerCore.doSetPlugins(Java11Shim.INSTANCE.copyOf(descriptors));
}
@ApiStatus.Internal
public boolean processAllBackwardDependencies(@NotNull IdeaPluginDescriptorImpl rootDescriptor,
boolean withOptionalDeps,
@NotNull Function<IdeaPluginDescriptorImpl, FileVisitResult> consumer) {
Map<PluginId, IdeaPluginDescriptorImpl> idMap = PluginManagerCore.buildPluginIdMap();
Collection<IdeaPluginDescriptorImpl> allPlugins = PluginManagerCore.getAllPlugins();
CachingSemiGraph<IdeaPluginDescriptorImpl> semiGraph = PluginManagerCore.createPluginIdGraph(allPlugins, idMap, withOptionalDeps);
@NotNull PluginSet pluginSet = PluginManagerCore.getPluginSet();
CachingSemiGraph<IdeaPluginDescriptorImpl> semiGraph = PluginManagerCore.createPluginIdGraph(pluginSet.loadedPlugins, pluginSet, withOptionalDeps);
Graph<IdeaPluginDescriptorImpl> graph = GraphGenerator.generate(semiGraph);
Set<IdeaPluginDescriptorImpl> dependencies = new LinkedHashSet<>();
GraphAlgorithms.getInstance().collectOutsRecursively(graph, rootDescriptor, dependencies);
@@ -213,9 +209,8 @@ public final class PluginManager {
return true;
}
@NotNull IdeaPluginDescriptorImpl @NotNull [] getPluginsSortedByDependency(@NotNull List<IdeaPluginDescriptorImpl> descriptors) {
Map<PluginId, IdeaPluginDescriptorImpl> idMap = PluginManagerCore.buildPluginIdMap();
InboundSemiGraph<IdeaPluginDescriptorImpl> graph = PluginManagerCore.createPluginIdGraph(descriptors, idMap, true);
@NotNull List<IdeaPluginDescriptorImpl> getPluginsSortedByDependency(@NotNull List<IdeaPluginDescriptorImpl> descriptors) {
InboundSemiGraph<IdeaPluginDescriptorImpl> graph = PluginManagerCore.createPluginIdGraph(descriptors, PluginManagerCore.getPluginSet(), true);
return PluginManagerCore.getTopologicallySorted(graph);
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins;
import com.intellij.core.CoreBundle;
@@ -17,15 +17,12 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.PlatformUtils;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.execution.ParametersListUtil;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.GraphGenerator;
import com.intellij.util.graph.InboundSemiGraph;
import com.intellij.util.lang.Java11Shim;
import com.intellij.util.lang.UrlClassLoader;
import org.jetbrains.annotations.*;
@@ -39,7 +36,6 @@ import java.nio.file.*;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ForkJoinPool;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -48,6 +44,9 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
// Prefer to use only JDK classes. Any post start-up functionality should be placed in PluginManager class.
/**
* See <a href="https://github.com/JetBrains/intellij-community/blob/master/platform/core-impl/src/com/intellij/ide/plugins/readme.md">Plugin Model V2 documentation</a>
*/
public final class PluginManagerCore {
public static final @NonNls String META_INF = "META-INF/";
@@ -78,19 +77,14 @@ public final class PluginManagerCore {
private static final MethodType HAS_LOADED_CLASS_METHOD_TYPE = MethodType.methodType(boolean.class, String.class);
private static Reference<Map<PluginId, Set<String>>> brokenPluginVersions;
private static volatile IdeaPluginDescriptorImpl[] ourPlugins;
private static volatile List<IdeaPluginDescriptorImpl> ourLoadedPlugins;
private static Map<PluginId, PluginLoadingError> ourPluginLoadingErrors;
private static Map<String, String[]> ourAdditionalLayoutMap = Collections.emptyMap();
private static volatile @Nullable PluginSet pluginSet;
private static Map<PluginId, PluginLoadingError> pluginLoadingErrors;
@SuppressWarnings("StaticNonFinalField")
public static volatile boolean isUnitTestMode = Boolean.getBoolean("idea.is.unit.test");
@ApiStatus.Internal
static final boolean usePluginClassLoader = Boolean.getBoolean("idea.from.sources.plugins.class.loader");
@ApiStatus.Internal
private static final List<Supplier<? extends HtmlChunk>> ourPluginErrors = new ArrayList<>();
private static final List<Supplier<? extends HtmlChunk>> pluginErrors = new ArrayList<>();
private static Set<PluginId> ourPluginsToDisable;
private static Set<PluginId> ourPluginsToEnable;
@@ -118,16 +112,11 @@ public final class PluginManagerCore {
* Do not call this method during bootstrap, should be called in a copy of PluginManager, loaded by PluginClassLoader.
*/
public static @NotNull IdeaPluginDescriptor @NotNull[] getPlugins() {
IdeaPluginDescriptor[] result = ourPlugins;
if (result == null) {
loadAndInitializePlugins(null, null);
return ourPlugins;
}
return result;
return getPluginSet().allPlugins.toArray(new IdeaPluginDescriptor[0]);
}
static @NotNull Collection<IdeaPluginDescriptorImpl> getAllPlugins() {
return Arrays.asList(ourPlugins);
static @NotNull PluginSet getPluginSet() {
return Objects.requireNonNull(pluginSet);
}
/**
@@ -140,37 +129,30 @@ public final class PluginManagerCore {
@ApiStatus.Internal
public static @NotNull List<IdeaPluginDescriptorImpl> getLoadedPlugins(@Nullable ClassLoader coreClassLoader) {
List<IdeaPluginDescriptorImpl> result = ourLoadedPlugins;
if (result == null) {
loadAndInitializePlugins(null, coreClassLoader);
return ourLoadedPlugins;
PluginSet result = pluginSet;
if (result != null) {
return result.loadedPlugins;
}
return result;
return loadAndInitializePlugins(PluginDescriptorLoader.loadDescriptors(isUnitTestMode, isRunningFromSources()),
coreClassLoader == null ? PluginManagerCore.class.getClassLoader() : coreClassLoader).loadedPlugins;
}
@ApiStatus.Internal
public static @NotNull List<HtmlChunk> getAndClearPluginLoadingErrors() {
synchronized (ourPluginErrors) {
List<HtmlChunk> errors = ContainerUtil.map(ourPluginErrors, Supplier::get);
ourPluginErrors.clear();
synchronized (pluginErrors) {
List<HtmlChunk> errors = ContainerUtil.map(pluginErrors, Supplier::get);
pluginErrors.clear();
return errors;
}
}
private static void registerPluginErrors(List<? extends Supplier<? extends HtmlChunk>> errors) {
synchronized (ourPluginErrors) {
ourPluginErrors.addAll(errors);
}
}
@ApiStatus.Internal
public static boolean arePluginsInitialized() {
return ourPlugins != null;
return pluginSet != null;
}
static synchronized void doSetPlugins(@NotNull IdeaPluginDescriptorImpl @Nullable [] value) {
ourPlugins = value;
ourLoadedPlugins = value == null ? null : Collections.unmodifiableList(getOnlyEnabledPlugins(value));
static synchronized void doSetPlugins(@Nullable List<IdeaPluginDescriptorImpl> value) {
pluginSet = value == null ? null : new PluginSet(value, getOnlyEnabledPlugins(value));
}
public static boolean isDisabled(@NotNull PluginId pluginId) {
@@ -308,8 +290,8 @@ public final class PluginManagerCore {
@ApiStatus.Internal
public static @Nullable PluginDescriptor getPluginDescriptorOrPlatformByClassName(@NotNull @NonNls String className) {
List<IdeaPluginDescriptorImpl> loadedPlugins = ourLoadedPlugins;
if (loadedPlugins == null ||
PluginSet pluginSet = PluginManagerCore.pluginSet;
if (pluginSet == null ||
className.startsWith("java.") ||
className.startsWith("javax.") ||
className.startsWith("kotlin.") ||
@@ -319,7 +301,7 @@ public final class PluginManagerCore {
}
IdeaPluginDescriptor result = null;
for (IdeaPluginDescriptorImpl o : loadedPlugins) {
for (IdeaPluginDescriptorImpl o : pluginSet.loadedPlugins) {
ClassLoader classLoader = o.getPluginClassLoader();
if (!hasLoadedClass(className, classLoader)) {
continue;
@@ -344,7 +326,7 @@ public final class PluginManagerCore {
// otherwise we need to check plugins with use-idea-classloader="true"
String root = null;
for (IdeaPluginDescriptorImpl o : loadedPlugins) {
for (IdeaPluginDescriptorImpl o : pluginSet.loadedPlugins) {
if (!o.isUseIdeaClassLoader) {
continue;
}
@@ -420,14 +402,12 @@ public final class PluginManagerCore {
static @Nullable IdeaPluginDescriptorImpl getImplicitDependency(@NotNull IdeaPluginDescriptorImpl descriptor,
@NotNull Supplier<IdeaPluginDescriptorImpl> javaDepGetter) {
// skip our plugins as expected to be up-to-date whether bundled or not
if (descriptor.isBundled() ||
VENDOR_JETBRAINS.equals(descriptor.getVendor())) {
if (descriptor.isBundled() || VENDOR_JETBRAINS.equals(descriptor.getVendor())) {
return null;
}
PluginId pluginId = descriptor.getPluginId();
if (CORE_ID.equals(pluginId) ||
JAVA_PLUGIN_ID.equals(pluginId)) {
if (CORE_ID.equals(pluginId) || JAVA_PLUGIN_ID.equals(pluginId)) {
return null;
}
@@ -459,8 +439,8 @@ public final class PluginManagerCore {
ourShadowedBundledPlugins = null;
}
private static void logPlugins(@NotNull IdeaPluginDescriptorImpl @NotNull [] plugins,
Collection<IdeaPluginDescriptorImpl> incompletePlugins) {
private static void logPlugins(@NotNull List<IdeaPluginDescriptorImpl> plugins,
@NotNull Collection<IdeaPluginDescriptorImpl> incompletePlugins) {
StringBuilder bundled = new StringBuilder();
StringBuilder disabled = new StringBuilder();
StringBuilder custom = new StringBuilder();
@@ -528,7 +508,7 @@ public final class PluginManagerCore {
private static void prepareLoadingPluginsErrorMessage(@NotNull Map<PluginId, PluginLoadingError> pluginErrors,
@NotNull List<Supplier<@NlsContexts.DetailedDescription String>> globalErrors,
@NotNull List<Supplier<HtmlChunk>> actions) {
ourPluginLoadingErrors = pluginErrors;
pluginLoadingErrors = pluginErrors;
if (pluginErrors.isEmpty() && globalErrors.isEmpty()) {
return;
@@ -552,7 +532,10 @@ public final class PluginManagerCore {
).collect(Collectors.toList());
if (!errorsList.isEmpty()) {
registerPluginErrors(ContainerUtil.concat(errorsList, actions));
synchronized (PluginManagerCore.pluginErrors) {
PluginManagerCore.pluginErrors.addAll(errorsList);
PluginManagerCore.pluginErrors.addAll(actions);
}
}
getLogger().warn(logMessage);
@@ -563,71 +546,61 @@ public final class PluginManagerCore {
}
public static @Nullable @NlsContexts.Label String getShortLoadingErrorMessage(@NotNull IdeaPluginDescriptor pluginDescriptor) {
PluginLoadingError error = ourPluginLoadingErrors.get(pluginDescriptor.getPluginId());
PluginLoadingError error = pluginLoadingErrors.get(pluginDescriptor.getPluginId());
return error == null ? null : error.getShortMessage();
}
public static @Nullable PluginId getFirstDisabledDependency(@NotNull IdeaPluginDescriptor pluginDescriptor) {
PluginLoadingError error = ourPluginLoadingErrors.get(pluginDescriptor.getPluginId());
PluginLoadingError error = pluginLoadingErrors.get(pluginDescriptor.getPluginId());
return error == null ? null : error.disabledDependency;
}
static @NotNull CachingSemiGraph<IdeaPluginDescriptorImpl> createPluginIdGraph(@NotNull Collection<IdeaPluginDescriptorImpl> descriptors,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap,
@NotNull PluginSet pluginSet,
boolean withOptional) {
boolean hasAllModules = idToDescriptorMap.containsKey(ALL_MODULES_MARKER);
Supplier<IdeaPluginDescriptorImpl> javaDep = () -> idToDescriptorMap.get(JAVA_MODULE_ID);
boolean hasAllModules = pluginSet.isPluginEnabled(ALL_MODULES_MARKER);
Supplier<IdeaPluginDescriptorImpl> javaDep = () -> pluginSet.findEnabledPlugin(JAVA_MODULE_ID);
Set<IdeaPluginDescriptorImpl> uniqueCheck = new HashSet<>();
Map<IdeaPluginDescriptorImpl, List<IdeaPluginDescriptorImpl>> in = new HashMap<>(descriptors.size());
List<IdeaPluginDescriptorImpl> list = new ArrayList<>(32);
for (IdeaPluginDescriptorImpl descriptor : descriptors) {
List<IdeaPluginDescriptorImpl> list = getDirectDependencies(descriptor, idToDescriptorMap, withOptional, hasAllModules, javaDep, uniqueCheck);
getDirectDependencies(descriptor, pluginSet, withOptional, hasAllModules, javaDep, uniqueCheck, list);
if (!list.isEmpty()) {
in.put(descriptor, list);
in.put(descriptor, Java11Shim.INSTANCE.copyOf(list));
list.clear();
}
}
return new CachingSemiGraph<>(descriptors, in);
}
private static @NotNull List<IdeaPluginDescriptorImpl> getDirectDependencies(@NotNull IdeaPluginDescriptorImpl rootDescriptor,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap,
boolean withOptional,
boolean hasAllModules,
@NotNull Supplier<IdeaPluginDescriptorImpl> javaDep,
@NotNull Set<IdeaPluginDescriptorImpl> uniqueCheck) {
List<PluginDependency> dependencies = rootDescriptor.pluginDependencies;
private static void getDirectDependencies(@NotNull IdeaPluginDescriptorImpl rootDescriptor,
@NotNull PluginSet pluginSet,
boolean withOptional,
boolean hasAllModules,
@NotNull Supplier<IdeaPluginDescriptorImpl> javaDep,
@NotNull Set<IdeaPluginDescriptorImpl> uniqueCheck,
@NotNull List<IdeaPluginDescriptorImpl> result) {
IdeaPluginDescriptorImpl implicitDep = hasAllModules ? getImplicitDependency(rootDescriptor, javaDep) : null;
int capacity = dependencies.size() + rootDescriptor.incompatibilities.size();
if (!withOptional) {
for (PluginDependency dependency : dependencies) {
if (dependency.isOptional()) {
capacity--;
}
}
}
if (capacity == 0) {
return implicitDep == null ? Collections.emptyList() : Collections.singletonList(implicitDep);
}
uniqueCheck.clear();
List<IdeaPluginDescriptorImpl> plugins = new ArrayList<>(capacity + (implicitDep == null ? 0 : 1));
if (implicitDep != null) {
if (rootDescriptor == implicitDep) {
getLogger().error("Plugin " + rootDescriptor + " depends on self");
}
else {
uniqueCheck.add(implicitDep);
plugins.add(implicitDep);
result.add(implicitDep);
}
}
for (PluginDependency dependency : dependencies) {
for (PluginDependency dependency : rootDescriptor.pluginDependencies) {
if (!withOptional && dependency.isOptional()) {
continue;
}
// check for missing optional dependency
IdeaPluginDescriptorImpl dep = idToDescriptorMap.get(dependency.getPluginId());
IdeaPluginDescriptorImpl dep = pluginSet.findEnabledPlugin(dependency.getPluginId());
// if 'dep' refers to a module we need to check the real plugin containing this module only if it's still enabled,
// otherwise the graph will be inconsistent
if (dep == null) {
@@ -642,24 +615,51 @@ public final class PluginManagerCore {
}
}
else if (uniqueCheck.add(dep)) {
plugins.add(dep);
result.add(dep);
}
}
directDependenciesOfModule(rootDescriptor, pluginSet, uniqueCheck, result);
// graph for plugins, not for modules - so, dependency of content must be taken into account
for (PluginContentDescriptor.ModuleItem module : rootDescriptor.content.modules) {
directDependenciesOfModule(module.requireDescriptor(), pluginSet, uniqueCheck, result);
}
for (PluginId moduleId : rootDescriptor.incompatibilities) {
IdeaPluginDescriptorImpl dep = idToDescriptorMap.get(moduleId);
IdeaPluginDescriptorImpl dep = pluginSet.findEnabledPlugin(moduleId);
if (dep != null && uniqueCheck.add(dep)) {
plugins.add(dep);
result.add(dep);
}
}
}
private static void directDependenciesOfModule(@NotNull IdeaPluginDescriptorImpl module,
@NotNull PluginSet pluginSet,
@NotNull Set<IdeaPluginDescriptorImpl> uniqueCheck,
@NotNull List<IdeaPluginDescriptorImpl> result) {
for (ModuleDependenciesDescriptor.ModuleReference item : module.dependencies.modules) {
PluginContentDescriptor.ModuleItem dep = pluginSet.findEnabledModule(item.name);
if (dep != null) {
IdeaPluginDescriptorImpl descriptor = dep.requireDescriptor();
if (uniqueCheck.add(descriptor)) {
result.add(descriptor);
}
}
}
return plugins;
for (ModuleDependenciesDescriptor.PluginItem item : module.dependencies.plugins) {
IdeaPluginDescriptorImpl descriptor = pluginSet.findEnabledPlugin(item.id);
if (descriptor != null && uniqueCheck.add(descriptor)) {
result.add(descriptor);
}
}
}
private static void checkPluginCycles(@NotNull List<IdeaPluginDescriptorImpl> descriptors,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap,
@NotNull PluginSet pluginSet,
@NotNull List<Supplier<@Nls String>> errors) {
CachingSemiGraph<IdeaPluginDescriptorImpl> graph = createPluginIdGraph(descriptors, idToDescriptorMap, true);
CachingSemiGraph<IdeaPluginDescriptorImpl> graph = createPluginIdGraph(descriptors, pluginSet, true);
DFSTBuilder<IdeaPluginDescriptorImpl> builder = new DFSTBuilder<>(GraphGenerator.generate(graph));
if (builder.isAcyclic()) {
return;
@@ -776,52 +776,16 @@ public final class PluginManagerCore {
}
@ApiStatus.Internal
public static @NotNull CompletionStage<List<IdeaPluginDescriptorImpl>> initPlugins(@NotNull ClassLoader coreClassLoader) {
public static @NotNull CompletableFuture<List<IdeaPluginDescriptorImpl>> initPlugins(@NotNull ClassLoader coreClassLoader) {
CompletableFuture<DescriptorListLoadingContext> future = descriptorListFuture;
if (future == null) {
future = CompletableFuture.completedFuture(null);
throw new IllegalStateException("Call scheduleDescriptorLoading() first");
}
return future.thenApply(context -> {
loadAndInitializePlugins(context, coreClassLoader);
return ourLoadedPlugins;
return loadAndInitializePlugins(context, coreClassLoader).loadedPlugins;
});
}
private static @NotNull Map<String, String[]> loadAdditionalLayoutMap() {
Path fileWithLayout = usePluginClassLoader
? Paths.get(PathManager.getSystemPath(), PlatformUtils.getPlatformPrefix() + ".txt")
: null;
if (fileWithLayout == null || !Files.exists(fileWithLayout)) {
return Collections.emptyMap();
}
Map<String, String[]> additionalLayoutMap = new LinkedHashMap<>();
try (BufferedReader bufferedReader = Files.newBufferedReader(fileWithLayout)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
List<String> parameters = ParametersListUtil.parse(line.trim());
if (parameters.size() < 2) {
continue;
}
additionalLayoutMap.put(parameters.get(0), ArrayUtilRt.toStringArray(parameters.subList(1, parameters.size())));
}
}
catch (Exception ignored) {
}
return additionalLayoutMap;
}
/**
* not used by plugin manager - only for dynamic plugin reloading.
* Building plugin graph and using `getInList` as it is done for regular loading is not required - all that magic and checks
* are not required here because only regular plugins maybe dynamically reloaded.
*/
@ApiStatus.Internal
public static @NotNull ClassLoaderConfigurator createClassLoaderConfiguratorForDynamicPlugin(@NotNull IdeaPluginDescriptorImpl pluginDescriptor) {
Map<PluginId, IdeaPluginDescriptorImpl> idMap = buildPluginIdMap(ContainerUtil.concat(getLoadedPlugins(null), Collections.singletonList(pluginDescriptor)));
return new ClassLoaderConfigurator(true, PluginManagerCore.class.getClassLoader(), idMap, ourAdditionalLayoutMap);
}
public static @NotNull BuildNumber getBuildNumber() {
BuildNumber result = ourBuildNumber;
if (result == null) {
@@ -1019,11 +983,12 @@ public final class PluginManagerCore {
}
List<IdeaPluginDescriptorImpl> descriptors = loadingResult.getEnabledPlugins();
PluginSet rawPluginSet = new PluginSet(descriptors, descriptors);
disableIncompatiblePlugins(descriptors, idMap, pluginErrors);
checkPluginCycles(descriptors, idMap, globalErrors);
checkPluginCycles(descriptors, rawPluginSet, globalErrors);
// topological sort based on required dependencies only
IdeaPluginDescriptorImpl[] sortedRequired = getTopologicallySorted(createPluginIdGraph(descriptors, idMap, false));
List<IdeaPluginDescriptorImpl> sortedRequired = getTopologicallySorted(createPluginIdGraph(descriptors, rawPluginSet, false));
Set<PluginId> enabledPluginIds = new LinkedHashSet<>();
Set<PluginId> enabledModuleIds = new LinkedHashSet<>();
@@ -1047,92 +1012,32 @@ public final class PluginManagerCore {
prepareLoadingPluginsErrorMessage(disabledIds, disabledRequiredIds, idMap, pluginErrors, globalErrors);
// topological sort based on all (required and optional) dependencies
CachingSemiGraph<IdeaPluginDescriptorImpl> graph = createPluginIdGraph(Arrays.asList(sortedRequired), idMap, true);
IdeaPluginDescriptorImpl[] sortedAll = getTopologicallySorted(graph);
List<IdeaPluginDescriptorImpl> enabledPlugins = getOnlyEnabledPlugins(sortedAll);
for (IdeaPluginDescriptorImpl plugin : enabledPlugins) {
checkOptionalDescriptors(plugin.pluginDependencies, idMap);
CachingSemiGraph<IdeaPluginDescriptorImpl> graph = createPluginIdGraph(sortedRequired, rawPluginSet, true);
List<IdeaPluginDescriptorImpl> allPlugins = getTopologicallySorted(graph);
List<IdeaPluginDescriptorImpl> enabledPlugins = getOnlyEnabledPlugins(allPlugins);
if (!context.result.incompletePlugins.isEmpty()) {
allPlugins.addAll(context.result.incompletePlugins.values());
}
Map<String, String[]> additionalLayoutMap = loadAdditionalLayoutMap();
ourAdditionalLayoutMap = additionalLayoutMap;
ClassLoaderConfigurator classLoaderConfigurator = new ClassLoaderConfigurator(context.usePluginClassLoader, coreLoader, idMap,
additionalLayoutMap);
enabledPlugins.forEach(classLoaderConfigurator::configure);
PluginSet pluginSet = new PluginSet(allPlugins, enabledPlugins);
ClassLoaderConfigurator classLoaderConfigurator = new ClassLoaderConfigurator(pluginSet, coreLoader, context.usePluginClassLoader);
pluginSet.loadedPlugins.forEach(classLoaderConfigurator::configure);
if (checkEssentialPlugins) {
checkEssentialPluginsAreAvailable(idMap);
}
Set<PluginId> effectiveDisabledIds = disabledIds.isEmpty() ? Collections.emptySet() : new HashSet<>(disabledIds.keySet());
return new PluginManagerState(sortedAll, enabledPlugins, disabledRequiredIds, effectiveDisabledIds, idMap);
Set<PluginId> effectiveDisabledIds = disabledIds.isEmpty() ? Collections.emptySet() : Java11Shim.INSTANCE.copyOf(disabledIds.keySet());
return new PluginManagerState(pluginSet, disabledRequiredIds, effectiveDisabledIds);
}
private static void checkOptionalDescriptors(@NotNull List<PluginDependency> pluginDependencies,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idMap) {
for (PluginDependency dependency : pluginDependencies) {
IdeaPluginDescriptorImpl subDescriptor = dependency.subDescriptor;
if (subDescriptor == null || dependency.isDisabledOrBroken) {
continue;
}
IdeaPluginDescriptorImpl dependencyDescriptor = idMap.get(dependency.getPluginId());
if (dependencyDescriptor == null || !dependencyDescriptor.isEnabled()) {
dependency.isDisabledOrBroken = true;
continue;
}
// check that plugin doesn't depend on unavailable plugin
List<PluginDependency> childDependencies = subDescriptor.pluginDependencies;
if (!checkChildDeps(childDependencies, idMap)) {
dependency.isDisabledOrBroken = true;
}
}
}
// multiple dependency condition is not supported, so,
// jsp-javaee.xml depends on com.intellij.javaee.web, and included file in turn define jsp-css.xml that depends on com.intellij.css
// that's why nesting level is more than one
private static boolean checkChildDeps(@NotNull List<PluginDependency> childDependencies, @NotNull Map<PluginId, IdeaPluginDescriptorImpl> idMap) {
for (PluginDependency dependency : childDependencies) {
if (dependency.isDisabledOrBroken) {
if (dependency.isOptional()) {
continue;
}
return false;
}
IdeaPluginDescriptorImpl dependentDescriptor = idMap.get(dependency.getPluginId());
if (dependentDescriptor == null || !dependentDescriptor.isEnabled()) {
dependency.isDisabledOrBroken = true;
if (dependency.isOptional()) {
continue;
}
return false;
}
if (dependency.subDescriptor != null) {
List<PluginDependency> list = dependency.subDescriptor.pluginDependencies;
if (!checkChildDeps(list, idMap)) {
dependency.isDisabledOrBroken = true;
if (dependency.isOptional()) {
continue;
}
return false;
}
}
}
return true;
}
static @NotNull IdeaPluginDescriptorImpl @NotNull [] getTopologicallySorted(@NotNull InboundSemiGraph<IdeaPluginDescriptorImpl> graph) {
static @NotNull List<IdeaPluginDescriptorImpl> getTopologicallySorted(@NotNull InboundSemiGraph<IdeaPluginDescriptorImpl> graph) {
DFSTBuilder<IdeaPluginDescriptorImpl> requiredOnlyGraph = new DFSTBuilder<>(GraphGenerator.generate(graph));
IdeaPluginDescriptorImpl[] sortedRequired = graph.getNodes().toArray(new IdeaPluginDescriptorImpl[0]);
List<IdeaPluginDescriptorImpl> sortedRequired = new ArrayList<>(graph.getNodes());
Comparator<IdeaPluginDescriptorImpl> comparator = requiredOnlyGraph.comparator();
// there is circular reference between core and implementation-detail plugin, as not all such plugins extracted from core,
// so, ensure that core plugin is always first (otherwise not possible to register actions - parent group not defined)
Arrays.sort(sortedRequired, (o1, o2) -> {
sortedRequired.sort((o1, o2) -> {
return CORE_ID.equals(o1.getPluginId()) ? -1 : CORE_ID.equals(o2.getPluginId()) ? 1 : comparator.compare(o1, o2);
});
return sortedRequired;
@@ -1280,38 +1185,23 @@ public final class PluginManagerCore {
}
@SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
private static synchronized void loadAndInitializePlugins(@Nullable DescriptorListLoadingContext context, @Nullable ClassLoader coreLoader) {
if (coreLoader == null) {
Class<?> callerClass = ReflectionUtil.findCallerClass(1);
assert callerClass != null;
coreLoader = callerClass.getClassLoader();
}
private static synchronized @NotNull PluginSet loadAndInitializePlugins(@NotNull DescriptorListLoadingContext context,
@NotNull ClassLoader coreLoader) {
try {
if (context == null) {
//noinspection resource
context = PluginDescriptorLoader.loadDescriptors(isUnitTestMode, isRunningFromSources());
}
Activity activity = StartUpMeasurer.startActivity("plugin initialization", ActivityCategory.DEFAULT);
PluginManagerState initResult = initializePlugins(context, coreLoader, !isUnitTestMode);
ourPlugins = initResult.sortedPlugins;
PluginLoadingResult result = context.result;
if (!result.incompletePlugins.isEmpty()) {
int oldSize = initResult.sortedPlugins.length;
IdeaPluginDescriptorImpl[] all = Arrays.copyOf(initResult.sortedPlugins, oldSize + result.incompletePlugins.size());
ArrayUtil.copy(result.incompletePlugins.values(), all, oldSize);
ourPlugins = all;
}
ourPluginsToDisable = initResult.effectiveDisabledIds;
ourPluginsToEnable = initResult.disabledRequiredIds;
ourLoadedPlugins = initResult.sortedEnabledPlugins;
ourShadowedBundledPlugins = result.shadowedBundledIds;
activity.end();
activity.setDescription("plugin count: " + ourLoadedPlugins.size());
logPlugins(initResult.sortedPlugins, result.incompletePlugins.values());
activity.setDescription("plugin count: " + initResult.pluginSet.loadedPlugins.size());
logPlugins(initResult.pluginSet.allPlugins, result.incompletePlugins.values());
pluginSet = initResult.pluginSet;
return initResult.pluginSet;
}
catch (RuntimeException e) {
getLogger().error(e);
@@ -1348,7 +1238,7 @@ public final class PluginManagerCore {
}
public static @Nullable IdeaPluginDescriptor findPluginByModuleDependency(@NotNull PluginId id) {
for (IdeaPluginDescriptorImpl descriptor : ourPlugins) {
for (IdeaPluginDescriptorImpl descriptor : getPluginSet().allPlugins) {
if (descriptor.modules.contains(id)) {
return descriptor;
}
@@ -1363,7 +1253,7 @@ public final class PluginManagerCore {
@ApiStatus.Internal
public static @NotNull Map<PluginId, IdeaPluginDescriptorImpl> buildPluginIdMap() {
LoadingState.COMPONENTS_REGISTERED.checkOccurred();
return buildPluginIdMap(Arrays.asList(ourPlugins));
return buildPluginIdMap(Objects.requireNonNull(pluginSet).allPlugins);
}
/**
@@ -1430,8 +1320,8 @@ public final class PluginManagerCore {
return true;
}
private static @NotNull List<IdeaPluginDescriptorImpl> getOnlyEnabledPlugins(@NotNull IdeaPluginDescriptorImpl @NotNull[] sortedAll) {
List<IdeaPluginDescriptorImpl> enabledPlugins = new ArrayList<>(sortedAll.length);
private static @NotNull List<IdeaPluginDescriptorImpl> getOnlyEnabledPlugins(@NotNull List<IdeaPluginDescriptorImpl> sortedAll) {
List<IdeaPluginDescriptorImpl> enabledPlugins = new ArrayList<>(sortedAll.size());
for (IdeaPluginDescriptorImpl descriptor : sortedAll) {
if (descriptor.isEnabled()) {
enabledPlugins.add(descriptor);

View File

@@ -1,30 +1,22 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins;
import com.intellij.openapi.extensions.PluginId;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
import java.util.Set;
// todo merge into PluginSetState?
public final class PluginManagerState {
final Set<PluginId> effectiveDisabledIds;
final Set<PluginId> disabledRequiredIds;
final IdeaPluginDescriptorImpl @NotNull [] sortedPlugins;
final List<IdeaPluginDescriptorImpl> sortedEnabledPlugins;
final PluginSet pluginSet;
final Map<PluginId, IdeaPluginDescriptorImpl> idMap;
PluginManagerState(@NotNull IdeaPluginDescriptorImpl @NotNull [] sortedPlugins,
@NotNull List<IdeaPluginDescriptorImpl> sortedEnabledPlugins,
PluginManagerState(@NotNull PluginSet pluginSet,
@NotNull Set<PluginId> disabledRequiredIds,
@NotNull Set<PluginId> effectiveDisabledIds,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> idMap) {
this.sortedPlugins = sortedPlugins;
this.sortedEnabledPlugins = sortedEnabledPlugins;
@NotNull Set<PluginId> effectiveDisabledIds) {
this.pluginSet = pluginSet;
this.disabledRequiredIds = disabledRequiredIds;
this.effectiveDisabledIds = effectiveDisabledIds;
this.idMap = idMap;
}
}

View File

@@ -0,0 +1,75 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.intellij.openapi.extensions.PluginId
import com.intellij.util.lang.Java11Shim
import org.jetbrains.annotations.ApiStatus
// if otherwise not specified, `module` in terms of v2 plugin model
@ApiStatus.Internal
class PluginSet(
@JvmField val allPlugins: List<IdeaPluginDescriptorImpl>,
loadedPlugins: List<IdeaPluginDescriptorImpl>,
) {
@JvmField val loadedPlugins: List<IdeaPluginDescriptorImpl> = Java11Shim.INSTANCE.copyOf(loadedPlugins)
private val enabledPluginAndV1ModuleMap: Map<PluginId, IdeaPluginDescriptorImpl>
//private val allPluginAndV1ModuleMap: Map<PluginId, IdeaPluginDescriptorImpl>
// module map in a new v2 format
private val moduleMap: Map<String, PluginContentDescriptor.ModuleItem>
init {
val enabledPluginAndV1ModuleMap = HashMap<PluginId, IdeaPluginDescriptorImpl>(loadedPlugins.size)
//val allPluginAndV1ModuleMap = HashMap<PluginId, IdeaPluginDescriptorImpl>(allPlugins.size)
val moduleMap = HashMap<String, PluginContentDescriptor.ModuleItem>()
for (descriptor in loadedPlugins) {
addWithV1Modules(enabledPluginAndV1ModuleMap, descriptor)
for (module in descriptor.content.modules) {
if (module.configFile == null) {
val duplicate = moduleMap.putIfAbsent(module.name, module)
if (duplicate != null) {
throw RuntimeException("Duplicated module name (first=$duplicate, second=$module)")
}
}
}
if (descriptor.packagePrefix != null) {
val pluginAsModuleItem = PluginContentDescriptor.ModuleItem(name = descriptor.id.idString, configFile = null)
pluginAsModuleItem.descriptor = descriptor
moduleMap.put(pluginAsModuleItem.name, pluginAsModuleItem)
}
}
//for (descriptor in allPlugins) {
// addWithV1Modules(allPluginAndV1ModuleMap, descriptor)
//}
val java11Shim = Java11Shim.INSTANCE
this.enabledPluginAndV1ModuleMap = java11Shim.copyOf(enabledPluginAndV1ModuleMap)
//this.allPluginAndV1ModuleMap = java11Shim.copyOf(allPluginAndV1ModuleMap)
this.moduleMap = java11Shim.copyOf(moduleMap)
}
fun isPluginEnabled(id: PluginId): Boolean = enabledPluginAndV1ModuleMap.containsKey(id)
fun findEnabledPlugin(id: PluginId): IdeaPluginDescriptorImpl? = enabledPluginAndV1ModuleMap.get(id)
//fun findPlugin(id: PluginId): IdeaPluginDescriptorImpl? = allPluginAndV1ModuleMap.get(id)
// module in term of v2 model
fun findEnabledModule(id: String): PluginContentDescriptor.ModuleItem? = moduleMap.get(id)
fun concat(descriptor: IdeaPluginDescriptorImpl): PluginSet {
return PluginSet(allPlugins = allPlugins.plus(descriptor),
loadedPlugins = Java11Shim.INSTANCE.copyOf(loadedPlugins.plus(descriptor)))
}
private fun addWithV1Modules(result: MutableMap<PluginId, IdeaPluginDescriptorImpl>, descriptor: IdeaPluginDescriptorImpl) {
result.put(descriptor.id, descriptor)
for (module in descriptor.modules) {
result.put(module, descriptor)
}
}
}

View File

@@ -136,6 +136,21 @@ class PluginXmlPathResolver(private val pluginJarFiles: List<Path>) : PathResolv
return null
}
override fun resolveModuleFile(readContext: ReadModuleContext,
dataLoader: DataLoader,
path: String,
readInto: RawPluginDescriptor?): RawPluginDescriptor {
val input = dataLoader.load(path)
?: throw RuntimeException("Cannot resolve $path (dataLoader=$dataLoader, pluginJarFiles=${pluginJarFiles.joinToString(separator = "\n ")})")
return readModuleDescriptor(input = input,
readContext = readContext,
pathResolver = this,
dataLoader = dataLoader,
includeBase = null,
readInto = readInto,
locationSource = null)
}
private fun findInJarFiles(readInto: RawPluginDescriptor,
readContext: ReadModuleContext,
dataLoader: DataLoader,

View File

@@ -52,8 +52,8 @@ class RawPluginDescriptor {
@JvmField var epNameToExtensions: MutableMap<String, MutableList<ExtensionDescriptor>>? = null
@JvmField internal var contentDescriptor = PluginContentDescriptor.EMPTY
@JvmField internal var dependencyDescriptor = ModuleDependenciesDescriptor.EMPTY
@JvmField internal var content = PluginContentDescriptor.EMPTY
@JvmField internal var dependencies = ModuleDependenciesDescriptor.EMPTY
class ActionDescriptor(
@JvmField val name: String,

View File

@@ -1,55 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins;
import com.intellij.ide.plugins.cl.PluginClassLoader;
import com.intellij.util.lang.ClassPath;
import com.intellij.util.lang.UrlClassLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
final class SubPluginClassLoader extends PluginClassLoader {
private final String[] packagePrefixes;
static {
registerAsParallelCapable();
}
SubPluginClassLoader(@NotNull IdeaPluginDescriptorImpl pluginDescriptor,
@NotNull UrlClassLoader.Builder urlClassLoaderBuilder,
@NotNull ClassLoader @NotNull [] parents,
@NotNull String @NotNull [] packagePrefixes,
@NotNull ClassLoader coreLoader,
@Nullable ClassPath.ResourceFileFactory resourceFileFactory) {
super(urlClassLoaderBuilder, parents, pluginDescriptor, pluginDescriptor.getPluginPath(), coreLoader, null, null, resourceFileFactory);
assert pluginDescriptor.packagePrefix == null;
this.packagePrefixes = packagePrefixes;
}
@Override
public @Nullable Class<?> loadClassInsideSelf(@NotNull String name, boolean forceLoadFromSubPluginClassloader) throws IOException {
if (forceLoadFromSubPluginClassloader) {
return super.loadClassInsideSelf(name, true);
}
for (String packagePrefix : packagePrefixes) {
if (name.startsWith(packagePrefix)) {
return super.loadClassInsideSelf(name, true);
}
}
int subIndex = name.indexOf('$');
if (subIndex > 0) {
// load inner classes
// we check findLoadedClass because classNames doesn't have full set of suitable names - PluginAwareClassLoader.SubClassLoader is used to force loading classes from sub classloader
Class<?> loadedClass = findLoadedClass(name.substring(0, subIndex));
if (loadedClass != null && loadedClass.getClassLoader() == this) {
return super.loadClassInsideSelf(name, true);
}
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:JvmName("XmlReader")
@file:Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty")
package com.intellij.ide.plugins
@@ -14,6 +14,7 @@ import com.intellij.openapi.util.createNonCoalescingXmlStreamReader
import com.intellij.platform.util.plugins.DataLoader
import com.intellij.util.NoOpXmlInterner
import com.intellij.util.XmlInterner
import com.intellij.util.lang.Java11Shim
import com.intellij.util.lang.ZipFilePool
import com.intellij.util.messages.ListenerDescriptor
import com.intellij.util.readXmlAsModel
@@ -31,6 +32,8 @@ import javax.xml.stream.XMLStreamException
import javax.xml.stream.XMLStreamReader
import javax.xml.stream.events.XMLEvent
private const val defaultXPointerValue = "xpointer(/idea-plugin/*)"
fun readModuleDescriptor(inputStream: InputStream,
readContext: ReadModuleContext,
pathResolver: PathResolver,
@@ -149,6 +152,7 @@ private fun readRootAttributes(reader: XMLStreamReader2, descriptor: RawPluginDe
}
}
private fun readRootElementChild(reader: XMLStreamReader2,
descriptor: RawPluginDescriptor,
localName: String,
@@ -247,8 +251,10 @@ private fun readRootElementChild(reader: XMLStreamReader2,
dataLoader = dataLoader,
includeBase = includeBase)
"content" -> readContent(reader, descriptor)
"dependencies" -> readDependencies(reader, descriptor)
"content" -> readContent(reader = reader,
descriptor = descriptor,
readContext = readContext)
"dependencies" -> readDependencies(reader = reader, descriptor = descriptor, readContext = readContext)
"depends" -> readOldDepends(reader, descriptor)
@@ -260,7 +266,7 @@ private fun readRootElementChild(reader: XMLStreamReader2,
pathResolver = pathResolver,
dataLoader = dataLoader,
includeBase = includeBase,
allowedPointer = "xpointer(/idea-plugin/*)")
allowedPointer = defaultXPointerValue)
"helpset" -> {
// deprecated and not used element
reader.skipElement()
@@ -311,7 +317,7 @@ private fun readOldDepends(reader: XMLStreamReader2, descriptor: RawPluginDescri
for (i in 0 until reader.attributeCount) {
when (reader.getAttributeLocalName(i)) {
"optional" -> isOptional = reader.getAttributeAsBoolean(i)
"config-file" -> configFile = getNullifiedAttributeValue(reader, i)
"config-file" -> configFile = reader.getAttributeValue(i)
}
}
@@ -321,8 +327,7 @@ private fun readOldDepends(reader: XMLStreamReader2, descriptor: RawPluginDescri
depends = ArrayList()
descriptor.depends = depends
}
depends.add(PluginDependency(pluginId = PluginId.getId(dependencyIdString), configFile = configFile, isOptional = isOptional,
isDisabledOrBroken = false))
depends.add(PluginDependency(pluginId = PluginId.getId(dependencyIdString), configFile = configFile, isOptional = isOptional))
}
private fun readExtensions(reader: XMLStreamReader2, descriptor: RawPluginDescriptor, interner: XmlInterner) {
@@ -638,64 +643,69 @@ private fun readComponents(reader: XMLStreamReader2, containerDescriptor: Contai
}
}
private fun readContent(reader: XMLStreamReader2, descriptor: RawPluginDescriptor) {
private fun readContent(reader: XMLStreamReader2,
descriptor: RawPluginDescriptor,
readContext: ReadModuleContext) {
val items = ArrayList<PluginContentDescriptor.ModuleItem>()
reader.consumeChildElements { elementName ->
when (elementName) {
"module" -> {
var name: String? = null
var packageName: String? = null
var configFile: String? = null
for (i in 0 until reader.attributeCount) {
when (reader.getAttributeLocalName(i)) {
"name" -> name = getNullifiedAttributeValue(reader, i)
"package" -> packageName = getNullifiedAttributeValue(reader, i)
"configFile" -> configFile = getNullifiedAttributeValue(reader, i)
"name" -> name = readContext.interner.name(reader.getAttributeValue(i))
}
}
items.add(PluginContentDescriptor.ModuleItem(name = name ?: throw RuntimeException("Name is not specified at ${reader.location}"),
packageName = packageName,
if (name.isNullOrEmpty()) {
throw RuntimeException("Name is not specified at ${reader.location}")
}
var configFile: String? = null
val index = name.lastIndexOf('/')
if (index != -1) {
configFile = "${name.substring(0, index)}.${name.substring(index + 1)}.xml"
}
items.add(PluginContentDescriptor.ModuleItem(name = name,
configFile = configFile))
}
else -> throw RuntimeException("Unknown content item type: ${elementName}")
else -> throw RuntimeException("Unknown content item type: $elementName")
}
reader.skipElement()
}
descriptor.contentDescriptor = PluginContentDescriptor(items)
descriptor.content = PluginContentDescriptor(Java11Shim.INSTANCE.copyOf(items))
assert(reader.isEndElement)
}
private fun readDependencies(reader: XMLStreamReader2, descriptor: RawPluginDescriptor) {
var modules: MutableList<ModuleDependenciesDescriptor.ModuleItem>? = null
private fun readDependencies(reader: XMLStreamReader2, descriptor: RawPluginDescriptor, readContext: ReadModuleContext) {
var modules: MutableList<ModuleDependenciesDescriptor.ModuleReference>? = null
var plugins: MutableList<ModuleDependenciesDescriptor.PluginItem>? = null
reader.consumeChildElements { elementName ->
when (elementName) {
"module" -> {
var name: String? = null
var packageName: String? = null
for (i in 0 until reader.attributeCount) {
when (reader.getAttributeLocalName(i)) {
"name" -> name = getNullifiedAttributeValue(reader, i)
"package" -> packageName = getNullifiedAttributeValue(reader, i)
"name" -> name = readContext.interner.name(reader.getAttributeValue(i))
}
}
if (modules == null) {
modules = mutableListOf()
modules = ArrayList()
}
modules!!.add(ModuleDependenciesDescriptor.ModuleItem(name!!, packageName))
modules!!.add(ModuleDependenciesDescriptor.ModuleReference(name!!))
}
"plugin" -> {
var id: String? = null
for (i in 0 until reader.attributeCount) {
when (reader.getAttributeLocalName(i)) {
"id" -> id = getNullifiedAttributeValue(reader, i)
"id" -> id = readContext.interner.name(reader.getAttributeValue(i))
}
}
if (plugins == null) {
plugins = mutableListOf()
plugins = ArrayList()
}
plugins!!.add(ModuleDependenciesDescriptor.PluginItem(PluginId.getId(id!!)))
}
@@ -703,7 +713,7 @@ private fun readDependencies(reader: XMLStreamReader2, descriptor: RawPluginDesc
}
reader.skipElement()
}
descriptor.dependencyDescriptor = ModuleDependenciesDescriptor(modules ?: emptyList(), plugins ?: emptyList())
descriptor.dependencies = ModuleDependenciesDescriptor(modules ?: Collections.emptyList(), plugins ?: Collections.emptyList())
assert(reader.isEndElement)
}
@@ -716,11 +726,9 @@ private fun findAttributeValue(reader: XMLStreamReader2, name: String): String?
return null
}
private fun getNullifiedContent(reader: XMLStreamReader2): String? {
return reader.elementText.takeIf { it.isNotEmpty() }
}
private fun getNullifiedContent(reader: XMLStreamReader2): String? = reader.elementText.takeIf { !it.isEmpty() }
private fun getNullifiedAttributeValue(reader: XMLStreamReader2, i: Int) = reader.getAttributeValue(i).takeIf { it.isNotEmpty() }
private fun getNullifiedAttributeValue(reader: XMLStreamReader2, i: Int) = reader.getAttributeValue(i).takeIf { !it.isEmpty() }
interface ReadModuleContext {
val interner: XmlInterner

View File

@@ -86,6 +86,10 @@ public class PluginClassLoader extends UrlClassLoader implements PluginAwareClas
String debugFilePath = System.getProperty("plugin.classloader.debug", "");
if (!debugFilePath.isEmpty()) {
try {
if (debugFilePath.startsWith("~/") || debugFilePath.startsWith("~\\")) {
debugFilePath = System.getProperty("user.home") + debugFilePath.substring(1);
}
logStreamCandidate = Files.newBufferedWriter(Paths.get(debugFilePath));
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
@Override

View File

@@ -1 +0,0 @@
Please refer to [Storing Sensitive Data](https://plugins.jetbrains.com/docs/intellij/persisting-sensitive-data.html)

View File

@@ -17,6 +17,7 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.ExtensionNotApplicableException
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.SystemProperties
import com.intellij.util.concurrency.NonUrgentExecutor
import com.intellij.util.io.jackson.IntelliJPrettyPrinter
@@ -145,7 +146,7 @@ class StartUpPerformanceReporter : StartupActivity, StartUpPerformanceService {
val classReport = System.getProperty("idea.log.class.list.file")
if (!classReport.isNullOrBlank()) {
generateJarAccessLog(Path.of(classReport))
generateJarAccessLog(Path.of(FileUtil.expandUserHome(classReport)))
}
return StartUpPerformanceReporterValues(pluginCostMap, currentReport, w.publicStatMetrics)
}

View File

@@ -1,165 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="956px" preserveAspectRatio="none" style="width:1184px;height:956px;" version="1.1" viewBox="0 0 1184 956" width="1184px" zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.process {
stroke: #383838; stroke-width: 1.0;
fill: #F8F8F8;
}
.note {
stroke: #383838; stroke-width: 1.0;
fill: #ECECEC;
}
</style>
<defs>
<filter height="300%" id="f1l0swyqxm156j" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<path class="note" d="M697.25,10 L697.25,43.8125 L677.25,47.8125 L697.25,51.8125 L697.25,85.625 A0,0 0 0 0 697.25,85.625 L994.25,85.625 A0,0 0 0 0 994.25,85.625 L994.25,20 L984.25,10 L697.25,10 A0,0 0 0 0 697.25,10 " filter="url(#f1l0swyqxm156j)"/>
<path class="note" d="M984.25,10 L984.25,20 L994.25,20 L984.25,10 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="86" x="703.25" y="27.9883">In any thread.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="128" x="703.25" y="44.3945">Get on demand only.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="123" x="703.25" y="60.8008">Do not cache result.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="276" x="703.25" y="77.207">Do not request in constructor unless needed.</text>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="36.4063" rx="12.5" ry="12.5" width="84" x="593.25" y="29.6094"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="64" x="603.25" y="52.5977">getService</text>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="36.4883" rx="12.5" ry="12.5" width="91" x="589.75" y="206.0313"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="42" x="599.75" y="229.1016">Return</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="26" x="644.75" y="229.5664">null</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="547.75,155.8281,722.75,155.8281,734.75,167.8281,722.75,179.8281,547.75,179.8281,535.75,167.8281,547.75,155.8281" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="639.25" y="192.8164">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="11" x="547.75" y="172.6133">Is</text>
<a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" target="_top" title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" xlink:actuate="onRequest" xlink:href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" xlink:show="new" xlink:title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#how-to-declare-a-service" xlink:type="simple">
<text fill="#1C1C1C" font-family="Roboto" font-size="14" lengthAdjust="spacingAndGlyphs" text-decoration="underline" textLength="118" x="561.75" y="172.6133">Service Declaration</text>
</a>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="40" x="682.75" y="172.6133">Found</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="734.75" y="164.4102">yes</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="588.75,105.625,681.75,105.625,693.75,117.625,681.75,129.625,588.75,129.625,576.75,117.625,588.75,105.625" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="639.25" y="142.6133">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="11" x="588.75" y="122.4102">Is</text>
<a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" target="_top" title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" xlink:actuate="onRequest" xlink:href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" xlink:show="new" xlink:title="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html?search=ser#light-service" xlink:type="simple">
<text fill="#1C1C1C" font-family="Roboto" font-size="14" lengthAdjust="spacingAndGlyphs" text-decoration="underline" textLength="79" x="602.75" y="122.4102">Light Service</text>
</a>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="693.75" y="114.207">yes</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="635.25,284.5195,647.25,296.5195,635.25,308.5195,623.25,296.5195,635.25,284.5195" style="stroke: #383838; stroke-width: 1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="573.75,378.7227,696.75,378.7227,708.75,390.7227,696.75,402.7227,573.75,402.7227,561.75,390.7227,573.75,378.7227" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="123" x="573.75" y="395.5078">Is Container Active?</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="37" x="524.75" y="387.3047">active</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="195" x="708.75" y="387.3047">disposed or dispose in progress</text>
<rect fill="#FFFFFF" filter="url(#f1l0swyqxm156j)" height="426.5352" style="stroke: #000000; stroke-width: 1.5;" width="867" x="11" y="412.7227"/>
<path d="M204,412.7227 L204,422.1289 L194,432.1289 L11,432.1289 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="183" x="14" y="426.7109">synchronized on service class</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="207.5,499.332,291.5,499.332,303.5,511.332,291.5,523.332,207.5,523.332,195.5,511.332,207.5,499.332" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="84" x="207.5" y="516.1172">Is Initializing?</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="174.5" y="507.9141">yes</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="303.5" y="507.9141">no</text>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="52.8945" rx="12.5" ry="12.5" width="182" x="23" y="533.332"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="40" x="37" y="556.4023">Throw</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="111" x="80" y="556.8672">PluginException</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="162" x="33" y="572.8086">Cyclic Service Initialization</text>
<rect fill="#FFFFFF" filter="url(#f1l0swyqxm156j)" height="249.9258" style="stroke: #000000; stroke-width: 1.5;" width="611" x="225" y="533.332"/>
<path d="M328,533.332 L328,542.7383 L318,552.7383 L225,552.7383 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="93" x="228" y="547.3203">non cancelable</text>
<path class="note" d="M464,569.7383 L464,587.1445 L444,591.1445 L464,595.1445 L464,612.5508 A0,0 0 0 0 464,612.5508 L826,612.5508 A0,0 0 0 0 826,612.5508 L826,579.7383 L816,569.7383 L464,569.7383 A0,0 0 0 0 464,569.7383 " filter="url(#f1l0swyqxm156j)"/>
<path class="note" d="M816,569.7383 L816,579.7383 L826,579.7383 L816,569.7383 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="341" x="470" y="587.7266">Avoid getting other services to reduce initialization tree.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="305" x="470" y="604.1328">As less dependencies, as more faster and reliable.</text>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="36.4063" width="118" x="326" y="572.9414"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="98" x="336" y="595.9297">Create Instance</text>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="52.8945" width="300" x="235" y="645.4688"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="280" x="245" y="668.457">Register to be Disposed on Container Dispose</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="84" x="304" y="684.9453">if Implements</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="75" x="391" y="685.4102">Disposable</text>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="52.8945" width="289" x="240.5" y="718.3633"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="134" x="318" y="741.3516">Load Persistent State</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="84" x="250.5" y="757.8398">if Implements</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="182" x="337.5" y="758.3047">PersistentStateComponent</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="170,449.1289,329,449.1289,341,461.1289,329,473.1289,170,473.1289,158,461.1289,170,449.1289" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="253.5" y="486.1172">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="159" x="170" y="465.9141">Is Created and Initialized?</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="341" y="457.7109">yes</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="249.5,803.2578,261.5,815.2578,249.5,827.2578,237.5,815.2578,249.5,803.2578" style="stroke: #383838; stroke-width: 1.0;"/>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="36.4883" rx="12.5" ry="12.5" width="246" x="898" y="412.7227"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="40" x="908" y="435.793">Throw</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="183" x="951" y="436.2578">ProcessCanceledException</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="555.75,328.5195,714.75,328.5195,726.75,340.5195,714.75,352.5195,555.75,352.5195,543.75,340.5195,555.75,328.5195" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="639.25" y="365.5078">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="159" x="555.75" y="345.3047">Is Created and Initialized?</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="726.75" y="337.1016">yes</text>
<polygon fill="#F8F8F8" filter="url(#f1l0swyqxm156j)" points="635.25,859.2578,647.25,871.2578,635.25,883.2578,623.25,871.2578,635.25,859.2578" style="stroke: #383838; stroke-width: 1.0;"/>
<rect class="process" filter="url(#f1l0swyqxm156j)" height="36.4063" rx="12.5" ry="12.5" width="119" x="575.75" y="903.2578"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="99" x="585.75" y="926.2461">Return Instance</text>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="179.8281" y2="206.0313"/>
<polygon fill="#383838" points="631.25,196.0313,635.25,206.0313,639.25,196.0313,635.25,200.0313" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="734.75" x2="746.75" y1="167.8281" y2="167.8281"/>
<polygon fill="#383838" points="742.75,211.2754,746.75,221.2754,750.75,211.2754,746.75,215.2754" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="746.75" x2="746.75" y1="167.8281" y2="264.5195"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="746.75" x2="635.25" y1="264.5195" y2="264.5195"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="264.5195" y2="284.5195"/>
<polygon fill="#383838" points="631.25,274.5195,635.25,284.5195,639.25,274.5195,635.25,278.5195" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="129.625" y2="155.8281"/>
<polygon fill="#383838" points="631.25,145.8281,635.25,155.8281,639.25,145.8281,635.25,149.8281" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="693.75" x2="756.75" y1="117.625" y2="117.625"/>
<polygon fill="#383838" points="752.75,205.2754,756.75,215.2754,760.75,205.2754,756.75,209.2754" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="756.75" x2="756.75" y1="117.625" y2="296.5195"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="756.75" x2="647.25" y1="296.5195" y2="296.5195"/>
<polygon fill="#383838" points="657.25,292.5195,647.25,296.5195,657.25,300.5195,653.25,296.5195" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="66.0156" y2="105.625"/>
<polygon fill="#383838" points="631.25,95.625,635.25,105.625,639.25,95.625,635.25,99.625" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="385" x2="385" y1="609.3477" y2="645.4688"/>
<polygon fill="#383838" points="381,635.4688,385,645.4688,389,635.4688,385,639.4688" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="385" x2="385" y1="698.3633" y2="718.3633"/>
<polygon fill="#383838" points="381,708.3633,385,718.3633,389,708.3633,385,712.3633" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="195.5" x2="114" y1="511.332" y2="511.332"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="114" x2="114" y1="511.332" y2="533.332"/>
<polygon fill="#383838" points="110,523.332,114,533.332,118,523.332,114,527.332" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="303.5" x2="385" y1="511.332" y2="511.332"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="385" x2="385" y1="511.332" y2="572.9414"/>
<polygon fill="#383838" points="381,562.9414,385,572.9414,389,562.9414,385,566.9414" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="385" x2="385" y1="771.2578" y2="788.2578"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="385" x2="249.5" y1="788.2578" y2="788.2578"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="249.5" x2="249.5" y1="788.2578" y2="803.2578"/>
<polygon fill="#383838" points="245.5,793.2578,249.5,803.2578,253.5,793.2578,249.5,797.2578" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="249.5" x2="249.5" y1="473.1289" y2="499.332"/>
<polygon fill="#383838" points="245.5,489.332,249.5,499.332,253.5,489.332,249.5,493.332" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="341" x2="856" y1="461.1289" y2="461.1289"/>
<polygon fill="#383838" points="852,644.7539,856,654.7539,860,644.7539,856,648.7539" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="856" x2="856" y1="461.1289" y2="815.2578"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="856" x2="261.5" y1="815.2578" y2="815.2578"/>
<polygon fill="#383838" points="271.5,811.2578,261.5,815.2578,271.5,819.2578,267.5,815.2578" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="561.75" x2="249.5" y1="390.7227" y2="390.7227"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="249.5" x2="249.5" y1="390.7227" y2="449.1289"/>
<polygon fill="#383838" points="245.5,439.1289,249.5,449.1289,253.5,439.1289,249.5,443.1289" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="708.75" x2="1021" y1="390.7227" y2="390.7227"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="1021" x2="1021" y1="390.7227" y2="412.7227"/>
<polygon fill="#383838" points="1017,402.7227,1021,412.7227,1025,402.7227,1021,406.7227" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="249.5" x2="249.5" y1="827.2578" y2="844.2578"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="249.5" x2="635.25" y1="844.2578" y2="844.2578"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="844.2578" y2="859.2578"/>
<polygon fill="#383838" points="631.25,849.2578,635.25,859.2578,639.25,849.2578,635.25,853.2578" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="352.5195" y2="378.7227"/>
<polygon fill="#383838" points="631.25,368.7227,635.25,378.7227,639.25,368.7227,635.25,372.7227" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="726.75" x2="1158" y1="340.5195" y2="340.5195"/>
<polygon fill="#383838" points="1154,622.5508,1158,632.5508,1162,622.5508,1158,626.5508" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="1158" x2="1158" y1="340.5195" y2="871.2578"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="1158" x2="647.25" y1="871.2578" y2="871.2578"/>
<polygon fill="#383838" points="657.25,867.2578,647.25,871.2578,657.25,875.2578,653.25,871.2578" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="308.5195" y2="328.5195"/>
<polygon fill="#383838" points="631.25,318.5195,635.25,328.5195,639.25,318.5195,635.25,322.5195" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="635.25" x2="635.25" y1="883.2578" y2="903.2578"/>
<polygon fill="#383838" points="631.25,893.2578,635.25,903.2578,639.25,893.2578,635.25,897.2578" style="stroke: #383838; stroke-width: 1.0;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,186 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" contentScriptType="application/ecmascript" contentStyleType="text/css" height="969px"
preserveAspectRatio="none" style="width:919px;height:969px;" version="1.1" viewBox="0 0 919 969" width="919px" zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.process {
stroke: #383838; stroke-width: 1.0;
fill: #F8F8F8;
}
.note {
stroke: #383838; stroke-width: 1.0;
fill: #ECECEC;
}
</style>
<defs>
<filter height="300%" id="f184fee447dtf9" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<path class="note" d="M359,15.959 L359,25.2031 L339,29.2031 L359,33.2031 L359,42.4473 A0,0 0 0 0 359,42.4473 L670,42.4473 A0,0 0 0 0 670,42.4473 L670,25.959 L660,15.959 L359,15.959 A0,0 0 0 0 359,15.959 " filter="url(#f184fee447dtf9)"/>
<path class="note" d="M660,15.959 L660,25.959 L670,25.959 L660,15.959 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="151" x="365" y="34.0293">Externally called method</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="136" x="519" y="34.4941">IconLoader.findIcon</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" rx="12.5" ry="12.5" width="74" x="265" y="11"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="54" x="275" y="33.9883">find-icon</text>
<path class="note" d="M374.5,64.1211 L374.5,81.6094 L354.5,85.6094 L374.5,89.6094 L374.5,107.0977 A0,0 0 0 0 374.5,107.0977 L907.5,107.0977 A0,0 0 0 0 907.5,107.0977 L907.5,74.1211 L897.5,64.1211 L374.5,64.1211 A0,0 0 0 0 374.5,64.1211 " filter="url(#f184fee447dtf9)"/>
<path class="note" d="M897.5,64.1211 L897.5,74.1211 L907.5,74.1211 L897.5,64.1211 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="148" x="380.5" y="82.1914">Internally called method</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="198" x="531.5" y="82.6563">CachedImageIcon.loadImage</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="4" x="729.5" y="82.1914">.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="164" x="380.5" y="98.6797">Called numerous times per</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="122" x="547.5" y="99.1445">CachedImageIcon</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="220" x="672.5" y="98.6797">instance — to compute a new scale.</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" rx="12.5" ry="12.5" width="105" x="249.5" y="67.4063"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="85" x="259.5" y="90.3945">find-icon-load</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="281.5,127.0977,322.5,127.0977,334.5,139.0977,322.5,151.0977,281.5,151.0977,269.5,139.0977,281.5,127.0977" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="41" x="281.5" y="143.8828">Is SVG</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="248.5" y="135.6797">yes</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="334.5" y="135.6797">no</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="73" x="218.5" y="161.0977"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="53" x="228.5" y="184.0859">svg-load</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="75" x="311.5" y="161.0977"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="55" x="321.5" y="184.0859">png-load</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="302,203.5039,314,215.5039,302,227.5039,290,215.5039,302,203.5039" style="stroke: #383838; stroke-width: 1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="222,297.707,382,297.707,394,309.707,382,321.707,222,321.707,210,309.707,222,297.707" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="160" x="222" y="314.4922">Is resourceClass Provided</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="189" y="306.2891">yes</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="394" y="306.2891">no</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="139" x="100.5" y="331.707"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="119" x="110.5" y="354.6953">load-from-resource</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="85,388.1133,255,388.1133,267,400.1133,255,412.1133,85,412.1133,73,400.1133,85,388.1133" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="170" x="85" y="404.8984">Is prebuiltCacheId Provided</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="52" y="396.6953">yes</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="93" x="16.5" y="422.1133"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="73" x="26.5" y="445.1016">svg-prebuilt</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" rx="12.5" ry="12.5" width="104" x="11" y="493.5195"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="84" x="21" y="516.5078">Return Image</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4883" rx="12.5" ry="12.5" width="91" x="231.5" y="472.3164"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="42" x="241.5" y="495.3867">Return</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="26" x="286.5" y="495.8516">null</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="224.5,422.1133,329.5,422.1133,341.5,434.1133,329.5,446.1133,224.5,446.1133,212.5,434.1133,224.5,422.1133" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="281" y="459.1016">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="105" x="224.5" y="438.8984">Is Resource Exist</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="341.5" y="430.6953">yes</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="101" x="383.5" y="331.707"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="81" x="393.5" y="354.6953">load-from-url</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="302,551.0078,314,563.0078,302,575.0078,290,563.0078,302,551.0078" style="stroke: #383838; stroke-width: 1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="281.5,601.4668,322.5,601.4668,334.5,613.4668,322.5,625.4668,281.5,625.4668,269.5,613.4668,281.5,601.4668" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="41" x="281.5" y="618.252">Is SVG</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="248.5" y="610.0488">yes</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="334.5" y="610.0488">no</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="115" x="177.5" y="635.4668"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="95" x="187.5" y="658.4551">svg-cache-read</text>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="91" x="189.5" y="742.0762"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="71" x="199.5" y="765.0645">svg-decode</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="204.5,691.873,265.5,691.873,277.5,703.873,265.5,715.873,204.5,715.873,192.5,703.873,204.5,691.873" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="239" y="728.8613">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="61" x="204.5" y="708.6582">Is Cached</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="277.5" y="700.4551">yes</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="235,798.4824,247,810.4824,235,822.4824,223,810.4824,235,798.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" width="93" x="322.5" y="635.4668"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="73" x="332.5" y="658.4551">png-decode</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="302,828.4824,314,840.4824,302,852.4824,290,840.4824,302,828.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="271.5,247.5039,332.5,247.5039,344.5,259.5039,332.5,271.5039,271.5,271.5039,259.5,259.5039,271.5,247.5039" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="306" y="284.4922">no</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="61" x="271.5" y="264.2891">Is Cached</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="344.5" y="256.0859">yes</text>
<polygon fill="#F8F8F8" filter="url(#f184fee447dtf9)" points="302,872.4824,314,884.4824,302,896.4824,290,884.4824,302,872.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<rect class="process" filter="url(#f184fee447dtf9)" height="36.4063" rx="12.5" ry="12.5" width="104" x="250" y="916.4824"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="84" x="260" y="939.4707">Return Image</text>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="47.4063" y2="67.4063"/>
<polygon fill="#383838" points="298,57.4063,302,67.4063,306,57.4063,302,61.4063" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="269.5" x2="255" y1="139.0977" y2="139.0977"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="255" x2="255" y1="139.0977" y2="161.0977"/>
<polygon fill="#383838" points="251,151.0977,255,161.0977,259,151.0977,255,155.0977" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="334.5" x2="349" y1="139.0977" y2="139.0977"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="349" x2="349" y1="139.0977" y2="161.0977"/>
<polygon fill="#383838" points="345,151.0977,349,161.0977,353,151.0977,349,155.0977" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="255" x2="255" y1="197.5039" y2="215.5039"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="255" x2="290" y1="215.5039" y2="215.5039"/>
<polygon fill="#383838" points="280,211.5039,290,215.5039,280,219.5039,284,215.5039" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="349" x2="349" y1="197.5039" y2="215.5039"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="349" x2="314" y1="215.5039" y2="215.5039"/>
<polygon fill="#383838" points="324,211.5039,314,215.5039,324,219.5039,320,215.5039" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="103.8125" y2="127.0977"/>
<polygon fill="#383838" points="298,117.0977,302,127.0977,306,117.0977,302,121.0977" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="63" x2="63" y1="458.5195" y2="493.5195"/>
<polygon fill="#383838" points="59,483.5195,63,493.5195,67,483.5195,63,487.5195" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="277" x2="277" y1="446.1133" y2="472.3164"/>
<polygon fill="#383838" points="273,462.3164,277,472.3164,281,462.3164,277,466.3164" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="341.5" x2="353.5" y1="434.1133" y2="434.1133"/>
<polygon fill="#383838" points="349.5,480.5605,353.5,490.5605,357.5,480.5605,353.5,484.5605" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="353.5" x2="353.5" y1="434.1133" y2="546.0078"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="353.5" x2="170" y1="546.0078" y2="546.0078"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="170" x2="170" y1="546.0078" y2="563.0078"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="170" x2="290" y1="563.0078" y2="563.0078"/>
<polygon fill="#383838" points="280,559.0078,290,563.0078,280,567.0078,284,563.0078" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="73" x2="63" y1="400.1133" y2="400.1133"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="63" x2="63" y1="400.1133" y2="422.1133"/>
<polygon fill="#383838" points="59,412.1133,63,422.1133,67,412.1133,63,416.1133" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="267" x2="277" y1="400.1133" y2="400.1133"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="277" x2="277" y1="400.1133" y2="422.1133"/>
<polygon fill="#383838" points="273,412.1133,277,422.1133,281,412.1133,277,416.1133" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="170" x2="170" y1="368.1133" y2="388.1133"/>
<polygon fill="#383838" points="166,378.1133,170,388.1133,174,378.1133,170,382.1133" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="210" x2="170" y1="309.707" y2="309.707"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="170" x2="170" y1="309.707" y2="331.707"/>
<polygon fill="#383838" points="166,321.707,170,331.707,174,321.707,170,325.707" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="394" x2="434" y1="309.707" y2="309.707"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="434" x2="434" y1="309.707" y2="331.707"/>
<polygon fill="#383838" points="430,321.707,434,331.707,438,321.707,434,325.707" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="434" x2="434" y1="368.1133" y2="563.0078"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="434" x2="314" y1="563.0078" y2="563.0078"/>
<polygon fill="#383838" points="324,559.0078,314,563.0078,324,567.0078,320,563.0078" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="235" x2="235" y1="715.873" y2="742.0762"/>
<polygon fill="#383838" points="231,732.0762,235,742.0762,239,732.0762,235,736.0762" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="277.5" x2="290.5" y1="703.873" y2="703.873"/>
<polygon fill="#383838" points="286.5,750.2793,290.5,760.2793,294.5,750.2793,290.5,754.2793" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="290.5" x2="290.5" y1="703.873" y2="810.4824"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="290.5" x2="247" y1="810.4824" y2="810.4824"/>
<polygon fill="#383838" points="257,806.4824,247,810.4824,257,814.4824,253,810.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="235" x2="235" y1="778.4824" y2="798.4824"/>
<polygon fill="#383838" points="231,788.4824,235,798.4824,239,788.4824,235,792.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="235" x2="235" y1="671.873" y2="691.873"/>
<polygon fill="#383838" points="231,681.873,235,691.873,239,681.873,235,685.873" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="269.5" x2="235" y1="613.4668" y2="613.4668"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="235" x2="235" y1="613.4668" y2="635.4668"/>
<polygon fill="#383838" points="231,625.4668,235,635.4668,239,625.4668,235,629.4668" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="334.5" x2="369" y1="613.4668" y2="613.4668"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="369" x2="369" y1="613.4668" y2="635.4668"/>
<polygon fill="#383838" points="365,625.4668,369,635.4668,373,625.4668,369,629.4668" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="235" x2="235" y1="822.4824" y2="840.4824"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="235" x2="290" y1="840.4824" y2="840.4824"/>
<polygon fill="#383838" points="280,836.4824,290,840.4824,280,844.4824,284,840.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="369" x2="369" y1="671.873" y2="840.4824"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="369" x2="314" y1="840.4824" y2="840.4824"/>
<polygon fill="#383838" points="324,836.4824,314,840.4824,324,844.4824,320,840.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="575.0078" y2="601.4668"/>
<polygon fill="#383838" points="298,591.4668,302,601.4668,306,591.4668,302,595.4668" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="271.5039" y2="297.707"/>
<polygon fill="#383838" points="298,287.707,302,297.707,306,287.707,302,291.707" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="344.5" x2="504.5" y1="259.5039" y2="259.5039"/>
<polygon fill="#383838" points="500.5,571.4668,504.5,581.4668,508.5,571.4668,504.5,575.4668" style="stroke: #383838; stroke-width: 1.5;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="504.5" x2="504.5" y1="259.5039" y2="884.4824"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="504.5" x2="314" y1="884.4824" y2="884.4824"/>
<polygon fill="#383838" points="324,880.4824,314,884.4824,324,888.4824,320,884.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="852.4824" y2="872.4824"/>
<polygon fill="#383838" points="298,862.4824,302,872.4824,306,862.4824,302,866.4824" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="227.5039" y2="247.5039"/>
<polygon fill="#383838" points="298,237.5039,302,247.5039,306,237.5039,302,241.5039" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="302" x2="302" y1="896.4824" y2="916.4824"/>
<polygon fill="#383838" points="298,906.4824,302,916.4824,306,906.4824,302,910.4824" style="stroke: #383838; stroke-width: 1.0;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,50 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>IntelliJ Platform Activity Diagrams</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css">
</head>
<body>
<script>
function applyItem(item) {
document.getElementById("svgElement").data = `${item.value}.svg`
const title = `${item.label} — IntelliJ Platform Activity Diagrams`
document.title = title
const id = item.value
history.pushState(null, title, `#${id}`)
localStorage.setItem("selectedDiagram", id)
}
function diagramChanged(event) {
applyItem(event.target.options[event.target.selectedIndex])
}
</script>
<section class="section">
<div class="select is-pulled-right">
<label>
<select id="diagramSelector" onchange="diagramChanged(event)">
<option value="getting-service">Getting Service</option>
<option value="projectClose-dispose-flow">Close Project Flow</option>
<option value="icon-loading-stat">Icon Loading Stats</option>
</select>
</label>
</div>
<!--suppress CheckTagEmptyBody -->
<object data="getting-service.svg" type="image/svg+xml" id="svgElement"></object>
</section>
<script>
(function () {
const selectedId = localStorage.getItem("selectedDiagram")
if (selectedId == null) {
applyItem(document.getElementById("diagramSelector").options[0])
}
else {
applyItem(Array.from(document.getElementById("diagramSelector").options).find(it => it.value === selectedId))
}
})()
</script>
</body>
</html>

View File

@@ -1,176 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" contentScriptType="application/ecmascript" contentStyleType="text/css" height="1160px"
preserveAspectRatio="none" style="width:1194px;height:1160px;" version="1.1" viewBox="0 0 1194 1160" width="1194px"
zoomAndPan="magnify">
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&amp;display=swap');
.text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
}
.code {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}
.process {
stroke: #383838; stroke-width: 1.0;
fill: #F8F8F8;
}
.note {
stroke: #383838; stroke-width: 1.0;
fill: #ECECEC;
}
</style>
<defs>
<filter height="300%" id="fsv0at21hgsol" width="300%" x="-1" y="-1">
<feGaussianBlur result="blurOut" stdDeviation="2.0"/>
<feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/>
<feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/>
<feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/>
</filter>
</defs>
<g>
<path class="note" d="M881.5,16 L881.5,25.2031 L861.5,29.2031 L881.5,33.2031 L881.5,42.4063 A0,0 0 0 0 881.5,42.4063 L1182.5,42.4063 A0,0 0 0 0 1182.5,42.4063 L1182.5,26 L1172.5,16 L881.5,16 A0,0 0 0 0 881.5,16 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M1172.5,16 L1172.5,26 L1182.5,26 L1172.5,16 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="280" x="887.5" y="33.9883">Project closing executed in a dispatch thread.</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" rx="12.5" ry="12.5" width="96" x="765.5" y="11"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="76" x="775.5" y="33.9883">closeProject</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="52.8945" width="197" x="715" y="67.4063"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="57" x="785" y="90.3945">canClose</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="177" x="725" y="107.3477">(ep=ProjectCloseHandler)</text>
<polygon fill="#F8F8F8" filter="url(#fsv0at21hgsol)" points="719,140.3008,908,140.3008,920,156.748,908,173.1953,719,173.1953,707,156.748,719,140.3008" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="57" x="785" y="153.2891">canClose</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="0" x="731" y="169.7773"/>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="177" x="731" y="170.2422">(ep=ProjectCloseHandler)</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="21" x="686" y="153.3301">yes</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="16" x="920" y="153.3301">no</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="166" x="472.5" y="183.1953"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="146" x="482.5" y="206.1836">Stop Service Preloading</text>
<path class="note" d="M705,244.6836 L705,262.0898 L685,266.0898 L705,270.0898 L705,287.4961 A0,0 0 0 0 705,287.4961 L993,287.4961 A0,0 0 0 0 993,287.4961 L993,254.6836 L983,244.6836 L705,244.6836 A0,0 0 0 0 705,244.6836 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M983,244.6836 L983,254.6836 L993,254.6836 L983,244.6836 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="196" x="711" y="262.6719">In unit test mode in a light tests,</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="267" x="711" y="279.0781">light project is not closed and not disposed.</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="52.9766" width="259" x="426" y="239.6016"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="23" x="436" y="262.6719">Fire</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="175" x="462" y="263.1367">projectClosingBeforeSave</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="35" x="640" y="262.6719">Event</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="186" x="462.5" y="279.625">(l=ProjectManagerListener)</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="128" x="491.5" y="312.5781"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="108" x="501.5" y="335.5664">Save Project Files</text>
<polygon fill="#F8F8F8" filter="url(#fsv0at21hgsol)" points="490,368.9844,621,368.9844,633,380.9844,621,392.9844,490,392.9844,478,380.9844,490,368.9844" style="stroke: #383838; stroke-width: 1.0;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="131" x="490" y="385.7695">Save Project Settings</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="178" x="300" y="377.5664">Successfully or Error Ignored</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="91" x="633" y="377.5664">Error Occurred</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="52.9766" width="206" x="106.5" y="402.9844"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="23" x="127.5" y="426.0547">Fire</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="100" x="153.5" y="426.5195">projectClosing</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="35" x="256.5" y="426.0547">Event</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="186" x="116.5" y="443.0078">(l=ProjectManagerListener)</text>
<rect fill="#FFFFFF" filter="url(#fsv0at21hgsol)" height="677.334" style="stroke: #000000; stroke-width: 1.5;" width="812" x="11" y="465.9609"/>
<path d="M94,465.9609 L94,475.3672 L84,485.3672 L11,485.3672 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="73" x="14" y="479.9492">write action</text>
<path class="note" d="M418,495.3672 L418,521.0586 L398,525.0586 L418,529.0586 L418,554.75 A0,0 0 0 0 418,554.75 L813,554.75 A0,0 0 0 0 813,554.75 L813,505.3672 L803,495.3672 L418,495.3672 A0,0 0 0 0 418,495.3672 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M803,495.3672 L803,505.3672 L813,505.3672 L803,495.3672 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="217" x="424" y="513.4375">If you incorrectly specify project for</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="151" x="644" y="513.9023">MessageBus.connect()</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="3" x="795" y="513.4375">,</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="214" x="424" y="529.8438">it will be disconnected on this step.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="88" x="424" y="546.332">Do not specify</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="120" x="515" y="546.7969">parentDisposable</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="92" x="638" y="546.332">unless needed.</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="377" x="21" y="506.8555"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="357" x="31" y="529.8438">Dispose everything that uses Project as parent disposable</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="52.8125" width="296" x="61.5" y="574.75"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="263" x="78" y="597.7383">Dispose Project Message Bus Connections</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="276" x="71.5" y="614.1445">without explicitly specified parent disposable</text>
<path class="note" d="M378.5,637.5625 L378.5,671.416 L358.5,675.416 L378.5,679.416 L378.5,713.2695 A0,0 0 0 0 378.5,713.2695 L790.5,713.2695 A0,0 0 0 0 790.5,713.2695 L790.5,647.5625 L780.5,637.5625 L378.5,637.5625 A0,0 0 0 0 378.5,637.5625 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M780.5,637.5625 L780.5,647.5625 L790.5,647.5625 L780.5,637.5625 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="294" x="384.5" y="655.5508">Getting services and publishing to message bus</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="162" x="384.5" y="671.957">is prohibited from now on.</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="125" x="384.5" y="688.9102">Project.isDisposed</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="45" x="512.5" y="688.4453">returns</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="28" x="560.5" y="688.9102">true</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="184" x="591.5" y="688.4453">(not changed in a read action,</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="232" x="384.5" y="704.8516">because state is set in a write action).</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4883" width="298" x="60.5" y="657.1719"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="119" x="70.5" y="680.2422">Set Project State to</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="156" x="192.5" y="680.707">DISPOSE_IN_PROGRESS</text>
<path class="note" d="M403,738.2695 L403,747.4727 L383,751.4727 L403,755.4727 L403,764.6758 A0,0 0 0 0 403,764.6758 L761,764.6758 A0,0 0 0 0 761,764.6758 L761,748.2695 L751,738.2695 L403,738.2695 A0,0 0 0 0 403,738.2695 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M751,738.2695 L751,748.2695 L761,748.2695 L751,738.2695 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="337" x="409" y="756.2578">Connecting to message bus is prohibited from now on.</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="347" x="36" y="733.2695"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="327" x="46" y="756.2578">Dispose Project Message Bus Connection Disposable</text>
<path class="note" d="M351,786.4316 L351,803.8789 L331,807.8789 L351,811.8789 L351,829.3262 A0,0 0 0 0 351,829.3262 L664,829.3262 A0,0 0 0 0 664,829.3262 L664,796.4316 L654,786.4316 L351,786.4316 A0,0 0 0 0 351,786.4316 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M654,786.4316 L654,796.4316 L664,796.4316 L654,786.4316 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="55" x="357" y="804.502">Result of</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="234" x="415" y="804.9668">ProjectManager.getOpenProjects()</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="173" x="357" y="820.9082">is valid only in a read action.</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="243" x="88" y="789.6758"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="223" x="98" y="812.6641">Remove Project from List of Opened</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="52.9766" width="206" x="106.5" y="849.3262"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="23" x="130" y="872.3965">Fire</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="95" x="156" y="872.8613">projectClosed</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="35" x="254" y="872.3965">Event</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="186" x="116.5" y="889.3496">(l=ProjectManagerListener)</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4883" width="209" x="105" y="922.3027"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="119" x="115" y="945.373">Set Project State to</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="67" x="237" y="945.8379">DISPOSED</text>
<path class="note" d="M346,975.5879 L346,992.9941 L326,996.9941 L346,1000.9941 L346,1018.4004 A0,0 0 0 0 346,1018.4004 L591,1018.4004 A0,0 0 0 0 591,1018.4004 L591,985.5879 L581,975.5879 L346,975.5879 A0,0 0 0 0 346,975.5879 " filter="url(#fsv0at21hgsol)"/>
<path class="note" d="M581,975.5879 L581,985.5879 L591,985.5879 L581,975.5879 "/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="170" x="352" y="993.5762">First created, last disposed.</text>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="224" x="352" y="1009.9824">Children are disposed before parent.</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="233" x="93" y="978.791"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="213" x="103" y="1001.7793">Dispose Services and Components</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" width="155" x="132" y="1038.4004"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="135" x="142" y="1061.3887">Dispose Message Bus</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4883" rx="12.5" ry="12.5" width="289" x="65" y="1094.8066"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="119" x="75" y="1117.877">Set Project State to</text>
<text class="code" lengthAdjust="spacingAndGlyphs" textLength="147" x="197" y="1118.3418">DISPOSE_COMPLETED</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" rx="12.5" ry="12.5" width="117" x="843" y="402.9844"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="97" x="853" y="425.9727">Close Cancelled</text>
<rect class="process" filter="url(#fsv0at21hgsol)" height="36.4063" rx="12.5" ry="12.5" width="117" x="1013" y="183.1953"/>
<text class="text" lengthAdjust="spacingAndGlyphs" textLength="97" x="1023" y="206.1836">Close Cancelled</text>
<line style="stroke: #383838; stroke-width: 1.5;" x1="813.5" x2="813.5" y1="47.4063" y2="67.4063"/>
<polygon fill="#383838" points="809.5,57.4063,813.5,67.4063,817.5,57.4063,813.5,61.4063" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="555.5" x2="555.5" y1="219.6016" y2="239.6016"/>
<polygon fill="#383838" points="551.5,229.6016,555.5,239.6016,559.5,229.6016,555.5,233.6016" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="555.5" x2="555.5" y1="292.5781" y2="312.5781"/>
<polygon fill="#383838" points="551.5,302.5781,555.5,312.5781,559.5,302.5781,555.5,306.5781" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="543.2617" y2="574.75"/>
<polygon fill="#383838" points="205.5,564.75,209.5,574.75,213.5,564.75,209.5,568.75" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="627.5625" y2="657.1719"/>
<polygon fill="#383838" points="205.5,647.1719,209.5,657.1719,213.5,647.1719,209.5,651.1719" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="693.6602" y2="733.2695"/>
<polygon fill="#383838" points="205.5,723.2695,209.5,733.2695,213.5,723.2695,209.5,727.2695" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="769.6758" y2="789.6758"/>
<polygon fill="#383838" points="205.5,779.6758,209.5,789.6758,213.5,779.6758,209.5,783.6758" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="826.082" y2="849.3262"/>
<polygon fill="#383838" points="205.5,839.3262,209.5,849.3262,213.5,839.3262,209.5,843.3262" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="902.3027" y2="922.3027"/>
<polygon fill="#383838" points="205.5,912.3027,209.5,922.3027,213.5,912.3027,209.5,916.3027" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="958.791" y2="978.791"/>
<polygon fill="#383838" points="205.5,968.791,209.5,978.791,213.5,968.791,209.5,972.791" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="1015.1973" y2="1038.4004"/>
<polygon fill="#383838" points="205.5,1028.4004,209.5,1038.4004,213.5,1028.4004,209.5,1032.4004" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="1074.8066" y2="1094.8066"/>
<polygon fill="#383838" points="205.5,1084.8066,209.5,1094.8066,213.5,1084.8066,209.5,1088.8066" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="455.9609" y2="506.8555"/>
<polygon fill="#383838" points="205.5,496.8555,209.5,506.8555,213.5,496.8555,209.5,500.8555" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="478" x2="209.5" y1="380.9844" y2="380.9844"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="209.5" x2="209.5" y1="380.9844" y2="402.9844"/>
<polygon fill="#383838" points="205.5,392.9844,209.5,402.9844,213.5,392.9844,209.5,396.9844" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="633" x2="901.5" y1="380.9844" y2="380.9844"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="901.5" x2="901.5" y1="380.9844" y2="402.9844"/>
<polygon fill="#383838" points="897.5,392.9844,901.5,402.9844,905.5,392.9844,901.5,396.9844" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="555.5" x2="555.5" y1="348.9844" y2="368.9844"/>
<polygon fill="#383838" points="551.5,358.9844,555.5,368.9844,559.5,358.9844,555.5,362.9844" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="707" x2="555.5" y1="156.748" y2="156.748"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="555.5" x2="555.5" y1="156.748" y2="183.1953"/>
<polygon fill="#383838" points="551.5,173.1953,555.5,183.1953,559.5,173.1953,555.5,177.1953" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="920" x2="1071.5" y1="156.748" y2="156.748"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="1071.5" x2="1071.5" y1="156.748" y2="183.1953"/>
<polygon fill="#383838" points="1067.5,173.1953,1071.5,183.1953,1075.5,173.1953,1071.5,177.1953" style="stroke: #383838; stroke-width: 1.0;"/>
<line style="stroke: #383838; stroke-width: 1.5;" x1="813.5" x2="813.5" y1="120.3008" y2="140.3008"/>
<polygon fill="#383838" points="809.5,130.3008,813.5,140.3008,817.5,130.3008,813.5,134.3008" style="stroke: #383838; stroke-width: 1.0;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,6 +0,0 @@
IntelliJ Platform SDK [docs](https://www.jetbrains.org/intellij/sdk/docs/welcome.html) sources located on [GitHub](https://github.com/JetBrains/intellij-sdk-docs).
This directory contains only activity diagrams sources.
### How to write diagram in PlantUML
Install [PlantUML integration](https://plugins.jetbrains.com/plugin/7017-plantuml-integration/) plugin to add PlantUML support to IntelliJ IDEA.

View File

@@ -1,3 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util

View File

@@ -191,7 +191,11 @@ class FileTemplatesLoader implements Disposable {
if (!processedUrls.add(url)) {
continue;
}
loadDefaultsFromRoot(url, prefixes, result);
List<String> children = UrlUtil.getChildrenRelativePaths(url);
if (!children.isEmpty()) {
loadDefaultsFromRoot(url, children, prefixes, result);
}
}
}
catch (IOException e) {
@@ -201,14 +205,11 @@ class FileTemplatesLoader implements Disposable {
return result;
}
private static void loadDefaultsFromRoot(@NotNull URL root, @NotNull List<String> prefixes, @NotNull FileTemplateLoadResult result)
throws IOException {
final List<String> children = UrlUtil.getChildrenRelativePaths(root);
if (children.isEmpty()) {
return;
}
final Set<String> descriptionPaths = new HashSet<>();
private static void loadDefaultsFromRoot(@NotNull URL root,
@NotNull List<String> children,
@NotNull List<String> prefixes,
@NotNull FileTemplateLoadResult result) throws IOException {
Set<String> descriptionPaths = new HashSet<>();
for (String path : children) {
if (path.equals("default.html")) {
result.setDefaultTemplateDescription(
@@ -222,7 +223,7 @@ class FileTemplatesLoader implements Disposable {
}
}
for (final String path : children) {
for (String path : children) {
if (!path.endsWith(FTManager.TEMPLATE_EXTENSION_SUFFIX)) {
continue;
}

View File

@@ -35,34 +35,36 @@ public final class UrlUtil {
}
}
@NotNull
public static List<String> getChildrenRelativePaths(@NotNull URL root) throws IOException {
final String protocol = root.getProtocol();
public static @NotNull List<String> getChildrenRelativePaths(@NotNull URL root) throws IOException {
String protocol = root.getProtocol();
if ("jar".equalsIgnoreCase(protocol)) {
return getChildPathsFromJar(root);
}
if (FILE_PROTOCOL.equalsIgnoreCase(protocol)){
else if (FILE_PROTOCOL.equalsIgnoreCase(protocol)) {
return getChildPathsFromFile(root);
}
return Collections.emptyList();
else {
return Collections.emptyList();
}
}
@NotNull
private static List<String> getChildPathsFromFile(@NotNull URL root) {
private static @NotNull List<String> getChildPathsFromFile(@NotNull URL root) {
final List<String> paths = new ArrayList<>();
final File rootFile = new File(FileUtil.unquote(root.getPath()));
new Object() {
void collectFiles(File fromFile, String prefix) {
final File[] list = fromFile.listFiles();
if (list != null) {
for (File file : list) {
final String childRelativePath = prefix.isEmpty() ? file.getName() : prefix + URL_PATH_SEPARATOR + file.getName();
if (file.isDirectory()) {
collectFiles(file, childRelativePath);
}
else {
paths.add(childRelativePath);
}
File[] list = fromFile.listFiles();
if (list == null) {
return;
}
for (File file : list) {
String childRelativePath = prefix.isEmpty() ? file.getName() : prefix + URL_PATH_SEPARATOR + file.getName();
if (file.isDirectory()) {
collectFiles(file, childRelativePath);
}
else {
paths.add(childRelativePath);
}
}
}
@@ -70,24 +72,24 @@ public final class UrlUtil {
return paths;
}
@NotNull
private static List<String> getChildPathsFromJar(@NotNull URL root) throws IOException {
private static @NotNull List<String> getChildPathsFromJar(@NotNull URL root) throws IOException {
String file = root.getFile();
file = StringUtil.trimStart(file, FILE_PROTOCOL_PREFIX);
final int jarSeparatorIndex = file.indexOf(JAR_SEPARATOR);
int jarSeparatorIndex = file.indexOf(JAR_SEPARATOR);
assert jarSeparatorIndex > 0;
String rootDirName = file.substring(jarSeparatorIndex + 2);
if (!rootDirName.endsWith(URL_PATH_SEPARATOR)) {
rootDirName += URL_PATH_SEPARATOR;
}
try (ZipFile zipFile = new ZipFile(FileUtil.unquote(file.substring(0, jarSeparatorIndex)))) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
final List<String> paths = new ArrayList<>();
Enumeration<? extends ZipEntry> entries = zipFile.entries();
List<String> paths = new ArrayList<>();
while (entries.hasMoreElements()) {
final ZipEntry entry = entries.nextElement();
ZipEntry entry = entries.nextElement();
if (!entry.isDirectory()) {
final String relPath = entry.getName();
String relPath = entry.getName();
if (relPath.startsWith(rootDirName)) {
paths.add(relPath.substring(rootDirName.length()));
}

View File

@@ -325,7 +325,7 @@ class ModuleManagerComponentBridge(private val project: Project) : ModuleManager
LOG.debug { "Loading modules for ${loadedEntities.size} entities" }
val plugins = PluginManagerCore.getLoadedPlugins(null)
val corePlugin = plugins.find { it.pluginId == PluginManagerCore.CORE_ID }
val corePlugin = plugins.firstOrNull { it.pluginId == PluginManagerCore.CORE_ID }
val precomputedExtensionModel = precomputeExtensionModel(plugins)

View File

@@ -10,7 +10,7 @@ private val LOG = logger<ClassLoaderTreeChecker>()
internal class ClassLoaderTreeChecker(private val unloadedMainDescriptor: IdeaPluginDescriptorImpl,
private val classLoaders: WeakList<PluginClassLoader>) {
fun checkThatClassLoaderNotReferencedByPluginClassLoader() {
if (!ClassLoaderConfigurationData.SEPARATE_CLASSLOADER_FOR_SUB || unloadedMainDescriptor.classLoader !is PluginClassLoader) {
if (unloadedMainDescriptor.classLoader !is PluginClassLoader) {
return
}
@@ -37,6 +37,7 @@ internal class ClassLoaderTreeChecker(private val unloadedMainDescriptor: IdeaPl
}
}
@Suppress("TestOnlyProblems")
val parents = classLoader._getParents()
for (unloadedClassLoader in classLoaders) {

View File

@@ -1,4 +1,5 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Suppress("TestOnlyProblems")
package com.intellij.ide.plugins
import com.intellij.diagnostic.PluginException
@@ -24,6 +25,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.stubs.StubElementTypeHolderEP
import com.intellij.serviceContainer.ComponentManagerImpl
import com.intellij.util.getErrorsAsString
import io.github.classgraph.AnnotationEnumValue
import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
@@ -32,11 +34,9 @@ import kotlin.properties.Delegates.notNull
@Suppress("HardCodedStringLiteral")
private class CreateAllServicesAndExtensionsAction : AnAction("Create All Services And Extensions"), DumbAware {
override fun actionPerformed(e: AnActionEvent) {
val errors = mutableListOf<Throwable>()
runModalTask("Creating All Services And Extensions", cancellable = true) { indicator ->
val logger = logger<ComponentManagerImpl>()
val taskExecutor: (task: () -> Unit) -> Unit = { task ->
try {
task()
@@ -45,7 +45,6 @@ private class CreateAllServicesAndExtensionsAction : AnAction("Create All Servic
throw e
}
catch (e: Throwable) {
logger.error(e)
errors.add(e)
}
}
@@ -59,7 +58,11 @@ private class CreateAllServicesAndExtensionsAction : AnAction("Create All Servic
}
indicator.text2 = "Checking light services..."
checkLightServices(taskExecutor)
checkLightServices(taskExecutor, errors)
}
if (errors.isNotEmpty()) {
logger<ComponentManagerImpl>().error(getErrorsAsString(errors))
}
// some errors are not thrown but logged
val message = (if (errors.isEmpty()) "No errors" else "${errors.size} errors were logged") + ". Check also that no logged errors."
@@ -98,7 +101,6 @@ const val ACTION_ID = "CreateAllServicesAndExtensions"
private val badServices = java.util.Set.of(
"com.intellij.usageView.impl.UsageViewContentManagerImpl",
"com.jetbrains.python.scientific.figures.PyPlotToolWindow",
"org.jetbrains.plugins.grails.runner.GrailsConsole",
"com.intellij.analysis.pwa.analyser.PwaServiceImpl",
"com.intellij.analysis.pwa.view.toolwindow.PwaProblemsViewImpl",
)
@@ -110,7 +112,8 @@ private fun checkContainer(container: ComponentManagerImpl, indicator: ProgressI
indicator.text2 = "Checking ${container.activityNamePrefix()}extensions..."
container.extensionArea.processExtensionPoints { extensionPoint ->
// requires read action
if (extensionPoint.name == "com.intellij.favoritesListProvider" || extensionPoint.name == "com.intellij.favoritesListProvider") {
if (extensionPoint.name == "com.intellij.favoritesListProvider" ||
extensionPoint.name == "org.jetbrains.kotlin.defaultErrorMessages") {
return@processExtensionPoints
}
@@ -143,10 +146,10 @@ private fun checkExtensionPoint(extensionPoint: ExtensionPointImpl<*>, taskExecu
}
}
private fun checkLightServices(taskExecutor: (task: () -> Unit) -> Unit) {
for (plugin in PluginManagerCore.getLoadedPlugins(null)) {
private fun checkLightServices(taskExecutor: (task: () -> Unit) -> Unit, errors: MutableList<Throwable>) {
for (plugin in PluginManagerCore.getPluginSet().loadedPlugins) {
// we don't check classloader for sub descriptors because url set is the same
if (plugin.classLoader !is PluginClassLoader || plugin.pluginDependencies.isEmpty()) {
if (plugin.classLoader !is PluginClassLoader) {
continue
}
@@ -158,8 +161,19 @@ private fun checkLightServices(taskExecutor: (task: () -> Unit) -> Unit) {
.use { scanResult ->
val lightServices = scanResult.getClassesWithAnnotation(Service::class.java.name)
for (lightService in lightServices) {
if (lightService.name == "org.jetbrains.plugins.grails.runner.GrailsConsole") {
// wants EDT in constructor
continue
}
// not clear - from what classloader light service will be loaded in reality
val lightServiceClass = loadLightServiceClass(lightService, plugin)
val lightServiceClass = try {
loadLightServiceClass(lightService, plugin)
}
catch (e: Throwable) {
errors.add(e)
continue
}
val isProjectLevel: Boolean
val isAppLevel: Boolean
@@ -176,7 +190,12 @@ private fun checkLightServices(taskExecutor: (task: () -> Unit) -> Unit) {
if (isAppLevel) {
taskExecutor {
ApplicationManager.getApplication().getService(lightServiceClass)
try {
ApplicationManager.getApplication().getService(lightServiceClass)
}
catch (e: Throwable) {
errors.add(RuntimeException("Cannot create $lightServiceClass", e))
}
}
}
if (isProjectLevel) {
@@ -190,21 +209,10 @@ private fun checkLightServices(taskExecutor: (task: () -> Unit) -> Unit) {
}
private fun loadLightServiceClass(lightService: ClassInfo, mainDescriptor: IdeaPluginDescriptorImpl): Class<*> {
//
for (pluginDependency in mainDescriptor.pluginDependencies) {
val subPluginClassLoader = pluginDependency.subDescriptor?.classLoader as? PluginClassLoader ?: continue
val packagePrefix = subPluginClassLoader.packagePrefix ?: continue
if (lightService.name.startsWith(packagePrefix)) {
return subPluginClassLoader.loadClass(lightService.name, true)
}
}
for (pluginDependency in mainDescriptor.pluginDependencies) {
val subPluginClassLoader = pluginDependency.subDescriptor?.classLoader as? PluginClassLoader ?: continue
val clazz = subPluginClassLoader.loadClass(lightService.name, true)
if (clazz != null && clazz.classLoader === subPluginClassLoader) {
// light class is resolved from this sub plugin classloader - check successful
return clazz
for (item in mainDescriptor.content.modules) {
val classLoader = item.requireDescriptor().classLoader as? PluginClassLoader ?: continue
if (lightService.name.startsWith(classLoader.packagePrefix!!)) {
return classLoader.loadClass(lightService.name, true)
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.fasterxml.jackson.databind.type.TypeFactory
@@ -23,7 +23,6 @@ import com.intellij.ide.ui.TopHitCache
import com.intellij.ide.ui.UIThemeProvider
import com.intellij.ide.util.TipDialog
import com.intellij.idea.IdeaLogger
import com.intellij.idea.ZipFilePoolImpl
import com.intellij.internal.statistic.eventLog.FeatureUsageData
import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger
import com.intellij.internal.statistic.utils.getPluginInfoByDescriptor
@@ -68,8 +67,6 @@ import com.intellij.openapi.vfs.newvfs.FileAttribute
import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.impl.IdeFrameImpl
import com.intellij.openapi.wm.impl.ProjectFrameHelper
import com.intellij.platform.util.plugins.DataLoader
import com.intellij.platform.util.plugins.LocalFsDataLoader
import com.intellij.psi.util.CachedValuesManager
import com.intellij.serviceContainer.ComponentManagerImpl
import com.intellij.ui.IconDeferrer
@@ -80,7 +77,6 @@ import com.intellij.util.ReflectionUtil
import com.intellij.util.SystemProperties
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.WeakList
import com.intellij.util.lang.ZipFilePool
import com.intellij.util.messages.impl.MessageBusEx
import com.intellij.util.ref.GCWatcher
import net.sf.cglib.core.ClassNameReader
@@ -89,19 +85,17 @@ import java.awt.KeyboardFocusManager
import java.awt.Window
import java.nio.channels.FileChannel
import java.nio.file.FileVisitResult
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
import java.text.SimpleDateFormat
import java.util.*
import java.util.function.Function
import java.util.function.Predicate
import javax.swing.JComponent
import javax.swing.ToolTipManager
import kotlin.collections.component1
import kotlin.collections.component2
private val LOG = logger<DynamicPlugins>()
private val LOG = logger<DynamicPlugins>()
private val classloadersFromUnloadedPlugins = ContainerUtil.createWeakValueMap<PluginId, PluginClassLoader>()
object DynamicPlugins {
@@ -124,9 +118,8 @@ object DynamicPlugins {
*/
@JvmStatic
fun loadPlugins(descriptors: Collection<IdeaPluginDescriptor>): Boolean {
val loader = lazy(LazyThreadSafetyMode.NONE) { OptionalDependencyDescriptorLoader() }
return updateDescriptorsWithoutRestart(descriptors, load = true) {
loadPlugin(it, checkImplementationDetailDependencies = true, loader = loader)
loadPlugin(it, checkImplementationDetailDependencies = true)
}
}
@@ -183,12 +176,9 @@ object DynamicPlugins {
return true
}
private fun pluginsSortedByDependency(
descriptors: List<IdeaPluginDescriptorImpl>,
load: Boolean,
): List<IdeaPluginDescriptorImpl> {
private fun pluginsSortedByDependency(descriptors: List<IdeaPluginDescriptorImpl>, load: Boolean): List<IdeaPluginDescriptorImpl> {
val plugins = PluginManager.getInstance().getPluginsSortedByDependency(descriptors)
return if (load) plugins.asList() else plugins.reversed()
return if (load) plugins else plugins.reversed()
}
/**
@@ -235,19 +225,20 @@ object DynamicPlugins {
return e.cause?.localizedMessage ?: "checkUnloadPlugin listener blocked plugin unload"
}
val pluginStateChecker = PluginStateChecker()
val pluginSet = PluginManagerCore.getPluginSet()
if (!Registry.`is`("ide.plugins.allow.unload.from.sources")) {
val loadedPluginDescriptor = if (descriptor === baseDescriptor) {
PluginManagerCore.getPlugin(descriptor.pluginId) as IdeaPluginDescriptorImpl?
pluginSet.findEnabledPlugin(descriptor.pluginId)
}
else {
null
}
if (loadedPluginDescriptor != null && !descriptor.isUseIdeaClassLoader &&
pluginStateChecker.isPluginOrModuleLoaded(loadedPluginDescriptor.pluginId)) {
val pluginClassLoader = loadedPluginDescriptor.pluginClassLoader
if (pluginClassLoader !is PluginClassLoader && !app.isUnitTestMode) {
pluginSet.isPluginEnabled(loadedPluginDescriptor.pluginId)) {
val pluginClassLoader = loadedPluginDescriptor.classLoader
if (pluginClassLoader != null && pluginClassLoader !is PluginClassLoader && !app.isUnitTestMode) {
return "Plugin ${descriptor.pluginId} is not unload-safe because of use of ${pluginClassLoader.javaClass.name} as the default class loader. " +
"For example, the IDE is started from the sources with the plugin."
}
@@ -258,14 +249,16 @@ object DynamicPlugins {
val epNameToExtensions = descriptor.epNameToExtensions
if (epNameToExtensions != null) {
doCheckExtensionsCanUnloadWithoutRestart(extensions = epNameToExtensions,
descriptor = descriptor,
baseDescriptor = baseDescriptor,
isSubDescriptor = isSubDescriptor,
app = app,
optionalDependencyPluginId = optionalDependencyPluginId,
context = context,
pluginStateChecker = pluginStateChecker)?.let { return it }
doCheckExtensionsCanUnloadWithoutRestart(
extensions = epNameToExtensions,
descriptor = descriptor,
baseDescriptor = baseDescriptor,
isSubDescriptor = isSubDescriptor,
app = app,
optionalDependencyPluginId = optionalDependencyPluginId,
context = context,
pluginSet = pluginSet,
)?.let { return it }
}
val pluginId = descriptor.pluginId
@@ -273,7 +266,7 @@ object DynamicPlugins {
ActionManagerImpl.checkUnloadActions(pluginId, descriptor)?.let { return it }
for (dependency in descriptor.pluginDependencies) {
if (pluginStateChecker.isPluginOrModuleLoaded(dependency.pluginId)) {
if (pluginSet.isPluginEnabled(dependency.pluginId)) {
checkCanUnloadWithoutRestart(dependency.subDescriptor ?: continue, baseDescriptor ?: descriptor, null, context)?.let {
return "$it in optional dependency on ${dependency.pluginId}"
}
@@ -286,15 +279,10 @@ object DynamicPlugins {
}
var dependencyMessage: String? = null
processLoadedOptionalDependenciesOnPlugin(descriptor.pluginId) { mainDescriptor, subDescriptor ->
if (subDescriptor == null) {
// <depends optional="true">XPathView</depends> Here subDescriptor will be null.
return@processLoadedOptionalDependenciesOnPlugin true
}
processOptionalDependenciesOnPlugin(descriptor, pluginSet, isLoaded = true) { mainDescriptor, subDescriptor ->
if (!ClassLoaderConfigurationData.isClassloaderPerDescriptorEnabled(mainDescriptor.pluginId, subDescriptor.packagePrefix)) {
dependencyMessage = "Plugin ${subDescriptor.pluginId} that optionally depends on ${descriptor.pluginId} does not have a separate classloader for the dependency"
return@processLoadedOptionalDependenciesOnPlugin false
return@processOptionalDependenciesOnPlugin false
}
dependencyMessage = checkCanUnloadWithoutRestart(subDescriptor, mainDescriptor, subDescriptor.pluginId, context)
@@ -442,14 +430,10 @@ object DynamicPlugins {
}
@JvmStatic
fun unloadPlugin(fullyLoadedPluginDescriptor: IdeaPluginDescriptorImpl, options: UnloadPluginOptions = UnloadPluginOptions()): Boolean {
fun unloadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl, options: UnloadPluginOptions = UnloadPluginOptions()): Boolean {
val app = ApplicationManager.getApplication() as ApplicationImpl
val pluginId = fullyLoadedPluginDescriptor.pluginId
// The descriptor passed to `unloadPlugin` is the full descriptor loaded from disk, it does not have a classloader.
// We need to find the real plugin loaded into the current instance and unload its classloader.
val pluginDescriptor = PluginManagerCore.getPlugin(pluginId) as? IdeaPluginDescriptorImpl
?: return false
val pluginId = pluginDescriptor.pluginId
val pluginSet = PluginManagerCore.getPluginSet()
if (options.checkImplementationDetailDependencies) {
processImplementationDetailDependenciesOnPlugin(pluginDescriptor) { dependentDescriptor ->
@@ -483,9 +467,9 @@ object DynamicPlugins {
// mark plugin classloaders as being unloaded to ensure that new extension instances will be not created during unload
setClassLoaderState(pluginDescriptor, PluginClassLoader.UNLOAD_IN_PROGRESS)
unloadLoadedOptionalDependenciesOnPlugin(pluginDescriptor, classLoaders)
unloadLoadedOptionalDependenciesOnPlugin(pluginDescriptor, pluginSet = pluginSet, classLoaders = classLoaders)
unloadDependencyDescriptors(pluginDescriptor.pluginDependencies, PluginStateChecker(), classLoaders)
unloadDependencyDescriptors(pluginDescriptor, pluginSet, classLoaders)
unloadPluginDescriptorNotRecursively(pluginDescriptor, true)
clearPluginClassLoaderParentListCache()
@@ -522,10 +506,10 @@ object DynamicPlugins {
if (options.disable) {
// update list of disabled plugins
PluginManager.getInstance().setPlugins(PluginManagerCore.getPlugins().asList())
PluginManager.getInstance().setPlugins(PluginManagerCore.getPluginSet().allPlugins)
}
else {
PluginManager.getInstance().setPlugins(PluginManagerCore.getPlugins().asSequence().minus(pluginDescriptor).toList())
PluginManager.getInstance().setPlugins(PluginManagerCore.getPluginSet().allPlugins.minus(pluginDescriptor))
}
}
finally {
@@ -627,46 +611,42 @@ object DynamicPlugins {
}
}
private fun unloadLoadedOptionalDependenciesOnPlugin(dependencyPluginDescriptor: IdeaPluginDescriptorImpl, classLoaders: WeakList<PluginClassLoader>) {
val dependencyClassloader = dependencyPluginDescriptor.classLoader
for (descriptor in PluginManagerCore.getLoadedPlugins(null)) {
val ok = processLoadedOptionalDependenciesOnPlugin(dependencyPluginDescriptor.pluginId, descriptor) { mainDescriptor, subDescriptor ->
val classLoader = (subDescriptor ?: mainDescriptor).pluginClassLoader
if (subDescriptor != null) {
unloadPluginDescriptorNotRecursively(subDescriptor, false)
}
private fun unloadLoadedOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDescriptorImpl,
pluginSet: PluginSet,
classLoaders: WeakList<PluginClassLoader>) {
val dependencyClassloader = dependencyPlugin.classLoader
processOptionalDependenciesOnPlugin(dependencyPlugin, pluginSet, isLoaded = true) { mainDescriptor, subDescriptor ->
val classLoader = subDescriptor.classLoader
unloadPluginDescriptorNotRecursively(subDescriptor, false)
// this additional code is required because in unit tests PluginClassLoader is not used
if (subDescriptor != null && mainDescriptor !== subDescriptor) {
subDescriptor.classLoader = null
}
// this additional code is required because in unit tests PluginClassLoader is not used
if (mainDescriptor !== subDescriptor) {
subDescriptor.classLoader = null
}
if (dependencyClassloader is PluginClassLoader && classLoader is PluginClassLoader) {
LOG.info("Detach classloader $dependencyClassloader from $classLoader")
if (subDescriptor != null && mainDescriptor !== subDescriptor && classLoader.pluginDescriptor === subDescriptor) {
classLoaders.add(classLoader)
classLoader.state = PluginClassLoader.UNLOAD_IN_PROGRESS
}
else if (!classLoader.detachParent(dependencyClassloader)) {
LOG.warn("Classloader $dependencyClassloader doesn't have $classLoader as parent")
}
if (dependencyClassloader is PluginClassLoader && classLoader is PluginClassLoader) {
LOG.info("Detach classloader $dependencyClassloader from $classLoader")
if (mainDescriptor !== subDescriptor && classLoader.pluginDescriptor === subDescriptor) {
classLoaders.add(classLoader)
classLoader.state = PluginClassLoader.UNLOAD_IN_PROGRESS
}
else if (!classLoader.detachParent(dependencyClassloader)) {
LOG.warn("Classloader $dependencyClassloader doesn't have $classLoader as parent")
}
true
}
if (!ok) {
break
}
true
}
}
private fun unloadDependencyDescriptors(pluginDependencies: List<PluginDependency>,
pluginStateChecker: PluginStateChecker,
private fun unloadDependencyDescriptors(plugin: IdeaPluginDescriptorImpl,
pluginSet: PluginSet,
classLoaders: WeakList<PluginClassLoader>) {
for (dependency in pluginDependencies) {
for (dependency in plugin.pluginDependencies) {
val subDescriptor = dependency.subDescriptor ?: continue
val classLoader = subDescriptor.classLoader
if (!pluginStateChecker.isPluginOrModuleLoaded(dependency.pluginId)) {
LOG.assertTrue(classLoader == null, "Expected not to have any sub descriptor classloader when dependency ${dependency.pluginId} is not loaded")
if (!pluginSet.isPluginEnabled(dependency.pluginId)) {
LOG.assertTrue(classLoader == null,
"Expected not to have any sub descriptor classloader when dependency ${dependency.pluginId} is not loaded")
continue
}
@@ -674,7 +654,19 @@ object DynamicPlugins {
classLoaders.add(classLoader)
}
unloadDependencyDescriptors(subDescriptor.pluginDependencies, pluginStateChecker, classLoaders)
unloadDependencyDescriptors(subDescriptor, pluginSet, classLoaders)
unloadPluginDescriptorNotRecursively(subDescriptor, true)
subDescriptor.classLoader = null
}
for (module in plugin.content.modules) {
val subDescriptor = module.requireDescriptor()
val classLoader = subDescriptor.classLoader ?: continue
if (classLoader is PluginClassLoader && classLoader.pluginDescriptor === subDescriptor) {
classLoaders.add(classLoader)
}
unloadPluginDescriptorNotRecursively(subDescriptor, true)
subDescriptor.classLoader = null
}
@@ -737,12 +729,12 @@ object DynamicPlugins {
val pluginId = pluginDescriptor.pluginId
app.unloadServices(pluginDescriptor.appContainerDescriptor.services, pluginId)
val appMessageBus = app.messageBus as MessageBusEx
pluginDescriptor.appContainerDescriptor.listeners?.let { appMessageBus.unsubscribeLazyListeners(pluginId, it) }
pluginDescriptor.appContainerDescriptor.listeners?.let { appMessageBus.unsubscribeLazyListeners(pluginDescriptor, it) }
for (project in openedProjects) {
(project as ComponentManagerImpl).unloadServices(pluginDescriptor.projectContainerDescriptor.services, pluginId)
pluginDescriptor.projectContainerDescriptor.listeners?.let {
((project as ComponentManagerImpl).messageBus as MessageBusEx).unsubscribeLazyListeners(pluginId, it)
((project as ComponentManagerImpl).messageBus as MessageBusEx).unsubscribeLazyListeners(pluginDescriptor, it)
}
val moduleServices = pluginDescriptor.moduleContainerDescriptor.services
@@ -807,16 +799,13 @@ object DynamicPlugins {
}
}
@JvmStatic
@JvmOverloads
fun loadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl, checkImplementationDetailDependencies: Boolean = true): Boolean {
return loadPlugin(pluginDescriptor, checkImplementationDetailDependencies,
lazy(LazyThreadSafetyMode.NONE) { OptionalDependencyDescriptorLoader() })
fun loadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl): Boolean {
return loadPlugin(pluginDescriptor, checkImplementationDetailDependencies = true)
}
private fun loadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl,
checkImplementationDetailDependencies: Boolean,
loader: Lazy<OptionalDependencyDescriptorLoader>): Boolean {
fun loadPlugin(pluginDescriptor: IdeaPluginDescriptorImpl,
checkImplementationDetailDependencies: Boolean = true,
classLoaderForTest: ClassLoader? = null): Boolean {
if (classloadersFromUnloadedPlugins[pluginDescriptor.pluginId] != null) {
LOG.info("Requiring restart for loading plugin ${pluginDescriptor.pluginId}" +
" because previous version of the plugin wasn't fully unloaded")
@@ -825,23 +814,17 @@ object DynamicPlugins {
val loadStartTime = System.currentTimeMillis()
val app = ApplicationManager.getApplication() as ApplicationImpl
val classLoaderConfigurator: ClassLoaderConfigurator?
if (app.isUnitTestMode) {
classLoaderConfigurator = null
}
else {
classLoaderConfigurator = PluginManagerCore.createClassLoaderConfiguratorForDynamicPlugin(pluginDescriptor)
classLoaderConfigurator.configure(pluginDescriptor)
}
val pluginSet = PluginManagerCore.getPluginSet().concat(pluginDescriptor)
val classLoaderConfigurator = ClassLoaderConfigurator(pluginSet)
classLoaderConfigurator.configure(pluginDescriptor, classLoaderForTest)
app.messageBus.syncPublisher(DynamicPluginListener.TOPIC).beforePluginLoaded(pluginDescriptor)
app.runWriteAction {
try {
addToLoadedPlugins(pluginDescriptor)
val pluginStateChecker = PluginStateChecker(classLoaderConfigurator?.idMap)
val listenerCallbacks = mutableListOf<Runnable>()
loadPluginDescriptor(pluginDescriptor, app, pluginStateChecker, listenerCallbacks)
loadOptionalDependenciesOnPlugin(pluginDescriptor, loader, pluginStateChecker, classLoaderConfigurator, listenerCallbacks)
loadPluginDescriptor(pluginDescriptor, app, listenerCallbacks)
loadOptionalDependenciesOnPlugin(pluginDescriptor, classLoaderConfigurator, pluginSet, listenerCallbacks)
clearPluginClassLoaderParentListCache()
for (openProject in ProjectUtil.getOpenProjects()) {
@@ -865,7 +848,7 @@ object DynamicPlugins {
processImplementationDetailDependenciesOnPlugin(pluginDescriptor) { dependentDescriptor ->
val dependencies = dependentDescriptor.pluginDependencies
if (dependencies.all { it.isOptional || PluginManagerCore.getPlugin(it.pluginId) != null }) {
if (!loadPlugin(dependentDescriptor, checkImplementationDetailDependencies = false, loader = loader)) {
if (!loadPlugin(dependentDescriptor, checkImplementationDetailDependencies = false)) {
implementationDetailsLoadedWithoutRestart = false
}
}
@@ -878,7 +861,7 @@ object DynamicPlugins {
private fun addToLoadedPlugins(pluginDescriptor: IdeaPluginDescriptorImpl) {
var foundExistingPlugin = false
val newPlugins = PluginManagerCore.getPlugins().map {
val newPlugins = PluginManagerCore.getPluginSet().allPlugins.map {
if (it.pluginId == pluginDescriptor.pluginId) {
foundExistingPlugin = true
pluginDescriptor
@@ -892,7 +875,7 @@ object DynamicPlugins {
PluginManager.getInstance().setPlugins(newPlugins)
}
else {
PluginManager.getInstance().setPlugins(PluginManagerCore.getPlugins().asSequence().plus(pluginDescriptor).toList())
PluginManager.getInstance().setPlugins(PluginManagerCore.getPluginSet().allPlugins.plus(pluginDescriptor))
}
}
@@ -1022,74 +1005,32 @@ private fun processImplementationDetailDependenciesOnPlugin(pluginDescriptor: Id
}
}
private class OptionalDependencyDescriptorLoader {
private val listContext = DescriptorListLoadingContext(disabledPlugins = DisabledPluginsState.disabledPlugins())
fun load(mainDescriptor: IdeaPluginDescriptorImpl, dependencyConfigFile: String): IdeaPluginDescriptorImpl? {
val pathResolver = createPathResolverForPlugin(mainDescriptor, true)
val zipFilePool = ZipFilePoolImpl()
ZipFilePool.POOL = zipFilePool
try {
val dataLoader: DataLoader
if (mainDescriptor.pluginPath.toString().endsWith(".jar")) {
val resolver = zipFilePool.load(mainDescriptor.pluginPath)
dataLoader = ImmutableZipFileDataLoader(resolver, mainDescriptor.pluginPath, zipFilePool)
}
else {
dataLoader = LocalFsDataLoader(mainDescriptor.pluginPath)
}
val raw = pathResolver.resolvePath(readContext = listContext,
dataLoader = dataLoader,
relativePath = dependencyConfigFile,
readInto = null)!!
// readExternal requires not-null id
val subDescriptor = mainDescriptor.createSub(raw, dependencyConfigFile)
subDescriptor.readExternal(raw = raw, pathResolver = pathResolver, context = listContext, isSub = true, dataLoader = dataLoader)
return subDescriptor
}
catch (e: Exception) {
LOG.info("Can't resolve optional dependency on plugin being loaded/unloaded: config file $dependencyConfigFile", e)
return null
}
finally {
ZipFilePool.POOL = null
// help GC
zipFilePool.clear()
}
}
}
/**
* Load all sub plugins that depend on specified [dependencyPlugin].
*/
private fun loadOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDescriptorImpl,
loader: Lazy<OptionalDependencyDescriptorLoader>,
pluginStateChecker: PluginStateChecker,
classLoaderConfigurator: ClassLoaderConfigurator?,
classLoaderConfigurator: ClassLoaderConfigurator,
pluginSet: PluginSet,
listenerCallbacks: MutableList<Runnable>) {
val mainToSub = LinkedHashMap<IdeaPluginDescriptorImpl, MutableList<IdeaPluginDescriptorImpl>>()
// 1. read and collect optional descriptors
for (descriptor in PluginManagerCore.getLoadedPlugins(null)) {
for (dependency in descriptor.pluginDependencies) {
readAndProcessOptionalDependencyDescriptor(dependencyPlugin.pluginId, descriptor, dependency, loader) { subDescriptor: IdeaPluginDescriptorImpl ->
mainToSub.computeIfAbsent(descriptor) { mutableListOf() }.add(subDescriptor)
}
}
// 1. collect optional descriptors
val mainToModule = LinkedHashMap<IdeaPluginDescriptorImpl, MutableList<IdeaPluginDescriptorImpl>>()
processOptionalDependenciesOnPlugin(dependencyPlugin, pluginSet, isLoaded = false) { mainDescriptor, subDescriptor ->
mainToModule.computeIfAbsent(mainDescriptor) { mutableListOf() }.add(subDescriptor)
}
if (mainToSub.isEmpty()) {
if (mainToModule.isEmpty()) {
return
}
// 2. setup classloaders
classLoaderConfigurator?.configureDependenciesIfNeeded(mainToSub, dependencyPlugin)
classLoaderConfigurator.configureDependenciesIfNeeded(mainToModule, dependencyPlugin)
val app = ApplicationManager.getApplication() as ComponentManagerImpl
// 3. load into service container
for (entry in mainToSub.entries) {
for (entry in mainToModule.entries) {
for (subDescriptor in entry.value) {
loadPluginDescriptor(subDescriptor, app, pluginStateChecker, listenerCallbacks)
loadPluginDescriptor(subDescriptor, app, listenerCallbacks)
}
}
}
@@ -1109,101 +1050,22 @@ private fun clearPluginClassLoaderParentListCache(descriptor: IdeaPluginDescript
}
}
private fun readAndProcessOptionalDependencyDescriptor(dependencyPluginId: PluginId,
mainDescriptor: IdeaPluginDescriptorImpl,
dependency: PluginDependency,
loader: Lazy<OptionalDependencyDescriptorLoader>,
processor: (pluginDescriptor: IdeaPluginDescriptorImpl) -> Unit) {
if (!dependency.isOptional) {
return
}
val newPluginDescriptor = dependency.configFile?.let { loader.value.load(mainDescriptor, it) } ?: return
if (dependency.pluginId == dependencyPluginId) {
dependency.subDescriptor = newPluginDescriptor
dependency.isDisabledOrBroken = false
processor(newPluginDescriptor)
}
for (subDependency in newPluginDescriptor.pluginDependencies) {
readAndProcessOptionalDependencyDescriptor(dependencyPluginId, mainDescriptor, subDependency, loader, processor)
}
}
private fun updateDependenciesStatus(pluginDescriptor: IdeaPluginDescriptorImpl, pluginStateChecker: PluginStateChecker) {
for (dependency in pluginDescriptor.pluginDependencies) {
val subDescriptor = dependency.subDescriptor ?: continue
if (pluginStateChecker.isPluginOrModuleLoaded(dependency.pluginId)) {
dependency.isDisabledOrBroken = false
updateDependenciesStatus(subDescriptor, pluginStateChecker)
}
else {
dependency.isDisabledOrBroken = true
}
}
}
private fun loadPluginDescriptor(pluginDescriptor: IdeaPluginDescriptorImpl,
app: ComponentManagerImpl,
pluginStateChecker: PluginStateChecker,
listenerCallbacks: MutableList<Runnable>) {
updateDependenciesStatus(pluginDescriptor, pluginStateChecker)
app: ComponentManagerImpl,
listenerCallbacks: MutableList<Runnable>) {
val list = listOf(pluginDescriptor)
app.registerComponents(plugins = list,
app = ApplicationManager.getApplication(),
precomputedExtensionModel = null,
listenerCallbacks = listenerCallbacks)
for (openProject in ProjectUtil.getOpenProjects()) {
(openProject as ComponentManagerImpl).registerComponents(list, ApplicationManager.getApplication(), null, listenerCallbacks)
for (module in ModuleManager.getInstance(openProject).modules) {
(module as ComponentManagerImpl).registerComponents(list, ApplicationManager.getApplication(), null, listenerCallbacks)
}
}
val list = listOf(pluginDescriptor)
app.registerComponents(plugins = list,
app = ApplicationManager.getApplication(),
precomputedExtensionModel = null,
listenerCallbacks = listenerCallbacks)
for (openProject in ProjectUtil.getOpenProjects()) {
(openProject as ComponentManagerImpl).registerComponents(list, ApplicationManager.getApplication(), null, listenerCallbacks)
for (module in ModuleManager.getInstance(openProject).modules) {
(module as ComponentManagerImpl).registerComponents(list, ApplicationManager.getApplication(), null, listenerCallbacks)
}
}
val actionManager = ActionManager.getInstance() as ActionManagerImpl
actionManager.registerActions(list, false)
}
private class PluginStateChecker(private val loadedIdMap: Map<PluginId, IdeaPluginDescriptorImpl>? = null) {
companion object {
@JvmStatic
private val NULL_PLUGIN_DESCRIPTOR: IdeaPluginDescriptorImpl
init {
val raw = RawPluginDescriptor()
raw.id = ""
@Suppress("TestOnlyProblems")
NULL_PLUGIN_DESCRIPTOR = IdeaPluginDescriptorImpl(raw, Path.of(""), false, null)
}
}
private val loadedPlugins = PluginManagerCore.getLoadedPlugins(null)
private val moduleToPluginCache = IdentityHashMap<PluginId, IdeaPluginDescriptor>()
private fun findLoadedPluginByModuleDependency(pluginId: PluginId): IdeaPluginDescriptor? {
return moduleToPluginCache.computeIfAbsent(pluginId, Function {
for (descriptor in loadedPlugins) {
if (descriptor.modules.contains(it)) {
return@Function descriptor
}
}
NULL_PLUGIN_DESCRIPTOR
}).takeIf { it !== NULL_PLUGIN_DESCRIPTOR }
}
fun isPluginOrModuleLoaded(pluginId: PluginId): Boolean {
return when {
PluginManagerCore.isModuleDependency(pluginId) -> findLoadedPluginByModuleDependency(pluginId) != null
loadedIdMap != null -> loadedIdMap.containsKey(pluginId)
else -> loadedPlugins.any { it.pluginId == pluginId }
}
}
fun findDescriptor(pluginId: PluginId): IdeaPluginDescriptorImpl? {
return loadedPlugins.find { it.pluginId == pluginId }
}
(ActionManager.getInstance() as ActionManagerImpl).registerActions(list)
}
private fun analyzeSnapshot(hprofPath: String, pluginId: PluginId): String {
@@ -1230,28 +1092,59 @@ private fun createDisposeTreePredicate(pluginDescriptor: IdeaPluginDescriptorImp
}
}
private fun processLoadedOptionalDependenciesOnPlugin(dependencyPluginId: PluginId,
processor: (mainDescriptor: IdeaPluginDescriptorImpl, subDescriptor: IdeaPluginDescriptorImpl?) -> Boolean) {
for (descriptor in PluginManagerCore.getLoadedPlugins(null)) {
if (!processLoadedOptionalDependenciesOnPlugin(dependencyPluginId, descriptor, processor)) {
break
private fun processOptionalDependenciesOnPlugin(dependencyPlugin: IdeaPluginDescriptorImpl,
pluginSet: PluginSet,
isLoaded: Boolean,
processor: (pluginDescriptor: IdeaPluginDescriptorImpl,
moduleDescriptor: IdeaPluginDescriptorImpl) -> Boolean) {
val wantedIds = HashSet<String>(1 + dependencyPlugin.content.modules.size)
wantedIds.add(dependencyPlugin.id.idString)
for (module in dependencyPlugin.content.modules) {
wantedIds.add(module.name)
}
for (plugin in pluginSet.loadedPlugins) {
if (!processOptionalDependenciesInOldFormatOnPlugin(dependencyPlugin.id, plugin, isLoaded, processor)) {
return
}
for (moduleItem in plugin.content.modules) {
val module = moduleItem.requireDescriptor()
val isModuleLoaded = module.classLoader != null
if (isModuleLoaded != isLoaded) {
continue
}
for (item in module.dependencies.modules) {
if (wantedIds.contains(item.name) && !processor(plugin, module)) {
return
}
}
}
}
}
private fun processLoadedOptionalDependenciesOnPlugin(dependencyPluginId: PluginId,
mainDescriptor: IdeaPluginDescriptorImpl,
processor: (mainDescriptor: IdeaPluginDescriptorImpl, subDescriptor: IdeaPluginDescriptorImpl?) -> Boolean): Boolean {
private fun processOptionalDependenciesInOldFormatOnPlugin(dependencyPluginId: PluginId,
mainDescriptor: IdeaPluginDescriptorImpl,
isLoaded: Boolean,
processor: (mainDescriptor: IdeaPluginDescriptorImpl, subDescriptor: IdeaPluginDescriptorImpl) -> Boolean): Boolean {
for (dependency in mainDescriptor.pluginDependencies) {
if (!dependency.isOptional || dependency.isDisabledOrBroken) {
if (!dependency.isOptional) {
continue
}
if (dependency.pluginId == dependencyPluginId && !processor(mainDescriptor, dependency.subDescriptor)) {
val subDescriptor = dependency.subDescriptor ?: continue
val isModuleLoaded = subDescriptor.classLoader != null
if (isModuleLoaded != isLoaded) {
continue
}
if (dependency.pluginId == dependencyPluginId && !processor(mainDescriptor, subDescriptor)) {
return false
}
if (!processLoadedOptionalDependenciesOnPlugin(dependencyPluginId, dependency.subDescriptor ?: continue, processor)) {
if (!processOptionalDependenciesInOldFormatOnPlugin(dependencyPluginId, subDescriptor, isLoaded, processor)) {
return false
}
}
@@ -1265,7 +1158,7 @@ private fun doCheckExtensionsCanUnloadWithoutRestart(extensions: Map<String, Lis
app: Application,
optionalDependencyPluginId: PluginId?,
context: List<IdeaPluginDescriptorImpl>,
pluginStateChecker: PluginStateChecker): String? {
pluginSet: PluginSet): String? {
val firstProject = ProjectUtil.getOpenProjects().firstOrNull()
val anyProject = firstProject ?: ProjectManager.getInstance().defaultProject
val anyModule = firstProject?.let { ModuleManager.getInstance(it).modules.firstOrNull() }
@@ -1273,11 +1166,11 @@ private fun doCheckExtensionsCanUnloadWithoutRestart(extensions: Map<String, Lis
val seenPlugins: MutableSet<IdeaPluginDescriptorImpl> = Collections.newSetFromMap(IdentityHashMap())
epLoop@ for (epName in extensions.keys) {
seenPlugins.clear()
val result = findPluginExtensionPointRecursive(pluginDescriptor = baseDescriptor ?: descriptor,
epName = epName,
pluginStateChecker = pluginStateChecker,
context = context,
seenPlugins = seenPlugins)
val result = findLoadedPluginExtensionPointRecursive(pluginDescriptor = baseDescriptor ?: descriptor,
epName = epName,
pluginSet = pluginSet,
context = context,
seenPlugins = seenPlugins)
if (result != null) {
val (pluginExtensionPoint, foundInDependencies) = result
// descriptor.pluginId is null when we check the optional dependencies of the plugin which is being loaded
@@ -1304,9 +1197,9 @@ private fun doCheckExtensionsCanUnloadWithoutRestart(extensions: Map<String, Lis
}
if (anyModule == null) {
val corePlugin = PluginManagerCore.getPlugin(PluginManagerCore.CORE_ID)
val corePlugin = pluginSet.findEnabledPlugin(PluginManagerCore.CORE_ID)
if (corePlugin != null) {
val coreEP = findPluginExtensionPoint(corePlugin as IdeaPluginDescriptorImpl, epName)
val coreEP = findPluginExtensionPoint(corePlugin, epName)
if (coreEP != null) {
if (!coreEP.isDynamic) {
return getNonDynamicUnloadError(epName, baseDescriptor, descriptor, optionalDependencyPluginId)
@@ -1356,27 +1249,36 @@ private fun findPluginExtensionPoint(pluginDescriptor: IdeaPluginDescriptorImpl,
?: findContainerExtensionPoint(pluginDescriptor.moduleContainerDescriptor)
}
private fun findPluginExtensionPointRecursive(pluginDescriptor: IdeaPluginDescriptorImpl,
epName: String,
pluginStateChecker: PluginStateChecker,
context: List<IdeaPluginDescriptorImpl>,
seenPlugins: MutableSet<IdeaPluginDescriptorImpl>): Pair<ExtensionPointDescriptor, Boolean>? {
if (pluginDescriptor in seenPlugins) {
private fun findLoadedPluginExtensionPointRecursive(pluginDescriptor: IdeaPluginDescriptorImpl,
epName: String,
pluginSet: PluginSet,
context: List<IdeaPluginDescriptorImpl>,
seenPlugins: MutableSet<IdeaPluginDescriptorImpl>): Pair<ExtensionPointDescriptor, Boolean>? {
if (!seenPlugins.add(pluginDescriptor)) {
return null
}
seenPlugins.add(pluginDescriptor)
findPluginExtensionPoint(pluginDescriptor, epName)?.let { return it to false }
for (dependency in pluginDescriptor.pluginDependencies) {
if (pluginStateChecker.isPluginOrModuleLoaded(dependency.pluginId) || context.any { it.id == dependency.pluginId }) {
if (pluginSet.isPluginEnabled(dependency.pluginId) || context.any { it.id == dependency.pluginId }) {
dependency.subDescriptor?.let { subDescriptor ->
findPluginExtensionPointRecursive(subDescriptor, epName, pluginStateChecker, context, seenPlugins)?.let { return it }
findLoadedPluginExtensionPointRecursive(subDescriptor, epName, pluginSet, context, seenPlugins)?.let { return it }
}
pluginStateChecker.findDescriptor(dependency.pluginId)?.let { dependencyDescriptor ->
findPluginExtensionPointRecursive(dependencyDescriptor, epName, pluginStateChecker, context, seenPlugins)?.let { return it.first to true }
pluginSet.findEnabledPlugin(dependency.pluginId)?.let { dependencyDescriptor ->
findLoadedPluginExtensionPointRecursive(dependencyDescriptor, epName, pluginSet, context, seenPlugins)?.let { return it.first to true }
}
}
}
for (item in pluginDescriptor.dependencies.modules) {
pluginSet.findEnabledModule(item.name)?.requireDescriptor()?.let { d ->
findLoadedPluginExtensionPointRecursive(d, epName, pluginSet, context, seenPlugins)?.let { return it.first to true }
}
}
for (item in pluginDescriptor.dependencies.plugins) {
pluginSet.findEnabledPlugin(item.id)?.let { d ->
findLoadedPluginExtensionPointRecursive(d, epName, pluginSet, context, seenPlugins)?.let { return it.first to true }
}
}
return null
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins;
import com.intellij.CommonBundle;
@@ -389,7 +389,7 @@ public final class PluginInstaller {
return false;
}
return DynamicPlugins.loadPlugin(targetDescriptor);
return DynamicPlugins.INSTANCE.loadPlugin(targetDescriptor);
}
private static @NotNull Set<String> findNotInstalledPluginDependencies(@NotNull List<? extends IdeaPluginDependency> dependencies,

View File

@@ -10,10 +10,7 @@ import com.intellij.ide.AssertiveRepaintManager;
import com.intellij.ide.BootstrapBundle;
import com.intellij.ide.CliResult;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.customize.AbstractCustomizeWizardStep;
import com.intellij.ide.customize.CommonCustomizeIDEWizardDialog;
import com.intellij.ide.customize.CustomizeIDEWizardDialog;
import com.intellij.ide.customize.CustomizeIDEWizardStepsProvider;
import com.intellij.ide.gdpr.Agreements;
import com.intellij.ide.gdpr.ConsentOptions;
import com.intellij.ide.gdpr.EndUserAgreement;
@@ -41,6 +38,7 @@ import com.intellij.ui.IconManager;
import com.intellij.ui.mac.MacOSApplicationProvider;
import com.intellij.ui.scale.JBUIScale;
import com.intellij.util.EnvironmentUtil;
import com.intellij.util.lang.Java11Shim;
import com.intellij.util.lang.ZipFilePool;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.accessibility.ScreenReader;
@@ -207,6 +205,7 @@ public final class StartupUtil {
ZipFilePool.POOL = new ZipFilePoolImpl();
PluginManagerCore.scheduleDescriptorLoading();
}
Java11Shim.INSTANCE = new Java11ShimImpl();
forkJoinPool.execute(() -> {
setupSystemLibraries();
@@ -974,4 +973,21 @@ public final class StartupUtil {
}
}
}
public static final class Java11ShimImpl extends Java11Shim {
@Override
public <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
return Map.copyOf(map);
}
@Override
public <E> Set<E> copyOf(Set<? extends E> collection) {
return Set.copyOf(collection);
}
@Override
public <E> List<E> copyOf(List<? extends E> collection) {
return List.copyOf(collection);
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.actionSystem.impl;
import com.intellij.AbstractBundle;
@@ -11,7 +11,10 @@ import com.intellij.icons.AllIcons;
import com.intellij.ide.ActivityTracker;
import com.intellij.ide.DataManager;
import com.intellij.ide.ProhibitAWTEvents;
import com.intellij.ide.plugins.*;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.ide.plugins.RawPluginDescriptor;
import com.intellij.ide.ui.customization.ActionUrl;
import com.intellij.ide.ui.customization.CustomActionsSchema;
import com.intellij.idea.IdeaLogger;
@@ -47,6 +50,7 @@ import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.util.text.Strings;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.serviceContainer.ContainerUtilKt;
import com.intellij.ui.icons.IconLoadMeasurer;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.ReflectionUtil;
@@ -58,6 +62,7 @@ import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import kotlin.Unit;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -134,7 +139,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
}
}
registerActions(PluginManagerCore.getLoadedPlugins(null), true);
registerActions(PluginManagerCore.getLoadedPlugins(null));
EP.forEachExtensionSafe(customizer -> customizer.customize(this));
DYNAMIC_EP_NAME.forEachExtensionSafe(customizer -> customizer.registerActions(this));
@@ -158,26 +163,12 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
}
@ApiStatus.Internal
public void registerActions(@NotNull List<IdeaPluginDescriptorImpl> plugins, @SuppressWarnings("unused") boolean initialStartup) {
public void registerActions(@NotNull List<IdeaPluginDescriptorImpl> plugins) {
KeymapManagerEx keymapManager = Objects.requireNonNull(KeymapManagerEx.getInstanceEx());
for (IdeaPluginDescriptorImpl plugin : plugins) {
registerPluginActions(plugin, keymapManager);
for (PluginDependency pluginDependency : plugin.pluginDependencies) {
IdeaPluginDescriptorImpl subPlugin = pluginDependency.isDisabledOrBroken ? null : pluginDependency.subDescriptor;
if (subPlugin == null) {
continue;
}
registerPluginActions(subPlugin, keymapManager);
for (PluginDependency subPluginDependency : subPlugin.pluginDependencies) {
IdeaPluginDescriptorImpl subSubPlugin = subPluginDependency.isDisabledOrBroken ? null : subPluginDependency.subDescriptor;
if (subSubPlugin != null) {
registerPluginActions(subSubPlugin, keymapManager);
}
}
}
}
ContainerUtilKt.executeRegisterTask(plugins, it -> {
registerPluginActions(it, keymapManager);
return Unit.INSTANCE;
});
}
private static @NotNull AnActionListener publisher() {
@@ -498,13 +489,11 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
}
@Override
@Nullable
public AnAction getAction(@NotNull String id) {
public @Nullable AnAction getAction(@NotNull String id) {
return getActionImpl(id, false);
}
@Nullable
private AnAction getActionImpl(@NotNull String id, boolean canReturnStub) {
private @Nullable AnAction getActionImpl(@NotNull String id, boolean canReturnStub) {
AnAction action;
synchronized (myLock) {
action = idToAction.get(id);
@@ -1266,20 +1255,17 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
private @Nullable AnAction addToMap(@NotNull String actionId,
@NotNull AnAction action,
@Nullable ProjectType projectType) {
AnAction chameleonAction = idToAction.computeIfPresent(actionId,
(__, old) -> old instanceof ChameleonAction
? old
: new ChameleonAction(old, projectType));
if (chameleonAction != null) {
return ((ChameleonAction)chameleonAction).addAction(action, projectType);
}
else {
AnAction result = projectType != null ?
new ChameleonAction(action, projectType) :
action;
AnAction chameleonAction = idToAction.computeIfPresent(actionId, (__, old) -> {
return old instanceof ChameleonAction ? old : new ChameleonAction(old, projectType);
});
if (chameleonAction == null) {
AnAction result = projectType == null ? action : new ChameleonAction(action, projectType);
idToAction.put(actionId, result);
return result;
}
else {
return ((ChameleonAction)chameleonAction).addAction(action, projectType);
}
}
private void reportActionIdCollision(@NotNull String actionId,
@@ -1299,7 +1285,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
@Override
public void registerAction(@NotNull String actionId, @NotNull AnAction action) {
registerAction(actionId, action, null);
registerAction(actionId, action, null, null);
}
@Override

View File

@@ -7,7 +7,7 @@
<xi:include href="/META-INF/SpellCheckerPlugin.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/JsonPlugin.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:include href="/intellij.json.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/structuralsearch.xml" xpointer="xpointer(/idea-plugin/*)">

View File

@@ -4,6 +4,7 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/testSnapshots" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View File

@@ -0,0 +1,12 @@
{
"intellij.angularJs" : {
"name" : "intellij.angularJs",
"package" : null,
"descriptor" : "/intellij.angularJs/META-INF/plugin.xml",
"content" : [ {
"name" : "intellij.angularJs/diagram",
"package" : "org.angularjs.diagram",
"descriptor" : "/intellij.angularJs/intellij.angularJs.diagram.xml"
} ]
}
}

View File

@@ -0,0 +1,6 @@
1 errors:
[1]: ------------------------------
`dependencies` must be specified only for plugin in a new format: package prefix is not specified (
referencingDescriptorFile=/dependent/META-INF/plugin.xml
)
-----------------------------------

View File

@@ -0,0 +1,15 @@
{
"intellij.dependency" : {
"name" : "intellij.dependency",
"package" : "dependencyPackagePrefix",
"descriptor" : "/dependency/META-INF/plugin.xml"
},
"intellij.dependent" : {
"name" : "intellij.dependent",
"package" : "dependentPackagePrefix",
"descriptor" : "/dependent/META-INF/plugin.xml",
"dependencies" : [ {
"plugin" : "dependency"
} ]
}
}

View File

@@ -0,0 +1,7 @@
1 errors:
[1]: ------------------------------
Plugin not found: incorrectId (
entry=XmlElement(name=plugin, attributes={id=incorrectId}, children=[], content=null),
referencingDescriptorFile=/dependent/META-INF/plugin.xml
)
-----------------------------------

View File

@@ -0,0 +1,14 @@
1 errors:
[1]: ------------------------------
Dependency on plugin must be specified using `plugin` and not `module` (
entry=XmlElement(name=module, attributes={name=intellij.dependent}, children=[], content=null),
referencingDescriptorFile=/dependent/META-INF/plugin.xml
)
Proposed fix:
Change dependency element to:
<plugin id="dependent"/>
-----------------------------------

View File

@@ -0,0 +1,7 @@
1 errors:
[1]: ------------------------------
Module not found: com.intellij.diagram (
entry=XmlElement(name=module, attributes={name=com.intellij.diagram}, children=[], content=null),
referencingDescriptorFile=/intellij.angularJs/intellij.angularJs.diagram.xml
)
-----------------------------------

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.intellij.ide.plugins.cl.PluginClassLoader
@@ -6,78 +6,89 @@ import com.intellij.openapi.util.BuildNumber
import com.intellij.testFramework.assertions.Assertions.assertThat
import com.intellij.testFramework.assertions.Assertions.assertThatThrownBy
import com.intellij.testFramework.rules.InMemoryFsRule
import com.intellij.util.io.Murmur3_32Hash
import com.intellij.util.io.directoryStreamIfExists
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
import java.nio.file.Path
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
private val buildNumber = BuildNumber.fromString("2042.0")!!
internal class ClassLoaderConfiguratorTest {
@Rule
@JvmField
val inMemoryFs = InMemoryFsRule()
@Rule @JvmField val name = TestName()
@Rule @JvmField val inMemoryFs = InMemoryFsRule()
@Test
fun packageForOptionalMustBeSpecified() {
assertThatThrownBy {
loadPlugins {
PluginBuilder().noDepends()
}
}.hasMessageStartingWith("Sub descriptor must specify package if it is specified for main plugin descriptor ")
loadPlugins(modulePackage = null)
}.hasMessage("Package is not specified (module=PluginDescriptor(name=p_dependent_i0ihh8, id=p_dependent_i0ihh8, descriptorPath=com.example.sub.xml, path=/p_dependent_i0ihh8, version=2042.0, package=null))")
}
@Test
fun packageForOptionalMustBeDifferent() {
assertThatThrownBy {
loadPlugins {
PluginBuilder().noDepends().packagePrefix("com.example")
}
}.hasMessageStartingWith("Sub descriptor must not specify the same package as main plugin descriptor [")
loadPlugins(modulePackage = "com.example")
}.hasMessage("Package prefix com.example is already used (module=PluginDescriptor(name=p_dependent_12m42wo, id=p_dependent_12m42wo, descriptorPath=com.example.sub.xml, path=/p_dependent_12m42wo, version=2042.0, package=com.example))")
}
@Test
fun packageMustBeUnique() {
assertThatThrownBy {
loadPlugins {
PluginBuilder().noDepends().packagePrefix("com.bar")
}
}.hasMessageStartingWith("Package prefix com.bar is already used [")
loadPlugins(modulePackage = "com.bar")
}.hasMessage("Package prefix com.bar is already used (module=PluginDescriptor(name=p_dependent_x9e2t2, id=p_dependent_x9e2t2, descriptorPath=com.example.sub.xml, path=/p_dependent_x9e2t2, version=2042.0, package=com.bar))")
}
@Test
fun regularPluginClassLoaderIsUsedIfPackageSpecified() {
loadPlugins {
PluginBuilder().noDepends().packagePrefix("com.example.extraSupportedFeature")
}.getEnabledPlugins().get(1).pluginDependencies.get(0).subDescriptor!!.classLoader!!.javaClass === PluginClassLoader::class.java
val plugin = loadPlugins(modulePackage = "com.example.extraSupportedFeature").getEnabledPlugins().get(1)
assertThat(plugin.content.modules.get(0).requireDescriptor().classLoader).isInstanceOf(PluginClassLoader::class.java)
}
private fun loadPlugins(dependent: () -> PluginBuilder): PluginLoadingResult {
@Suppress("PluginXmlValidity")
private fun loadPlugins(modulePackage: String?): PluginLoadingResult {
val rootDir = inMemoryFs.fs.getPath("/")
val pluginDependency = PluginBuilder()
.noDepends()
.packagePrefix("com.bar")
.extensionPoints(
"""<extensionPoint qualifiedName="bar.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>""")
.buildToAutoGeneratedSubDir(rootDir)
// toUnsignedLong - avoid - symbol
val pluginIdSuffix = Integer.toUnsignedLong(Murmur3_32Hash.MURMUR3_32.hashString(javaClass.name + name.methodName)).toString(36)
val dependencyId = "p_dependency_$pluginIdSuffix"
plugin(rootDir, """
<idea-plugin package="com.bar">
<id>$dependencyId</id>
<extensionPoints>
<extensionPoint qualifiedName="bar.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>"
</extensionPoints>
</idea-plugin>
""")
val dependentPluginId = "p_dependent_$pluginIdSuffix"
plugin(rootDir, """
<idea-plugin package="com.example">
<id>$dependentPluginId</id>
<content>
<module name="com.example.sub"/>
</content>
</idea-plugin>
""")
module(rootDir, dependentPluginId, "com.example.sub", """
<idea-plugin ${modulePackage?.let { """package="$it"""" } ?: ""}>
<!-- dependent must not be empty, add some extension -->
<extensionPoints>
<extensionPoint qualifiedName="bar.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>"
</extensionPoints>
</idea-plugin>
""")
PluginBuilder()
.noDepends()
.packagePrefix("com.example")
// dependent must not be empty, add some extension
.depends(pluginDependency.buildToAutoGeneratedSubDir(rootDir).id,
dependent().extensions("""<barExtension key="foo" implementationClass="y"/>""", "bar"))
.buildToAutoGeneratedSubDir(rootDir)
val loadResult = loadDescriptors(rootDir)
val plugins = loadResult.getEnabledPlugins()
assertThat(plugins).hasSize(2)
val classLoaderConfigurator = ClassLoaderConfigurator(true, PluginManagerCore::class.java.classLoader, loadResult.idMap, emptyMap())
plugins.forEach(Consumer(classLoaderConfigurator::configure))
val classLoaderConfigurator = ClassLoaderConfigurator(PluginSet(plugins, plugins))
plugins.forEach(classLoaderConfigurator::configure)
return loadResult
}
}
@@ -87,7 +98,7 @@ private fun loadDescriptors(dir: Path): PluginLoadingResult {
val context = DescriptorListLoadingContext(disabledPlugins = Collections.emptySet(), result = result)
// constant order in tests
val paths: List<Path> = dir.directoryStreamIfExists { it.sorted() }!!
val paths = dir.directoryStreamIfExists { it.sorted() }!!
context.use {
for (file in paths) {
result.add(loadDescriptor(file, false, context) ?: continue, false)

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Suppress("UsePropertyAccessSyntax")
package com.intellij.ide.plugins
@@ -24,6 +24,7 @@ import com.intellij.openapi.components.Storage
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.module.ModuleConfigurationEditor
import com.intellij.openapi.options.Configurable
@@ -109,7 +110,7 @@ class DynamicPluginsTest {
DisabledPluginsState.saveDisabledPlugins(PathManager.getConfigDir())
val newDescriptor = loadDescriptorInTest(path)
PluginManagerCore.createClassLoaderConfiguratorForDynamicPlugin(newDescriptor).configure(newDescriptor)
ClassLoaderConfigurator(PluginManagerCore.getPluginSet().concat(newDescriptor)).configure(newDescriptor)
DynamicPlugins.loadPlugin(newDescriptor)
try {
assertThat(PluginManagerCore.getPlugin(descriptor.pluginId)?.pluginClassLoader as? PluginClassLoader).isNotNull()
@@ -188,22 +189,29 @@ class DynamicPluginsTest {
@Test
fun loadOptionalDependency() {
// match production - on plugin load/unload ActionManager is already initialized
val actionManager = ActionManager.getInstance()
val beforeList = PluginManagerCore.getLoadedPlugins()
val plugin2Builder = PluginBuilder().randomId("bar")
val plugin1Disposable = loadPluginWithOptionalDependency(
PluginBuilder().randomId("foo").packagePrefix("org.foo"),
PluginBuilder().actions("""<group id="FooBarGroup"></group>""").packagePrefix("org.foo.bar"),
plugin2Builder
)
val plugin2Builder = PluginBuilder().randomId("bar").packagePrefix("bar")
val pluginDescriptor = PluginBuilder()
.randomId("foo")
.packagePrefix("org.foo")
.module(
"intellij.org.foo",
PluginBuilder().actions("""<group id="FooBarGroup"></group>""").packagePrefix("org.foo.bar").dependency(plugin2Builder.id)
)
val plugin1Disposable = loadPluginWithText(pluginDescriptor)
assertThat(actionManager.getAction("FooBarGroup")).isNull()
try {
val pluginToDisposable = loadPluginWithText(plugin2Builder)
try {
assertThat(ActionManager.getInstance().getAction("FooBarGroup")).isNotNull()
assertThat(actionManager.getAction("FooBarGroup")).isNotNull()
}
finally {
Disposer.dispose(pluginToDisposable)
}
assertThat(ActionManager.getInstance().getAction("FooBarGroup")).isNull()
assertThat(actionManager.getAction("FooBarGroup")).isNull()
}
finally {
Disposer.dispose(plugin1Disposable)
@@ -235,12 +243,21 @@ class DynamicPluginsTest {
val pluginTwoBuilder = PluginBuilder()
.randomId("optionalDependencyExtension-two")
.packagePrefix("org.foo.two")
.extensionPoints("""<extensionPoint qualifiedName="bar.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>""")
.extensionPoints(
"""<extensionPoint qualifiedName="bar.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>""")
val plugin1Disposable = loadPluginWithOptionalDependency(
PluginBuilder().randomId("optionalDependencyExtension-one").packagePrefix("org.foo.one"),
PluginBuilder().extensions("""<barExtension key="foo" implementationClass="y"/>""", "bar").packagePrefix("org.foo"),
pluginTwoBuilder
val plugin1Disposable = loadPluginWithText(
PluginBuilder()
.randomId("optionalDependencyExtension-one")
.packagePrefix("org.foo.one")
.noDepends()
.module(
moduleName = "intellij.foo.one.module1",
moduleDescriptor = PluginBuilder()
.extensions("""<barExtension key="foo" implementationClass="y"/>""", "bar")
.dependency(pluginTwoBuilder.id)
.packagePrefix("org.foo"),
)
)
try {
val plugin2Disposable = loadPluginWithText(pluginTwoBuilder)
@@ -266,12 +283,15 @@ class DynamicPluginsTest {
fun loadOptionalDependencyOwnExtension() {
val barBuilder = PluginBuilder().randomId("bar").packagePrefix("bar")
val fooBuilder = PluginBuilder().randomId("foo").packagePrefix("foo")
.extensionPoints("""<extensionPoint qualifiedName="foo.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>""")
val plugin1Disposable = loadPluginWithOptionalDependency(
fooBuilder,
PluginBuilder().extensions("""<barExtension key="foo" implementationClass="y"/>""", "foo").packagePrefix("foo1"),
barBuilder
)
.extensionPoints(
"""<extensionPoint qualifiedName="foo.barExtension" beanClass="com.intellij.util.KeyedLazyInstanceEP" dynamic="true"/>""")
.module("intellij.foo.sub",
PluginBuilder()
.extensions("""<barExtension key="foo" implementationClass="y"/>""", "foo")
.packagePrefix("foo1")
.dependency(barBuilder.id)
)
val plugin1Disposable = loadPluginWithText(fooBuilder)
try {
val ep = ApplicationManager.getApplication().extensionArea.getExtensionPointIfRegistered<KeyedLazyInstanceEP<*>>("foo.barExtension")
assertThat(ep).isNotNull()
@@ -279,7 +299,8 @@ class DynamicPluginsTest {
try {
val extension = ep!!.extensionList.single()
assertThat(extension.key).isEqualTo("foo")
assertThat(extension.pluginDescriptor).isEqualTo(PluginManagerCore.getPlugin(PluginId.getId(fooBuilder.id)))
assertThat(extension.pluginDescriptor)
.isEqualTo(PluginManagerCore.getPluginSet().findEnabledModule(fooBuilder.id)!!.requireDescriptor())
}
finally {
Disposer.dispose(plugin2Disposable)
@@ -323,17 +344,20 @@ class DynamicPluginsTest {
receivedNotifications.clear()
receivedNotifications2.clear()
val pluginTwoBuilder = PluginBuilder().randomId("optionalDependencyListener-two")
val pluginOneDisposable = loadPluginWithOptionalDependency(
PluginBuilder().randomId("optionalDependencyListener-one")
.applicationListeners("""<listener class="${MyUISettingsListener::class.java.name}" topic="com.intellij.ide.ui.UISettingsListener"/>""")
.packagePrefix("org.foo.one"),
PluginBuilder()
.applicationListeners("""<listener class="${MyUISettingsListener2::class.java.name}" topic="com.intellij.ide.ui.UISettingsListener"/>""")
.packagePrefix("org.foo"),
pluginTwoBuilder
)
val pluginTwoBuilder = PluginBuilder().randomId("optionalDependencyListener-two").packagePrefix("optionalDependencyListener-two")
val pluginDescriptor = PluginBuilder().randomId("optionalDependencyListener-one").packagePrefix("optionalDependencyListener-one")
.applicationListeners(
"""<listener class="${MyUISettingsListener::class.java.name}" topic="com.intellij.ide.ui.UISettingsListener"/>""")
.packagePrefix("org.foo.one")
.module(
"intellij.org.foo",
PluginBuilder()
.applicationListeners(
"""<listener class="${MyUISettingsListener2::class.java.name}" topic="com.intellij.ide.ui.UISettingsListener"/>""")
.packagePrefix("org.foo")
.dependency(pluginTwoBuilder.id),
)
val pluginOneDisposable = loadPluginWithText(pluginDescriptor)
try {
val app = ApplicationManager.getApplication()
app.messageBus.syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(UISettings())
@@ -416,13 +440,13 @@ class DynamicPluginsTest {
}
@Test
fun testExtensionOnServiceDependency() {
fun extensionOnServiceDependency() {
val project = projectRule.project
StartupManagerImpl.addActivityEpListener(project)
val disposable = loadExtensionWithText("""
<postStartupActivity implementation="${MyStartupActivity::class.java.name}"/>
<projectService serviceImplementation="${MyProjectService::class.java.name}"/>
""".trimIndent(), DynamicPluginsTest::class.java.classLoader)
""", DynamicPluginsTest::class.java.classLoader)
try {
assertThat(project.service<MyProjectService>().executed).isTrue()
}
@@ -575,67 +599,6 @@ class DynamicPluginsTest {
}
}
@Test
fun loadNestedOptionalDependency() {
val barBuilder = PluginBuilder().randomId("bar")
val quuxBuilder = PluginBuilder().randomId("quux")
val quuxDependencyDescriptor = PluginBuilder().extensions(
"""<applicationService serviceImplementation="${MyPersistentComponent::class.java.name}"/>""")
val barDependencyDescriptor = PluginBuilder().depends(quuxBuilder.id, quuxDependencyDescriptor)
val mainDescriptor = PluginBuilder().depends(barBuilder.id, barDependencyDescriptor)
val barDisposable = loadPluginWithText(barBuilder)
try {
val quuxDisposable = loadPluginWithText(quuxBuilder)
try {
val mainDisposable = loadPluginWithText(mainDescriptor)
try {
assertThat(ApplicationManager.getApplication().getService(MyPersistentComponent::class.java)).isNotNull()
}
finally {
Disposer.dispose(mainDisposable)
assertThat(ApplicationManager.getApplication().getService(MyPersistentComponent::class.java)).isNull()
}
}
finally {
Disposer.dispose(quuxDisposable)
}
}
finally {
Disposer.dispose(barDisposable)
}
}
@Test
fun unloadNestedOptionalDependency() {
val barBuilder = PluginBuilder().randomId("bar")
val quuxBuilder = PluginBuilder().randomId("quux")
val quuxDependencyDescriptor = PluginBuilder()
.extensions("""<applicationService serviceImplementation="${MyPersistentComponent::class.java.name}"/>""")
.packagePrefix("org.foo.quux")
val barDependencyDescriptor = PluginBuilder().depends(quuxBuilder.id, quuxDependencyDescriptor)
.packagePrefix("org.foo.bar")
val mainDescriptor = PluginBuilder().depends(barBuilder.id, barDependencyDescriptor).packagePrefix("org.foo")
val barDisposable = loadPluginWithText(barBuilder)
try {
val quuxDisposable = loadPluginWithText(quuxBuilder)
val mainDisposable = loadPluginWithText(mainDescriptor)
try {
assertThat(ApplicationManager.getApplication().getService(MyPersistentComponent::class.java)).isNotNull()
Disposer.dispose(quuxDisposable)
assertThat(ApplicationManager.getApplication().getService(MyPersistentComponent::class.java)).isNull()
}
finally {
Disposer.dispose(mainDisposable)
}
}
finally {
Disposer.dispose(barDisposable)
}
}
private fun loadPluginWithOptionalDependency(pluginDescriptor: PluginBuilder,
optionalDependencyDescriptor: PluginBuilder,
dependsOn: PluginBuilder): Disposable {
@@ -669,6 +632,7 @@ private class MyPersistentComponent : PersistentStateComponent<MyPersistentState
}
}
@InternalIgnoreDependencyViolation
private class MyStartupActivity : StartupActivity.DumbAware {
override fun runActivity(project: Project) {
val service = project.getService(MyProjectService::class.java)
@@ -678,6 +642,7 @@ private class MyStartupActivity : StartupActivity.DumbAware {
}
}
@InternalIgnoreDependencyViolation
private class MyProjectService {
companion object {
val LOG = Logger.getInstance(MyProjectService::class.java)
@@ -725,7 +690,5 @@ private class MyRunnable : Runnable {
}
private class MyModuleConfigurationEditorProvider : ModuleConfigurationEditorProvider {
override fun createEditors(state: ModuleConfigurationState?): Array<ModuleConfigurationEditor> {
return arrayOf()
}
override fun createEditors(state: ModuleConfigurationState?): Array<ModuleConfigurationEditor> = arrayOf()
}

View File

@@ -1,10 +1,8 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:JvmName("DynamicPluginsTestUtil")
@file:Suppress("UsePropertyAccessSyntax")
package com.intellij.ide.plugins
import com.intellij.ide.plugins.DynamicPlugins.loadPlugin
import com.intellij.ide.plugins.DynamicPlugins.unloadPlugin
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.util.BuildNumber
@@ -60,21 +58,18 @@ internal fun loadPluginWithText(pluginBuilder: PluginBuilder, loader: ClassLoade
pluginBuilder.build(pluginDirectory)
val descriptor = loadDescriptorInTest(pluginDirectory)
assertThat(DynamicPlugins.checkCanUnloadWithoutRestart(descriptor)).isNull()
setPluginClassLoaderForMainAndSubPlugins(descriptor, loader)
try {
loadPlugin(descriptor)
DynamicPlugins.loadPlugin(pluginDescriptor = descriptor, classLoaderForTest = loader)
}
catch (e: Exception) {
unloadPlugin(descriptor)
DynamicPlugins.unloadPlugin(descriptor)
throw e
}
return Disposable {
val unloadDescriptor = loadDescriptorInTest(pluginDirectory)
val canBeUnloaded = DynamicPlugins.allowLoadUnloadWithoutRestart(unloadDescriptor)
unloadPlugin(descriptor)
assertThat(canBeUnloaded).isTrue()
val reason = DynamicPlugins.checkCanUnloadWithoutRestart(descriptor)
DynamicPlugins.unloadPlugin(descriptor)
assertThat(reason).isNull()
}
}

View File

@@ -1,8 +1,9 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.intellij.util.io.Compressor
import com.intellij.util.io.write
import org.intellij.lang.annotations.Language
import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.nio.file.Files
@@ -11,6 +12,39 @@ import java.util.concurrent.atomic.AtomicInteger
private val pluginIdCounter = AtomicInteger()
fun plugin(outDir: Path, @Language("XML") descriptor: String) {
val rawDescriptor = try {
readModuleDescriptorForTest(descriptor.toByteArray())
}
catch (e: Throwable) {
throw RuntimeException("Cannot parse:\n ${descriptor.trimIndent().prependIndent(" ")}", e)
}
outDir.resolve("${rawDescriptor.id!!}/META-INF/plugin.xml").write(descriptor.trimIndent())
}
fun module(outDir: Path, ownerId: String, moduleId: String, @Language("XML") descriptor: String) {
try {
readModuleDescriptorForTest(descriptor.toByteArray())
}
catch (e: Throwable) {
throw RuntimeException("Cannot parse:\n ${descriptor.trimIndent().prependIndent(" ")}", e)
}
outDir.resolve("$ownerId/$moduleId.xml").write(descriptor.trimIndent())
}
//class PluginV2Builder(val id: String, private var b: PluginBuilder) {
// var packagePrefix: String
// get() = throw IllegalStateException("set only")
// set(value) {
// b.packagePrefix(value)
// }
//
// @Language("XML")
// val extensionPoints = mutableListOf<@Language("XML") String>()
//
// val pluginDependencies
//}
class PluginBuilder {
private data class ExtensionBlock(val ns: String, val text: String)
private data class DependsTag(val pluginId: String, val configFile: String?)
@@ -31,7 +65,10 @@ class PluginBuilder {
private var untilBuild: String? = null
private var version: String? = null
private val subDescriptors = mutableMapOf<String, PluginBuilder>()
private val content = mutableListOf<PluginContentDescriptor.ModuleItem>()
private val dependencies = mutableListOf<ModuleDependenciesDescriptor.ModuleReference>()
private val subDescriptors = HashMap<String, PluginBuilder>()
init {
depends("com.intellij.modules.lang")
@@ -57,7 +94,7 @@ class PluginBuilder {
return this
}
fun packagePrefix(value: String): PluginBuilder {
fun packagePrefix(value: String?): PluginBuilder {
packagePrefix = value
return this
}
@@ -69,11 +106,26 @@ class PluginBuilder {
fun depends(pluginId: String, subDescriptor: PluginBuilder): PluginBuilder {
val fileName = "dep_${pluginIdCounter.incrementAndGet()}.xml"
subDescriptors[fileName] = subDescriptor
subDescriptors.put("META-INF/$fileName", subDescriptor)
depends(pluginId, fileName)
return this
}
fun module(moduleName: String, moduleDescriptor: PluginBuilder): PluginBuilder {
val fileName = "$moduleName.xml"
subDescriptors.put(fileName, moduleDescriptor)
content.add(PluginContentDescriptor.ModuleItem(name = moduleName, configFile = null))
// remove default dependency on lang
moduleDescriptor.noDepends()
return this
}
fun dependency(moduleName: String): PluginBuilder {
dependencies.add(ModuleDependenciesDescriptor.ModuleReference(moduleName))
return this
}
fun noDepends(): PluginBuilder {
dependsTags.clear()
return this
@@ -104,7 +156,7 @@ class PluginBuilder {
return this
}
fun extensionPoints(text: String): PluginBuilder {
fun extensionPoints(@Language("XML") text: String): PluginBuilder {
extensionPoints = text
return this
}
@@ -148,14 +200,22 @@ class PluginBuilder {
extensionPoints?.let { append("<extensionPoints>$it</extensionPoints>") }
applicationListeners?.let { append("<applicationListeners>$it</applicationListeners>") }
actions?.let { append("<actions>$it</actions>") }
if (content.isNotEmpty()) {
append("\n<content>\n ")
content.joinTo(this, separator = "\n ") { """<module name="${it.name}" />""" }
append("\n</content>")
}
if (dependencies.isNotEmpty()) {
append("\n<dependencies>\n ")
dependencies.joinTo(this, separator = "\n ") { """<module name="${it.name}" />""" }
append("\n</dependencies>")
}
append("</idea-plugin>")
}
}
fun buildToAutoGeneratedSubDir(parentDir: Path): PluginBuilder {
return build(parentDir.resolve(id))
}
fun build(path: Path): PluginBuilder {
path.resolve("META-INF/plugin.xml").write(text())
writeSubDescriptors(path)
@@ -164,7 +224,7 @@ class PluginBuilder {
fun writeSubDescriptors(path: Path) {
for ((fileName, subDescriptor) in subDescriptors) {
path.resolve("META-INF/$fileName").write(subDescriptor.text(requireId = false))
path.resolve(fileName).write(subDescriptor.text(requireId = false))
subDescriptor.writeSubDescriptors(path)
}
}

View File

@@ -29,6 +29,7 @@ import java.nio.file.Path
import java.nio.file.Paths
import java.text.SimpleDateFormat
import java.util.*
import java.util.function.Function
import java.util.function.Supplier
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -115,9 +116,9 @@ class PluginDescriptorTest {
fun testProductionPlugins() {
IoTestUtil.assumeMacOS()
assumeNotUnderTeamcity()
val descriptors = loadAndInitDescriptors(Paths.get("/Applications/Idea.app/Contents/plugins"), PluginManagerCore.getBuildNumber()).sortedPlugins
val descriptors = loadAndInitDescriptors(Paths.get("/Applications/Idea.app/Contents/plugins"), PluginManagerCore.getBuildNumber()).pluginSet.allPlugins
assertThat(descriptors).isNotEmpty()
assertThat(descriptors.find { it!!.pluginId.idString == "com.intellij.java" }).isNotNull
assertThat(descriptors.find { it.pluginId.idString == "com.intellij.java" }).isNotNull
}
@Test
@@ -140,7 +141,7 @@ class PluginDescriptorTest {
IoTestUtil.assumeMacOS()
assumeNotUnderTeamcity()
val descriptors = loadAndInitDescriptors(Paths.get("/Volumes/data/plugins"), PluginManagerCore.getBuildNumber()).sortedPlugins
val descriptors = loadAndInitDescriptors(Paths.get("/Volumes/data/plugins"), PluginManagerCore.getBuildNumber()).pluginSet.allPlugins
assertThat(descriptors).isNotEmpty()
}
@@ -195,15 +196,15 @@ class PluginDescriptorTest {
<version>2.0</version>
</idea-plugin>""")
val result = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber())
val plugins = result.sortedEnabledPlugins
val pluginSet = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber()).pluginSet
val plugins = pluginSet.loadedPlugins
assertThat(plugins).hasSize(1)
val foo = plugins[0]
assertThat(foo.version).isEqualTo("2.0")
assertThat(foo.pluginId.idString).isEqualTo("foo")
assertThat(result.idMap).containsOnlyKeys(foo.pluginId)
assertThat(result.idMap.get(foo.pluginId)).isSameAs(foo)
assertThat(pluginSet.allPlugins.toList()).map(Function { it.id }).containsOnly(foo.pluginId)
assertThat(pluginSet.findEnabledPlugin(foo.pluginId)).isSameAs(foo)
}
@Suppress("PluginXmlValidity")
@@ -284,16 +285,16 @@ class PluginDescriptorTest {
<idea-version since-build="2.0" until-build="4.*"/>
</idea-plugin>""")
val result = loadAndInitDescriptors(pluginDir, BuildNumber.fromString("3.12")!!)
val pluginSet = loadAndInitDescriptors(pluginDir, BuildNumber.fromString("3.12")!!).pluginSet
val plugins = result.sortedEnabledPlugins
val plugins = pluginSet.loadedPlugins
assertThat(plugins).hasSize(1)
val foo = plugins[0]
assertThat(foo.version).isEqualTo("2.0")
assertThat(foo.pluginId.idString).isEqualTo("foo")
assertThat(result.idMap).containsOnlyKeys(foo.pluginId)
assertThat(result.idMap[foo.pluginId]).isSameAs(foo)
assertThat(pluginSet.allPlugins.toList()).map(Function { it.id }).containsOnly(foo.pluginId)
assertThat(pluginSet.findEnabledPlugin(foo.pluginId)).isSameAs(foo)
}
@Suppress("PluginXmlValidity")
@@ -303,15 +304,15 @@ class PluginDescriptorTest {
PluginBuilder().noDepends().id("foo").version("1.0").build(pluginDir.resolve("foo_1-0"))
PluginBuilder().noDepends().id("foo").version("1.0").build(pluginDir.resolve("foo_another"))
val result = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber())
val plugins = result.sortedEnabledPlugins
val pluginSet = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber()).pluginSet
val plugins = pluginSet.loadedPlugins
assertThat(plugins).hasSize(1)
val foo = plugins[0]
assertThat(foo.version).isEqualTo("1.0")
assertThat(foo.pluginId.idString).isEqualTo("foo")
assertThat(result.idMap).containsOnlyKeys(foo.pluginId)
assertThat(result.idMap.get(foo.pluginId)).isSameAs(foo)
assertThat(pluginSet.allPlugins.toList()).map(Function { it.id }).containsOnly(foo.pluginId)
assertThat(pluginSet.findEnabledPlugin(foo.pluginId)).isSameAs(foo)
}
@Suppress("PluginXmlValidity")
@@ -353,7 +354,7 @@ class PluginDescriptorTest {
}
private fun checkClassLoader(pluginDir: Path) {
val list = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber()).sortedEnabledPlugins
val list = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber()).pluginSet.loadedPlugins
assertThat(list).hasSize(2)
val bar = list[0]
@@ -453,8 +454,8 @@ class PluginDescriptorTest {
PluginBuilder().noDepends().id("foo").depends("bar").build(pluginDir.resolve("foo"))
PluginBuilder().noDepends().id("bar").build(pluginDir.resolve("bar"))
val result = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber(), setOf(PluginId.getId("bar")))
assertThat(result.sortedEnabledPlugins).isEmpty()
val pluginSet = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber(), setOf(PluginId.getId("bar"))).pluginSet
assertThat(pluginSet.loadedPlugins).isEmpty()
}
@Test
@@ -477,8 +478,8 @@ class PluginDescriptorTest {
.id("com.intellij.gradle")
.build(pluginDir.resolve("intellij.gradle"))
val result = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber(), setOf(PluginId.getId("com.intellij.gradle")))
assertThat(result.sortedEnabledPlugins).isEmpty()
val result = loadAndInitDescriptors(pluginDir, PluginManagerCore.getBuildNumber(), setOf(PluginId.getId("com.intellij.gradle"))).pluginSet
assertThat(result.loadedPlugins).isEmpty()
}
}

View File

@@ -195,7 +195,7 @@ public class PluginManagerTest {
PluginManagerCore.getAndClearPluginLoadingErrors();
PluginManagerState loadPluginResult = loadAndInitializeDescriptors(testDataName + ".xml", isBundled);
StringBuilder text = new StringBuilder();
for (IdeaPluginDescriptorImpl descriptor : loadPluginResult.sortedPlugins) {
for (IdeaPluginDescriptorImpl descriptor : loadPluginResult.pluginSet.loadedPlugins) {
text.append(descriptor.isEnabled() ? "+ " : " ").append(descriptor.getPluginId().getIdString()).append('\n');
}
text.append("\n\n");
@@ -270,6 +270,15 @@ public class PluginManagerTest {
}
throw new AssertionError("Unexpected: " + relativePath);
}
@NotNull
@Override
public RawPluginDescriptor resolveModuleFile(@NotNull ReadModuleContext readContext,
@NotNull DataLoader dataLoader,
@NotNull String path,
@Nullable RawPluginDescriptor readInto) {
return resolvePath(readContext, dataLoader, path, readInto);
}
};
for (XmlElement element : root.children) {

View File

@@ -1,517 +1,35 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.fasterxml.jackson.core.JsonFactory
import com.fasterxml.jackson.core.JsonGenerator
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.util.JDOMUtil
import com.intellij.project.IntelliJProjectConfiguration
import com.intellij.util.io.jackson.array
import com.intellij.util.io.jackson.obj
import com.intellij.util.throwIfNotEmpty
import org.jdom.Element
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModule
import com.intellij.util.getErrorsAsString
import org.jetbrains.jps.util.JpsPathUtil
import org.junit.Assert
import org.junit.Test
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
private data class ModuleInfo(val descriptorFile: Path, val isPlugin: Boolean) {
val dependencies = mutableListOf<Reference>()
val content = mutableListOf<Reference>()
}
private data class Reference(val moduleName: String, val packageName: String)
private val homePath by lazy { Path.of(PathManager.getHomePath()) }
private data class PluginTask(val pluginId: String,
val moduleName: String,
val descriptor: Element,
val pluginXml: Path) {
val moduleReferences = mutableListOf<Reference>()
}
private class ModuleDescriptorPath {
var pluginDescriptorFile: Path? = null
var moduleDescriptorFile: Path? = null
var pluginDescriptor: Element? = null
var moduleDescriptor: Element? = null
}
class PluginModelTest {
@Test
fun check() {
val modules = IntelliJProjectConfiguration.loadIntelliJProject(homePath).modules
val checker = PluginModelChecker(modules)
val modules = IntelliJProjectConfiguration.loadIntelliJProject(PluginModelValidator.homePath).modules.map { module ->
object : PluginModelValidator.Module {
override val name: String
get() = module.name
// we cannot check everything in one pass - before checking we have to compute map of plugin to plugin modules
// to be able to correctly check `<depends>Docker</depends>` that in a new plugin model specified as
// <module name="intellij.clouds.docker.compose" package="com.intellij.docker.composeFile"/>
val moduleNameToFileInfo = computeModuleSet(modules, checker)
l@ for ((moduleName, moduleMetaInfo) in moduleNameToFileInfo) {
val descriptor = moduleMetaInfo.pluginDescriptor ?: continue
val pluginXml = moduleMetaInfo.pluginDescriptorFile ?: continue
val id = descriptor.getChild("id")?.text ?: descriptor.getChild("name")?.text
if (id == null) {
checker.errors.add(PluginValidationError("Plugin id is not specified in ${homePath.relativize(pluginXml)}"))
continue
}
val task = PluginTask(pluginId = id, moduleName = moduleName, descriptor = descriptor, pluginXml = pluginXml)
checker.pluginIdToModules.put(id, task)
val content = descriptor.getChild("content")
if (content != null) {
for (item in content.getChildren("module")) {
// ignore missed attributes - will be validated on second pass
val packageName = item.getAttributeValue("package") ?: continue
task.moduleReferences.add(Reference(moduleName = item.getAttributeValue("name") ?: continue,
packageName = packageName))
val old = checker.packageToPlugin.put(packageName, task)
if (old != null) {
checker.errors.add(PluginValidationError("Duplicated package $packageName (old=$old, new=$task)"))
continue@l
}
override fun getSourceRoots(): List<Path> {
return module.sourceRoots.asSequence()
.filter { !it.rootType.isForTests }
.map { Path.of(JpsPathUtil.urlToPath(it.url)) }
.toList()
}
}
}
for (task in checker.pluginIdToModules.values) {
checkModule(task, checker)
val validator = PluginModelValidator()
val errors = validator.validate(modules)
if (!errors.isEmpty()) {
System.err.println(getErrorsAsString(errors, includeStackTrace = false))
Assert.fail()
}
throwIfNotEmpty(checker.errors)
printGraph(checker)
validator.printGraph()
}
private fun checkModule(info: PluginTask, checker: PluginModelChecker) {
val descriptor = info.descriptor
val dependencies = descriptor.getChild("dependencies")
if (dependencies != null) {
val pluginInfo = checker.graph.computeIfAbsent(info.moduleName) { ModuleInfo(info.pluginXml, isPlugin = true) }
try {
checker.checkDependencies(dependencies, descriptor, info.pluginXml, pluginInfo)
}
catch (e: PluginValidationError) {
checker.errors.add(e)
}
}
val content = descriptor.getChild("content")
if (content != null) {
val pluginInfo = checker.graph.computeIfAbsent(info.moduleName) { ModuleInfo(info.pluginXml, isPlugin = true) }
try {
checker.checkContent(content, descriptor, info.pluginXml, pluginInfo)
}
catch (e: PluginValidationError) {
checker.errors.add(e)
}
}
val aPackage = descriptor.getAttributeValue("package")
if (aPackage == null && checker.graph.containsKey(info.moduleName) /* something was added */) {
// some plugins cannot be yet fully migrated
System.err.println("Package is not specified for ${info.moduleName} " +
"(pluginId=${info.pluginId}, descriptor=${homePath.relativize(info.pluginXml)})")
}
}
}
private fun computeModuleSet(modules: List<JpsModule>, checker: PluginModelChecker): LinkedHashMap<String, ModuleDescriptorPath> {
val moduleNameToFileInfo = LinkedHashMap<String, ModuleDescriptorPath>()
for (module in modules) {
// platform/cwm-plugin/resources/META-INF/plugin.xml doesn't have id - ignore for now
if (module.name.startsWith("fleet.") ||
module.name == "fleet" ||
module.name == "intellij.idea.ultimate.resources" ||
module.name == "intellij.lightEdit" ||
module.name == "intellij.webstorm" ||
module.name == "intellij.cwm.plugin") {
continue
}
for (sourceRoot in module.sourceRoots) {
if (sourceRoot.rootType.isForTests) {
continue
}
val root = Path.of(JpsPathUtil.urlToPath(sourceRoot.url))
val metaInf = root.resolve("META-INF")
val pluginDescriptorFile = metaInf.resolve("plugin.xml")
val pluginDescriptor = try {
JDOMUtil.load(pluginDescriptorFile)
}
catch (ignore: NoSuchFileException) {
null
}
val moduleDescriptorFile = root.resolve("${module.name}.xml")
val moduleDescriptor = try {
JDOMUtil.load(moduleDescriptorFile)
}
catch (ignore: NoSuchFileException) {
null
}
if (Files.exists(metaInf.resolve("${module.name}.xml"))) {
checker.errors.add(PluginValidationError("Module descriptor must be in the root of module root", mapOf(
"module" to module.name,
"moduleDescriptor" to metaInf.resolve("${module.name}.xml"),
)))
continue
}
if (pluginDescriptor == null && moduleDescriptor == null) {
continue
}
val item = moduleNameToFileInfo.computeIfAbsent(module.name) { ModuleDescriptorPath() }
if (item.pluginDescriptorFile != null && pluginDescriptor != null) {
checker.errors.add(PluginValidationError("Duplicated plugin.xml", mapOf(
"module" to module.name,
"firstPluginDescriptor" to item.pluginDescriptorFile,
"secondPluginDescriptor" to pluginDescriptorFile,
)))
continue
}
if (item.pluginDescriptorFile != null && moduleDescriptor != null) {
checker.errors.add(PluginValidationError("Module cannot have both plugin.xml and module descriptor", mapOf(
"module" to module.name,
"pluginDescriptor" to item.pluginDescriptorFile,
"moduleDescriptor" to moduleDescriptorFile,
)))
continue
}
if (item.moduleDescriptorFile != null && pluginDescriptor != null) {
checker.errors.add(PluginValidationError("Module cannot have both plugin.xml and module descriptor", mapOf(
"module" to module.name,
"pluginDescriptor" to pluginDescriptorFile,
"moduleDescriptor" to item.moduleDescriptorFile,
)))
continue
}
if (pluginDescriptor == null) {
item.moduleDescriptorFile = moduleDescriptorFile
item.moduleDescriptor = moduleDescriptor
}
else {
item.pluginDescriptorFile = pluginDescriptorFile
item.pluginDescriptor = pluginDescriptor
}
}
}
return moduleNameToFileInfo
}
private fun printGraph(checker: PluginModelChecker) {
val stringWriter = StringWriter()
val writer = JsonFactory().createGenerator(stringWriter)
writer.useDefaultPrettyPrinter()
val graph = checker.graph
writer.use {
writer.obj {
val names = graph.keys.toTypedArray()
names.sort()
for (name in names) {
writer.obj(name) {
val item = graph.get(name)!!
writer.writeStringField("descriptor", homePath.relativize(item.descriptorFile).toString())
writeEntries(item.dependencies, "dependencies", writer)
writeEntries(item.content, "content", writer)
}
}
}
}
println(stringWriter.buffer)
Files.writeString(Path.of("/tmp/plugin-report.json"), stringWriter.buffer)
}
private fun writeEntries(items: List<Reference>, jsonName: String, writer: JsonGenerator) {
if (items.isNotEmpty()) {
writer.array(jsonName) {
for (entry in items) {
writer.obj {
writer.writeStringField("module", entry.moduleName)
writer.writeStringField("modulePackage", entry.packageName)
}
}
}
}
}
private class PluginModelChecker(modules: List<JpsModule>) {
val pluginIdToModules = LinkedHashMap<String, PluginTask>()
val packageToPlugin = HashMap<String, PluginTask>()
val graph = HashMap<String, ModuleInfo>()
val errors = mutableListOf<Throwable>()
private val nameToModule = modules.associateBy { it.name }
fun checkDependencies(element: Element, referencingDescriptor: Element, referencingDescriptorFile: Path, pluginInfo: ModuleInfo) {
for (child in element.children) {
if (child.name != "module") {
if (child.name == "plugin") {
if (pluginInfo.isPlugin) {
throw PluginValidationError("Plugin cannot depend on plugin: ${JDOMUtil.write(child)}",
mapOf(
"entry" to child,
"descriptorFile" to referencingDescriptorFile,
))
}
// todo check that the referenced plugin exists
continue
}
if (pluginInfo.isPlugin) {
throw PluginValidationError("Unsupported dependency type: ${child.name}",
mapOf(
"entry" to child,
"descriptorFile" to referencingDescriptorFile,
))
}
}
val moduleName = child.getAttributeValue("name") ?: throw PluginValidationError("Module name is not specified")
val module = nameToModule.get(moduleName) ?: throw PluginValidationError("Cannot find module $moduleName")
if (moduleName == "intellij.platform.commercial.verifier") {
continue
}
val (descriptor, referencedDescriptorFile) = loadFileInModule(module,
referencingDescriptorFile = referencingDescriptorFile,
child.getAttributeValue("package")!!)
val aPackage = checkPackage(descriptor, referencedDescriptorFile, child)
pluginInfo.dependencies.add(Reference(moduleName, aPackage))
// check that also specified using depends tag
var pluginDependency: String? = null
for (dependsElement in referencingDescriptor.getChildren("depends")) {
if (dependsElement.getAttributeValue("config-file") == "${module.name}.xml") {
pluginDependency = dependsElement.text
break
}
}
if (pluginDependency == null) {
//<dependencies>
// <!-- <depends>Docker</depends> -->
// <module name="intellij.clouds.docker.compose" package="com.intellij.docker.composeFile"/>
//</dependencies>
val task = packageToPlugin.get(aPackage)
if (task != null) {
for (dependsElement in referencingDescriptor.getChildren("depends")) {
if (dependsElement.text == task.pluginId) {
pluginDependency = task.pluginId
break
}
}
}
}
if (pluginDependency == null) {
throw PluginValidationError("Module, that used as dependency, must be also specified in `depends` (during transition period)." +
"\nPlease check that you use correct naming (not arbitrary file name, but exactly as module name plus `.xml` extension).",
mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingDescriptorFile,
"referencedDescriptorFile" to referencedDescriptorFile
))
}
var isPluginDependencySpecified = false
for (entryElement in descriptor.getChild("dependencies")?.getChildren("plugin") ?: emptyList()) {
if (entryElement.getAttributeValue("id") == pluginDependency) {
isPluginDependencySpecified = true
break
}
}
if (!isPluginDependencySpecified) {
throw PluginValidationError("Module, that used as dependency, must specify dependency on some plugin (`dependencies.plugin`)" +
"\n\nProposed fix (add to ${homePath.relativize(referencedDescriptorFile)}):\n\n" + """
<dependencies>
<plugin id="$pluginDependency"/>
</dependencies>
""".trimIndent() + "\n\n", mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingDescriptorFile,
"referencedDescriptorFile" to referencedDescriptorFile
))
}
descriptor.getChild("dependencies")?.let {
try {
checkDependencies(it, descriptor, referencedDescriptorFile,
graph.computeIfAbsent(moduleName) { ModuleInfo(referencedDescriptorFile, isPlugin = false) })
}
catch (e: PluginValidationError) {
errors.add(e)
}
}
}
}
// for plugin two variants:
// 1) depends + dependency on plugin in a referenced descriptor = optional descriptor. In old format: depends tag
// 2) no depends + no dependency on plugin in a referenced descriptor = directly injected into plugin (separate classloader is not created
// during transition period). In old format: xi:include (e.g. <xi:include href="dockerfile-language.xml"/>.
fun checkContent(content: Element,
referencingDescriptor: Element,
referencingDescriptorFile: Path,
pluginInfo: ModuleInfo) {
for (child in content.children) {
if (child.name != "module") {
throw PluginValidationError("Unexpected element: ${JDOMUtil.write(child)}")
}
val moduleName = child.getAttributeValue("name") ?: throw PluginValidationError("Module name is not specified")
val module = nameToModule.get(moduleName) ?: throw PluginValidationError("Cannot find module $moduleName")
if (moduleName == "intellij.platform.commercial.verifier") {
errors.add(PluginValidationError("intellij.platform.commercial.verifier is not supposed to be used as content of plugin",
mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingDescriptorFile,
)))
return
}
val (descriptor, referencedDescriptorFile) = loadFileInModule(module,
referencingDescriptorFile,
child.getAttributeValue("package")!!)
val aPackage = checkPackage(descriptor, referencedDescriptorFile, child)
pluginInfo.content.add(Reference(moduleName, aPackage))
// check that also specified using depends tag
var oldPluginDependencyId: String? = null
for (dependsElement in referencingDescriptor.getChildren("depends")) {
if (dependsElement.getAttributeValue("config-file") == "${module.name}.xml") {
oldPluginDependencyId = dependsElement.text
break
}
}
var isPluginDependencyDeclared = false
for (entryElement in descriptor.getChild("dependencies")?.getChildren("plugin") ?: emptyList()) {
if (entryElement.getAttributeValue("id") == oldPluginDependencyId) {
isPluginDependencyDeclared = true
break
}
}
// if dependency specified in a new format in the referenced descriptor, then must be also specified using old depends tag
if (oldPluginDependencyId == null && isPluginDependencyDeclared) {
throw PluginValidationError("Module, that used as plugin content and depends on a plugin, must be also specified in `depends` (during transition period)." +
"\nPlease check that you use correct naming (not arbitrary file name, but exactly as module name plus `.xml` extension).",
mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingDescriptorFile,
"referencedDescriptorFile" to referencedDescriptorFile
))
}
// if there is old depends tag, then dependency in a new format in the referenced descriptor must be also declared
if (oldPluginDependencyId != null && !isPluginDependencyDeclared) {
throw PluginValidationError("Module, that used as plugin content and depends on a plugin, must specify dependency on the plugin (`dependencies.plugin`)" +
"\n\nProposed fix (add to ${homePath.relativize(referencedDescriptorFile)}):\n\n" + """
<dependencies>
<plugin id="$oldPluginDependencyId"/>
</dependencies>
""".trimIndent() + "\n\n", mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingDescriptorFile,
"referencedDescriptorFile" to referencedDescriptorFile
))
}
descriptor.getChild("content")?.let {
try {
checkContent(it, descriptor, referencedDescriptorFile,
graph.computeIfAbsent(moduleName) { ModuleInfo(referencedDescriptorFile, isPlugin = false) })
}
catch (e: PluginValidationError) {
errors.add(e)
}
}
}
}
}
private fun checkPackage(descriptor: Element, descriptorFile: Path, child: Element): String {
val aPackage = descriptor.getAttributeValue("package")
?: throw PluginValidationError("package attribute is not specified", mapOf("descriptorFile" to descriptorFile))
if (aPackage != child.getAttributeValue("package")) {
throw PluginValidationError("package doesn't match", mapOf(
"entry" to child,
"referencedDescriptorFile" to descriptorFile,
"packageInDescriptor" to aPackage
))
}
return aPackage
}
private class PluginValidationError(message: String) : RuntimeException(message) {
constructor(message: String, params: Map<String, Any?>) : this(message + " (\n ${params.entries.joinToString(separator = ",\n ") { "${it.key}=${paramValueToString(it.value)}" }}\n)")
constructor(message: String, params: Map<String, Any?>, fix: String) : this(
message +
" (\n ${params.entries.joinToString(separator = ",\n ") { "${it.key}=${paramValueToString(it.value)}" }}\n)" +
"\n\nProposed fix:\n\n" + fix.trimIndent() + "\n\n"
)
}
private fun paramValueToString(value: Any?): String {
return when (value) {
// reformat according to IJ XMl code style
is Element -> JDOMUtil.write(value).replace("\" />", "\"/>")
is Path -> homePath.relativize(value).toString()
else -> value.toString()
}
}
private fun loadFileInModule(module: JpsModule,
referencingDescriptorFile: Path,
aPackage: String): Pair<Element, Path> {
val fileName = "${module.name}.xml"
val roots = module.getSourceRoots(JavaResourceRootType.RESOURCE) + module.getSourceRoots(JavaSourceRootType.SOURCE)
for (sourceRoot in roots) {
try {
val file = Path.of(JpsPathUtil.urlToPath(sourceRoot.url), fileName)
return Pair(JDOMUtil.load(file), file)
}
catch (ignore: NoSuchFileException) {
}
}
throw PluginValidationError("Module ${module.name} doesn't have descriptor file",
mapOf(
"expectedFile" to fileName,
"referencingDescriptorFile" to referencingDescriptorFile,
),
"""
Create file $fileName in ${homePath.relativize(Path.of(JpsPathUtil.urlToPath(roots.first().url)))}
with content:
<idea-plugin package="$aPackage">
</idea-plugin>
""")
}
}

View File

@@ -0,0 +1,545 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.fasterxml.jackson.core.JsonFactory
import com.fasterxml.jackson.core.JsonGenerator
import com.intellij.ide.plugins.PluginModelValidator.Companion.homePath
import com.intellij.openapi.application.PathManager
import com.intellij.util.XmlElement
import com.intellij.util.io.jackson.array
import com.intellij.util.io.jackson.obj
import com.intellij.util.readXmlAsModel
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
internal class PluginModelValidator {
internal interface Module {
val name: String
fun getSourceRoots(): List<Path>
}
companion object {
internal val homePath by lazy { Path.of(PathManager.getHomePath()) }
}
private val pluginIdToInfo = LinkedHashMap<String, ModuleInfo>()
private val errors = mutableListOf<Throwable>()
fun getErrors(): List<Throwable> = java.util.List.copyOf(errors)
fun validate(sourceModules: List<Module>): List<Throwable> {
// 1. collect plugin and module file info set
val sourceModuleNameToFileInfo = computeModuleSet(sourceModules, errors)
val moduleNameToInfo = HashMap<String, ModuleInfo>()
// 2. process plugins - process content to collect modules
for ((sourceModuleName, moduleMetaInfo) in sourceModuleNameToFileInfo) {
// interested only in plugins
val descriptor = moduleMetaInfo.pluginDescriptor ?: continue
val descriptorFile = moduleMetaInfo.pluginDescriptorFile ?: continue
val id = descriptor.getChild("id")?.content ?: descriptor.getChild("name")?.content
if (id == null) {
errors.add(PluginValidationError("Plugin id is not specified (descriptorFile=${pathToShortString(descriptorFile)})"))
continue
}
val moduleInfo = ModuleInfo(pluginId = id,
name = null,
sourceModuleName = sourceModuleName,
descriptorFile = descriptorFile,
packageName = descriptor.getAttributeValue("package"),
descriptor = descriptor)
val prev = pluginIdToInfo.put(id, moduleInfo)
if (prev != null) {
throw PluginValidationError("Duplicated plugin id: $id (prev=$prev, current=$moduleInfo)")
}
descriptor.getChild("content")?.let { contentElement ->
checkContent(content = contentElement,
referencingModuleInfo = moduleInfo,
sourceModuleNameToFileInfo = sourceModuleNameToFileInfo,
moduleNameToInfo = moduleNameToInfo)
}
}
// 3. check dependencies - we are aware about all modules now
for (moduleInfo in pluginIdToInfo.values) {
val descriptor = moduleInfo.descriptor
descriptor.getChild("dependencies")?.let { dependencies ->
checkDependencies(dependencies, moduleInfo, moduleNameToInfo, sourceModuleNameToFileInfo)
}
// in the end after processing content and dependencies
if (moduleInfo.packageName == null && hasContentOrDependenciesInV2Format(descriptor)) {
// some plugins cannot be yet fully migrated
System.err.println("Plugin ${moduleInfo.pluginId} is not fully migrated: package is not specified" +
" (pluginId=${moduleInfo.pluginId}, descriptor=${pathToShortString(moduleInfo.descriptorFile)})")
}
for (contentModuleInfo in moduleInfo.content) {
contentModuleInfo.descriptor.getChild("dependencies")?.let { dependencies ->
checkDependencies(dependencies, contentModuleInfo, moduleNameToInfo, sourceModuleNameToFileInfo)
}
}
}
return getErrors()
}
fun graphAsString(): CharSequence {
val stringWriter = StringWriter()
val writer = JsonFactory().createGenerator(stringWriter)
writer.useDefaultPrettyPrinter()
writer.use {
writer.obj {
val entries = pluginIdToInfo.entries.toMutableList()
entries.sortBy { it.value.sourceModuleName }
for (entry in entries) {
val item = entry.value
if (item.packageName == null && !hasContentOrDependenciesInV2Format(item.descriptor)) {
continue
}
writer.writeFieldName(item.sourceModuleName)
writeModuleInfo(writer, item)
}
}
}
return stringWriter.buffer
}
fun printGraph() {
val graphAsString = graphAsString()
println(graphAsString)
System.getProperty("plugin.graph.out")?.let {
val outFile = Path.of(it)
Files.writeString(outFile, "@startjson\n$graphAsString\n@endjson")
}
}
private fun checkDependencies(element: XmlElement,
referencingModuleInfo: ModuleInfo,
moduleNameToInfo: Map<String, ModuleInfo>,
sourceModuleNameToFileInfo: Map<String, ModuleDescriptorFileInfo>) {
if (referencingModuleInfo.packageName == null) {
errors.add(PluginValidationError(
"`dependencies` must be specified only for plugin in a new format: package prefix is not specified",
mapOf(
"referencingDescriptorFile" to referencingModuleInfo.descriptorFile,
)))
}
for (child in element.children) {
fun getErrorInfo(): Map<String, Any?> {
return mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingModuleInfo.descriptorFile,
)
}
if (child.name != "module") {
if (child.name == "plugin") {
// todo check that the referenced plugin exists
val id = child.getAttributeValue("id")
if (id == null) {
errors.add(PluginValidationError("Id is not specified for dependency on plugin", getErrorInfo()))
continue
}
val dependency = pluginIdToInfo.get(id)
if (!id.startsWith("com.intellij.modules.") && dependency == null) {
errors.add(PluginValidationError("Plugin not found: $id", getErrorInfo()))
continue
}
val ref = Reference(id, isPlugin = true)
assert(!referencingModuleInfo.dependencies.contains(ref))
referencingModuleInfo.dependencies.add(ref)
continue
}
if (referencingModuleInfo.isPlugin) {
errors.add(PluginValidationError("Unsupported dependency type: ${child.name}", getErrorInfo()))
continue
}
}
val moduleName = child.getAttributeValue("name")
if (moduleName == null) {
errors.add(PluginValidationError("Module name is not specified", getErrorInfo()))
continue
}
if (moduleName == "intellij.platform.commercial.verifier") {
continue
}
if (child.attributes.size > 1) {
errors.add(PluginValidationError("Unknown attributes: ${child.attributes.entries.filter { it.key != "name" }}", getErrorInfo()))
continue
}
val moduleInfo = moduleNameToInfo.get(moduleName)
if (moduleInfo == null) {
val moduleDescriptorFileInfo = sourceModuleNameToFileInfo.get(moduleName)
if (moduleDescriptorFileInfo != null) {
if (moduleDescriptorFileInfo.pluginDescriptor != null) {
errors.add(PluginValidationError(
message = "Dependency on plugin must be specified using `plugin` and not `module`",
params = getErrorInfo(),
fix = """
Change dependency element to:
<plugin id="${moduleDescriptorFileInfo.pluginDescriptor!!.getChild("id")?.content}"/>
"""
))
continue
}
}
errors.add(PluginValidationError("Module not found: $moduleName", getErrorInfo()))
continue
}
referencingModuleInfo.dependencies.add(Reference(moduleName, isPlugin = false))
for (dependsElement in referencingModuleInfo.descriptor.children) {
if (dependsElement.name != "depends") {
continue
}
if (dependsElement.getAttributeValue("config-file")?.removePrefix("/META-INF/") == moduleInfo.descriptorFile.fileName.toString()) {
errors.add(PluginValidationError("Module, that used as dependency, must be not specified in `depends`", getErrorInfo()))
break
}
}
}
}
// for plugin two variants:
// 1) depends + dependency on plugin in a referenced descriptor = optional descriptor. In old format: depends tag
// 2) no depends + no dependency on plugin in a referenced descriptor = directly injected into plugin (separate classloader is not created
// during transition period). In old format: xi:include (e.g. <xi:include href="dockerfile-language.xml"/>.
private fun checkContent(content: XmlElement,
referencingModuleInfo: ModuleInfo,
sourceModuleNameToFileInfo: Map<String, ModuleDescriptorFileInfo>,
moduleNameToInfo: MutableMap<String, ModuleInfo>) {
for (child in content.children) {
fun getErrorInfo(): Map<String, Any> {
return mapOf(
"entry" to child,
"referencingDescriptorFile" to referencingModuleInfo.descriptorFile,
)
}
if (child.name != "module") {
errors.add(PluginValidationError("Unexpected element: $child", getErrorInfo()))
continue
}
val moduleName = child.getAttributeValue("name")
if (moduleName == null) {
errors.add(PluginValidationError("Module name is not specified", getErrorInfo()))
continue
}
if (child.attributes.size > 1) {
errors.add(PluginValidationError("Unknown attributes: ${child.attributes.entries.filter { it.key != "name" }}", getErrorInfo()))
continue
}
if (moduleName == "intellij.platform.commercial.verifier") {
errors.add(PluginValidationError("intellij.platform.commercial.verifier is not supposed to be used as content of plugin",
getErrorInfo()))
continue
}
// ignore null - getModule reports error
val moduleDescriptorFileInfo = getModuleDescriptorFileInfo(moduleName, referencingModuleInfo, sourceModuleNameToFileInfo) ?: continue
val moduleDescriptor = moduleDescriptorFileInfo.moduleDescriptor!!
val aPackage = moduleDescriptor.getAttributeValue("package")
if (aPackage == null) {
errors.add(PluginValidationError("Module package is not specified", mapOf(
"descriptorFile" to moduleDescriptorFileInfo.moduleDescriptorFile!!,
)))
continue
}
val moduleInfo = ModuleInfo(pluginId = null,
name = moduleName,
sourceModuleName = moduleDescriptorFileInfo.sourceModule.name,
descriptorFile = moduleDescriptorFileInfo.moduleDescriptorFile!!,
packageName = aPackage,
descriptor = moduleDescriptor)
moduleNameToInfo.put(moduleName, moduleInfo)
referencingModuleInfo.content.add(moduleInfo)
// check that not specified using depends tag
for (dependsElement in referencingModuleInfo.descriptor.children) {
if (dependsElement.name != "depends") {
continue
}
if (dependsElement.getAttributeValue("config-file")?.removePrefix("/META-INF/") == moduleInfo.descriptorFile.fileName.toString()) {
errors.add(PluginValidationError(
"Module must be not specified in `depends`.",
getErrorInfo() + mapOf(
"referencedDescriptorFile" to moduleInfo.descriptorFile
)))
continue
}
}
moduleDescriptor.getChild("content")?.let {
errors.add(PluginValidationError("Module cannot define content", getErrorInfo() + mapOf(
"referencedDescriptorFile" to moduleInfo.descriptorFile
)))
}
}
}
private fun getModuleDescriptorFileInfo(moduleName: String,
referencingModuleInfo: ModuleInfo,
sourceModuleNameToFileInfo: Map<String, ModuleDescriptorFileInfo>): ModuleDescriptorFileInfo? {
var module = sourceModuleNameToFileInfo.get(moduleName)
if (module != null) {
return module
}
fun getErrorInfo(): Map<String, Path> {
return mapOf(
"referencingDescriptorFile" to referencingModuleInfo.descriptorFile
)
}
val prefix = "${referencingModuleInfo.sourceModuleName}/"
if (!moduleName.startsWith(prefix)) {
errors.add(PluginValidationError("Cannot find module $moduleName", getErrorInfo()))
return null
}
val slashIndex = prefix.length - 1
val containingModuleName = moduleName.substring(0, slashIndex)
module = sourceModuleNameToFileInfo.get(containingModuleName)
if (module == null) {
errors.add(PluginValidationError("Cannot find module $containingModuleName", getErrorInfo()))
return null
}
val fileName = "$containingModuleName.${moduleName.substring(slashIndex + 1)}.xml"
val result = loadFileInModule(sourceModule = module.sourceModule, fileName = fileName)
if (result == null) {
errors.add(PluginValidationError(
message = "Module ${module.sourceModule.name} doesn't have descriptor file",
params = mapOf(
"expectedFile" to fileName,
"referencingDescriptorFile" to referencingModuleInfo.descriptorFile,
),
fix = """
Create file $fileName in ${pathToShortString(module.sourceModule.getSourceRoots().first())}
with content:
<idea-plugin package="REPLACE_BY_MODULE_PACKAGE">
</idea-plugin>
"""
))
}
return result
}
}
internal data class ModuleInfo(
val pluginId: String?,
val name: String?,
val sourceModuleName: String,
val descriptorFile: Path,
val packageName: String?,
val descriptor: XmlElement,
) {
val content = mutableListOf<ModuleInfo>()
val dependencies = mutableListOf<Reference>()
val isPlugin: Boolean
get() = pluginId != null
}
internal data class Reference(val name: String, val isPlugin: Boolean)
private data class PluginInfo(val pluginId: String,
val sourceModuleName: String,
val descriptor: XmlElement,
val descriptorFile: Path)
private class ModuleDescriptorFileInfo(val sourceModule: PluginModelValidator.Module) {
var pluginDescriptorFile: Path? = null
var moduleDescriptorFile: Path? = null
var pluginDescriptor: XmlElement? = null
var moduleDescriptor: XmlElement? = null
}
private fun computeModuleSet(sourceModules: List<PluginModelValidator.Module>,
errors: MutableList<Throwable>): LinkedHashMap<String, ModuleDescriptorFileInfo> {
val sourceModuleNameToFileInfo = LinkedHashMap<String, ModuleDescriptorFileInfo>()
for (module in sourceModules) {
// platform/cwm-plugin/resources/META-INF/plugin.xml doesn't have id - ignore for now
if (module.name.startsWith("fleet.") ||
module.name == "fleet" ||
module.name == "intellij.idea.ultimate.resources" ||
// https://youtrack.jetbrains.com/issue/IDEA-261850
module.name == "intellij.indexing.shared.ultimate.plugin.internal.generator" ||
module.name == "intellij.indexing.shared.ultimate.plugin.public" ||
module.name == "kotlin-ultimate.mobile-native.overrides" ||
module.name == "kotlin-ultimate.appcode-with-mobile" ||
module.name == "intellij.javaFX.community" ||
module.name == "intellij.lightEdit" ||
module.name == "intellij.webstorm" ||
module.name == "intellij.cwm.plugin") {
continue
}
for (sourceRoot in module.getSourceRoots()) {
val metaInf = sourceRoot.resolve("META-INF")
val pluginDescriptorFile = metaInf.resolve("plugin.xml")
val pluginDescriptor = try {
readXmlAsModel(Files.newInputStream(pluginDescriptorFile))
}
catch (ignore: NoSuchFileException) {
null
}
val moduleDescriptorFile = sourceRoot.resolve("${module.name}.xml")
val moduleDescriptor = try {
readXmlAsModel(Files.newInputStream(moduleDescriptorFile))
}
catch (ignore: NoSuchFileException) {
null
}
if (Files.exists(metaInf.resolve("${module.name}.xml"))) {
errors.add(PluginValidationError("Module descriptor must be in the root of module root", mapOf(
"module" to module.name,
"moduleDescriptor" to metaInf.resolve("${module.name}.xml"),
)))
continue
}
if (pluginDescriptor == null && moduleDescriptor == null) {
continue
}
val item = sourceModuleNameToFileInfo.computeIfAbsent(module.name) { ModuleDescriptorFileInfo(module) }
if (item.pluginDescriptorFile != null && pluginDescriptor != null) {
errors.add(PluginValidationError("Duplicated plugin.xml", mapOf(
"module" to module.name,
"firstPluginDescriptor" to item.pluginDescriptorFile,
"secondPluginDescriptor" to pluginDescriptorFile,
)))
continue
}
if (item.pluginDescriptorFile != null && moduleDescriptor != null) {
errors.add(PluginValidationError("Module cannot have both plugin.xml and module descriptor", mapOf(
"module" to module.name,
"pluginDescriptor" to item.pluginDescriptorFile,
"moduleDescriptor" to moduleDescriptorFile,
)))
continue
}
if (item.moduleDescriptorFile != null && pluginDescriptor != null) {
errors.add(PluginValidationError("Module cannot have both plugin.xml and module descriptor", mapOf(
"module" to module.name,
"pluginDescriptor" to pluginDescriptorFile,
"moduleDescriptor" to item.moduleDescriptorFile,
)))
continue
}
if (pluginDescriptor == null) {
item.moduleDescriptorFile = moduleDescriptorFile
item.moduleDescriptor = moduleDescriptor
}
else {
item.pluginDescriptorFile = pluginDescriptorFile
item.pluginDescriptor = pluginDescriptor
}
}
}
return sourceModuleNameToFileInfo
}
private fun writeModuleInfo(writer: JsonGenerator, item: ModuleInfo) {
writer.obj {
writer.writeStringField("name", item.name ?: item.sourceModuleName)
writer.writeStringField("package", item.packageName)
writer.writeStringField("descriptor", pathToShortString(item.descriptorFile))
if (!item.content.isEmpty()) {
writer.array("content") {
for (child in item.content) {
writeModuleInfo(writer, child)
}
}
}
if (!item.dependencies.isEmpty()) {
writer.array("dependencies") {
writeDependencies(item.dependencies, writer)
}
}
}
}
private fun pathToShortString(file: Path): String {
return if (homePath.fileSystem === file.fileSystem) homePath.relativize(file).toString() else file.toString()
}
private fun writeDependencies(items: List<Reference>, writer: JsonGenerator) {
for (entry in items) {
writer.obj {
writer.writeStringField(if (entry.isPlugin) "plugin" else "module", entry.name)
}
}
}
private class PluginValidationError(message: String) : RuntimeException(message) {
constructor(message: String, params: Map<String, Any?>) :
this(message + " (\n ${params.entries.joinToString(separator = ",\n ") { "${it.key}=${paramValueToString(it.value)}" }}\n)")
constructor(message: String, params: Map<String, Any?>, fix: String) : this(
message +
" (\n ${params.entries.joinToString(separator = ",\n ") { "${it.key}=${paramValueToString(it.value)}" }}\n)" +
"\n\nProposed fix:\n\n" + fix.trimIndent() + "\n\n"
)
}
private fun paramValueToString(value: Any?): String {
return when (value) {
is Path -> pathToShortString(value)
else -> value.toString()
}
}
private fun loadFileInModule(sourceModule: PluginModelValidator.Module, fileName: String): ModuleDescriptorFileInfo? {
for (sourceRoot in sourceModule.getSourceRoots()) {
try {
val file = sourceRoot.resolve(fileName)
val info = ModuleDescriptorFileInfo(sourceModule)
info.moduleDescriptor = readXmlAsModel(Files.newInputStream(file))
info.moduleDescriptorFile = file
return info
}
catch (ignore: NoSuchFileException) {
}
}
return null
}
private fun hasContentOrDependenciesInV2Format(descriptor: XmlElement): Boolean {
return descriptor.children.any { it.name == "content" || it.name == "dependencies" }
}

View File

@@ -0,0 +1,145 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.TestDataPath
import com.intellij.testFramework.assertions.Assertions.assertThat
import com.intellij.testFramework.assertions.CleanupSnapshots
import com.intellij.testFramework.rules.InMemoryFsRule
import com.intellij.util.getErrorsAsString
import com.intellij.util.io.sanitizeFileName
import com.intellij.util.io.write
import org.intellij.lang.annotations.Language
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
import java.nio.file.Path
private val testSnapshotDir = Path.of(PlatformTestUtil.getCommunityPath(), "platform/platform-tests/testSnapshots/plugin-validator")
@TestDataPath("\$CONTENT_ROOT/testSnapshots/plugin-validator")
class PluginModelValidatorTest {
@Rule @JvmField val inMemoryFs = InMemoryFsRule()
@Rule @JvmField val testName = TestName()
private val snapshot: Path
get() = testSnapshotDir.resolve("${sanitizeFileName(testName.methodName)}.json")
companion object {
@ClassRule @JvmField val cleanupSnapshots = CleanupSnapshots(testSnapshotDir)
}
@Test
fun `dependency on a plugin in a new format must be in a plugin with package prefix`() {
val root = inMemoryFs.fs.getPath("/")
val modules = produceDependencyAndDependentPlugins(root) { it.replace(" package=\"dependentPackagePrefix\"", "") }
val validator = PluginModelValidator()
validator.validate(modules)
assertThatErrorsToMatchSnapshot(validator)
}
@Test
fun `dependency on a plugin is specified as a plugin`() {
val root = inMemoryFs.fs.getPath("/")
val modules = produceDependencyAndDependentPlugins(root) { it }
val validator = PluginModelValidator()
assertThat(validator.validate(modules)).isEmpty()
assertThat(validator.graphAsString()).toMatchSnapshot(snapshot)
}
@Test
fun `dependency on a plugin must be specified as a plugin`() {
val root = inMemoryFs.fs.getPath("/")
val modules = produceDependencyAndDependentPlugins(root) { it.replace("<plugin id=\"dependency\"/>", "<module name=\"intellij.dependent\"/>") }
val validator = PluginModelValidator()
validator.validate(modules)
assertThatErrorsToMatchSnapshot(validator)
}
@Test
fun `dependency on a plugin must be resolvable`() {
val root = inMemoryFs.fs.getPath("/")
val modules = produceDependencyAndDependentPlugins(root) { it.replace("<plugin id=\"dependency\"/>", "<plugin id=\"incorrectId\"/>") }
val validator = PluginModelValidator()
validator.validate(modules)
assertThatErrorsToMatchSnapshot(validator)
}
@Test
fun `content module in the same source module`() {
val modules = producePluginWithContentModuleInTheSameSourceModule(inMemoryFs.fs.getPath("/")) { it }
val validator = PluginModelValidator()
validator.validate(modules)
assertThat(validator.getErrors()).isEmpty()
assertThat(validator.graphAsString()).toMatchSnapshot(snapshot)
}
@Test
fun `validate dependencies of content module in the same source module`() {
val modules = producePluginWithContentModuleInTheSameSourceModule(inMemoryFs.fs.getPath("/")) {
it.replace("<dependencies>", "<dependencies><module name=\"com.intellij.diagram\"/>")
}
val validator = PluginModelValidator()
validator.validate(modules)
assertThatErrorsToMatchSnapshot(validator)
}
private fun producePluginWithContentModuleInTheSameSourceModule(root: Path,
mutator: (String) -> String): List<PluginModelValidator.Module> {
val moduleRoot = root.resolve("intellij.angularJs")
val module = writePluginXml("intellij.angularJs", moduleRoot, """
<!--suppress PluginXmlValidity -->
<idea-plugin>
<id>AngularJs</id>
<content>
<module name="intellij.angularJs/diagram"/>
</content>
</idea-plugin>
""")
moduleRoot.resolve("intellij.angularJs.diagram.xml").write(mutator("""
<idea-plugin package="org.angularjs.diagram">
<dependencies>
</dependencies>
</idea-plugin>
"""))
return listOf(module)
}
private fun produceDependencyAndDependentPlugins(root: Path, mutator: (String) -> String): List<PluginModelValidator.Module> {
val modules = mutableListOf<PluginModelValidator.Module>()
modules.add(writePluginXml("intellij.dependency", root.resolve("dependency"), """
<!--suppress PluginXmlValidity -->
<idea-plugin package="dependencyPackagePrefix">
<id>dependency</id>
</idea-plugin>
"""))
modules.add(writePluginXml("intellij.dependent", root.resolve("dependent"), mutator("""
<idea-plugin package="dependentPackagePrefix">
<id>dependent</id>
<dependencies>
<plugin id="dependency"/>
</dependencies>
</idea-plugin>
""")))
return modules
}
private fun assertThatErrorsToMatchSnapshot(validator: PluginModelValidator) {
assertThat(getErrorsAsString(validator.getErrors(), includeStackTrace = false)).toMatchSnapshot(snapshot)
}
}
private fun writePluginXml(name: String, root: Path, @Language("xml") content: String): PluginModelValidator.Module {
root.resolve("META-INF/plugin.xml").write(content.trimIndent())
return TestModule(name, listOf(root))
}
private class TestModule(override val name: String, private val sourceRoots: List<Path>) : PluginModelValidator.Module {
override fun getSourceRoots() = sourceRoots
}

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.project;
import org.jetbrains.annotations.NotNull;
@@ -23,7 +9,7 @@ import java.util.Objects;
/**
* @author Dmitry Avdeev
*/
public class ProjectType {
public final class ProjectType {
private @Nullable String id;

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Suppress("DeprecatedCallableAddReplaceWith", "ReplaceNegatedIsEmptyWithIsNotEmpty")
package com.intellij.serviceContainer
@@ -110,7 +110,10 @@ abstract class ComponentManagerImpl @JvmOverloads constructor(internal val paren
mainContainerDescriptor: ContainerDescriptor,
componentManager: ComponentManagerImpl,
crossinline task: (IdeaPluginDescriptorImpl, ContainerDescriptor) -> Unit) {
executeRegisterTask(mainPluginDescriptor, mainContainerDescriptor, componentManager::getContainerDescriptor, task)
task(mainPluginDescriptor, mainContainerDescriptor)
executeRegisterTaskForContent(mainPluginDescriptor) {
task(it, componentManager.getContainerDescriptor(it))
}
}
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:ApiStatus.Internal
package com.intellij.serviceContainer
import com.intellij.ide.plugins.ContainerDescriptor
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
import com.intellij.ide.plugins.PluginManagerCore
@@ -12,6 +12,7 @@ import com.intellij.openapi.extensions.PluginDescriptor
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import org.jetbrains.annotations.ApiStatus
import java.lang.reflect.Modifier
internal fun checkCanceledIfNotInClassInit() {
@@ -39,31 +40,26 @@ fun precomputeExtensionModel(plugins: List<IdeaPluginDescriptorImpl>): Precomput
val extensionPointDescriptors = ArrayList<List<ExtensionPointDescriptor>>()
val pluginDescriptors = ArrayList<IdeaPluginDescriptor>()
var extensionPointTotalCount = 0
val containerSelector: (pluginDescriptor: IdeaPluginDescriptorImpl) -> ContainerDescriptor = { it.moduleContainerDescriptor }
val nameToExtensions = HashMap<String, MutableList<Pair<IdeaPluginDescriptor, List<ExtensionDescriptor>>>>()
// step 1 - collect container level extension points
for (plugin in plugins) {
executeRegisterTask(plugin, containerSelector(plugin), containerSelector) { pluginDescriptor, containerDescriptor ->
containerDescriptor.extensionPoints?.let {
extensionPointDescriptors.add(it)
pluginDescriptors.add(pluginDescriptor)
extensionPointTotalCount += it.size
executeRegisterTask(plugins) { pluginDescriptor ->
pluginDescriptor.moduleContainerDescriptor.extensionPoints?.let {
extensionPointDescriptors.add(it)
pluginDescriptors.add(pluginDescriptor)
extensionPointTotalCount += it.size
for (descriptor in it) {
nameToExtensions.put(descriptor.getQualifiedName(pluginDescriptor), mutableListOf())
}
for (descriptor in it) {
nameToExtensions.put(descriptor.getQualifiedName(pluginDescriptor), mutableListOf())
}
}
}
// step 2 - collect container level extensions
for (plugin in plugins) {
executeRegisterTask(plugin, containerSelector(plugin), containerSelector) { pluginDescriptor, _ ->
val unsortedMap = pluginDescriptor.epNameToExtensions ?: return@executeRegisterTask
for ((name, list) in unsortedMap.entries) {
nameToExtensions.get(name)?.add(pluginDescriptor to list)
}
executeRegisterTask(plugins) { pluginDescriptor ->
val unsortedMap = pluginDescriptor.epNameToExtensions ?: return@executeRegisterTask
for ((name, list) in unsortedMap.entries) {
nameToExtensions.get(name)?.add(pluginDescriptor to list)
}
}
@@ -76,29 +72,40 @@ fun precomputeExtensionModel(plugins: List<IdeaPluginDescriptorImpl>): Precomput
)
}
internal inline fun executeRegisterTask(mainPluginDescriptor: IdeaPluginDescriptorImpl,
mainContainerDescriptor: ContainerDescriptor,
containerSelector: (pluginDescriptor: IdeaPluginDescriptorImpl) -> ContainerDescriptor,
crossinline task: (IdeaPluginDescriptorImpl, ContainerDescriptor) -> Unit) {
task(mainPluginDescriptor, mainContainerDescriptor)
inline fun executeRegisterTask(plugins: List<IdeaPluginDescriptorImpl>, crossinline task: (IdeaPluginDescriptorImpl) -> Unit) {
for (plugin in plugins) {
task(plugin)
executeRegisterTaskForContent(mainPluginDescriptor = plugin, task = task)
}
}
@PublishedApi
internal inline fun executeRegisterTaskForContent(mainPluginDescriptor: IdeaPluginDescriptorImpl,
crossinline task: (IdeaPluginDescriptorImpl) -> Unit) {
for (dep in mainPluginDescriptor.pluginDependencies) {
if (dep.isDisabledOrBroken) {
val subDescriptor = dep.subDescriptor
if (subDescriptor?.classLoader == null) {
continue
}
val subPluginDescriptor = dep.subDescriptor ?: continue
task(subPluginDescriptor, containerSelector(subPluginDescriptor))
task(subDescriptor)
for (subDep in subPluginDescriptor.pluginDependencies) {
if (!subDep.isDisabledOrBroken) {
val d = subDep.subDescriptor ?: continue
task(d, containerSelector(d))
for (subDep in subDescriptor.pluginDependencies) {
val d = subDep.subDescriptor
if (d?.classLoader != null) {
task(d)
assert(d.pluginDependencies.isEmpty() || d.pluginDependencies.all { it.subDescriptor == null })
}
}
}
}
for (item in mainPluginDescriptor.content.modules) {
val module = item.requireDescriptor()
if (module.classLoader != null) {
task(module)
}
}
}
internal fun isGettingServiceAllowedDuringPluginUnloading(descriptor: PluginDescriptor): Boolean {
return descriptor.isRequireRestart ||

View File

@@ -24,23 +24,23 @@ import java.util.List;
@SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
public final class Assertions extends org.assertj.core.api.Assertions {
@NotNull
public static JdomAssert assertThat(@Nullable Element element) {
public static @NotNull JdomAssert assertThat(@Nullable Element element) {
return new JdomAssert(element);
}
@NotNull
public static PathAssertEx assertThat(@Nullable Path actual) {
public static @NotNull PathAssertEx assertThat(@Nullable Path actual) {
return new PathAssertEx(actual);
}
@NotNull
public static StringAssertEx assertThat(@Nullable String actual) {
public static @NotNull StringAssertEx assertThat(@Nullable String actual) {
return new StringAssertEx(actual);
}
@NotNull
public static <ELEMENT> ListAssertEx<ELEMENT> assertThat(@Nullable List<? extends ELEMENT> actual) {
public static @NotNull CharSequenceEx assertThat(@Nullable CharSequence actual) {
return new CharSequenceEx(actual);
}
public static @NotNull <ELEMENT> ListAssertEx<ELEMENT> assertThat(@Nullable List<? extends ELEMENT> actual) {
return new ListAssertEx<>(actual);
}
}

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.testFramework.assertions
import org.assertj.core.api.AbstractCharSequenceAssert
import org.assertj.core.api.AbstractStringAssert
import java.nio.file.Path
@@ -10,4 +11,12 @@ class StringAssertEx(actual: String?) : AbstractStringAssert<StringAssertEx>(act
isNotNull
compareFileContent(actual, snapshotFile)
}
}
class CharSequenceEx(actual: CharSequence?) : AbstractCharSequenceAssert<CharSequenceEx, CharSequence>(actual, CharSequenceEx::class.java) {
fun toMatchSnapshot(snapshotFile: Path) {
snapshotFileUsageListeners.forEach { it.beforeMatch(snapshotFile) }
isNotNull
compareFileContent(actual, snapshotFile)
}
}

View File

@@ -74,7 +74,7 @@ fun compareFileContent(actual: Any, snapshotFile: Path, updateIfMismatch: Boolea
throw e
}
println("Write a new snapshot ${snapshotFile.fileName}")
println("Write a new snapshot: ${snapshotFile.fileName}")
snapshotFile.write(actualContent)
return
}
@@ -84,8 +84,8 @@ fun compareFileContent(actual: Any, snapshotFile: Path, updateIfMismatch: Boolea
}
if (updateIfMismatch) {
System.out.println("UPDATED snapshot ${snapshotFile.fileName}")
snapshotFile.write(StringBuilder(actualContent))
println("UPDATED snapshot ${snapshotFile.fileName}")
snapshotFile.write(actualContent)
}
else {
val firstMismatch = StringUtil.commonPrefixLength(actualContent, expected)

View File

@@ -17,6 +17,7 @@ import com.intellij.ide.impl.HeadlessDataManager
import com.intellij.ide.startup.impl.StartupManagerImpl
import com.intellij.ide.structureView.StructureViewFactory
import com.intellij.ide.structureView.impl.StructureViewFactoryImpl
import com.intellij.idea.StartupUtil
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.DataProvider
import com.intellij.openapi.application.Application
@@ -49,6 +50,7 @@ import com.intellij.util.MemoryDumpHelper
import com.intellij.util.ReflectionUtil
import com.intellij.util.concurrency.AppExecutorUtil
import com.intellij.util.concurrency.AppScheduledExecutorService
import com.intellij.util.lang.Java11Shim
import com.intellij.util.ref.GCUtil
import com.intellij.util.throwIfNotEmpty
import com.intellij.util.ui.UIUtil
@@ -68,6 +70,10 @@ import javax.swing.Timer
class TestApplicationManager private constructor() {
companion object {
init {
Java11Shim.INSTANCE = StartupUtil.Java11ShimImpl()
}
@Volatile
private var ourInstance: TestApplicationManager? = null
@Volatile
@@ -174,7 +180,10 @@ private var testCounter = 0
// Kotlin allows to easily debug code and to get clear and short stack traces
@ApiStatus.Internal
fun tearDownProjectAndApp(project: Project) {
if (project.isDisposed) return;
if (project.isDisposed) {
return
}
val isLightProject = ProjectManagerImpl.isLight(project)
val l = mutableListOf<Throwable>()
val app = ApplicationManager.getApplication()

View File

@@ -0,0 +1,36 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.lang;
import com.intellij.ReviseWhenPortedToJDK;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@ReviseWhenPortedToJDK("11")
@ApiStatus.Internal
// implementation of `copyOf` is allowed to not do copy - it can return the same map, read `copyOf` as `immutable`
public abstract class Java11Shim {
@SuppressWarnings("StaticNonFinalField")
public static @NotNull Java11Shim INSTANCE = new Java11Shim() {
@Override
public <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
return Collections.unmodifiableMap(map);
}
@Override
public <E> Set<E> copyOf(Set<? extends E> collection) {
return Collections.unmodifiableSet(collection);
}
@Override
public <E> List<E> copyOf(List<? extends E> collection) {
return Collections.unmodifiableList(new ArrayList<>(collection));
}
};
public abstract <@NotNull K, @NotNull V> Map<K, V> copyOf(Map<? extends K, ? extends V> map);
public abstract <@NotNull E> Set<E> copyOf(Set<? extends E> collection);
public abstract <@NotNull E> List<E> copyOf(List<? extends E> collection);
}

View File

@@ -2,6 +2,7 @@
package com.intellij.util
import com.intellij.util.lang.CompoundRuntimeException
import org.jetbrains.annotations.ApiStatus
fun throwIfNotEmpty(errors: List<Throwable>) {
val size = errors.size
@@ -11,4 +12,22 @@ fun throwIfNotEmpty(errors: List<Throwable>) {
else if (size != 0) {
throw CompoundRuntimeException(errors)
}
}
@ApiStatus.Internal
fun getErrorsAsString(errors: List<Throwable>, includeStackTrace: Boolean = true): CharSequence {
val sb = StringBuilder()
sb.append("${errors.size} errors:\n")
for (i in errors.indices) {
sb.append("[").append(i + 1).append("]: ------------------------------\n")
val error = errors[i]
val line = if (includeStackTrace) ExceptionUtil.getThrowableText(error) else error.message!!
sb.append(line)
if (!line.endsWith('\n')) {
sb.append('\n')
}
}
sb.append("-".repeat(5))
sb.append("------------------------------\n")
return sb
}

View File

@@ -81,6 +81,10 @@ public final class Murmur3_32Hash {
return fMix(h1, 2 * input.length());
}
public int hashString(CharSequence input) {
return hashString(input, 0, input.length());
}
public int hashString(CharSequence input, int start, int end) {
int h1 = seed;
int i = start;

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.util;
import com.intellij.openapi.Disposable;
@@ -8,7 +8,6 @@ import com.intellij.openapi.util.objectTree.ThrowableInterner;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.ContainerUtil;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import org.jetbrains.annotations.NotNull;
@@ -184,17 +183,22 @@ final class ObjectTree {
}
private static void handleExceptions(@NotNull List<? extends Throwable> exceptions) {
if (!exceptions.isEmpty()) {
for (Throwable exception : exceptions) {
if (!(exception instanceof ProcessCanceledException)) {
getLogger().error(exception);
}
}
if (exceptions.isEmpty()) {
return;
}
ProcessCanceledException pce = ContainerUtil.findInstance(exceptions, ProcessCanceledException.class);
if (pce != null) {
throw pce;
ProcessCanceledException processCanceledException = null;
for (Throwable exception : exceptions) {
if (!(exception instanceof ProcessCanceledException)) {
getLogger().error(exception);
}
else if (processCanceledException == null) {
processCanceledException = (ProcessCanceledException)exception;
}
}
if (processCanceledException != null) {
throw processCanceledException;
}
}

View File

@@ -21,7 +21,7 @@ interface DataLoader {
}
@ApiStatus.Internal
class LocalFsDataLoader(private val basePath: Path) : DataLoader {
class LocalFsDataLoader(val basePath: Path) : DataLoader {
override val pool: ZipFilePool?
get() = ZipFilePool.POOL

View File

@@ -0,0 +1,25 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.TestOnly
@ApiStatus.Internal
data class XmlElement(
@JvmField val name: String,
@JvmField val attributes: Map<String, String>,
@JvmField val children: List<XmlElement>,
@JvmField val content: String?,
) {
fun count(name: String): Int = children.count { it.name == name }
fun getAttributeValue(name: String): String? = attributes.get(name)
fun getAttributeValue(name: String, defaultValue: String?): String? = attributes.get(name) ?: defaultValue
fun getChild(name: String): XmlElement? = children.firstOrNull { it.name == name }
// should not be used - uncomment for migration
@TestOnly
fun getChildren(name: String): List<XmlElement> = children.filter { it.name == name }
}

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