new plugin descriptor format - full support on all levels
GitOrigin-RevId: 718c9401f22900c30029ec62c23f60f6f22278ee
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
166
docs/out/getting-service.svg
Normal 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&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 |
186
docs/out/icon-loading-stat.svg
Normal 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&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
@@ -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
|
After Width: | Height: | Size: 334 KiB |
118
docs/out/plugin-model.svg
Normal 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&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 |
175
docs/out/projectClose-dispose-flow.svg
Normal 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&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
@@ -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
@@ -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
|
||||
@@ -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, doesn’t have own classloader, but it will be changed in the future and explicit dependency on plugin’s 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
@@ -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.
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 + "/>";
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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!!
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 +
|
||||
')'
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
75
platform/core-impl/src/com/intellij/ide/plugins/PluginSet.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Please refer to [Storing Sensitive Data](https://plugins.jetbrains.com/docs/intellij/persisting-sensitive-data.html)
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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&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 |
@@ -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&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 |
@@ -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>
|
||||
@@ -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&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 |
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/*)">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
-----------------------------------
|
||||
@@ -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"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
-----------------------------------
|
||||
@@ -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"/>
|
||||
|
||||
-----------------------------------
|
||||
@@ -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
|
||||
)
|
||||
-----------------------------------
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
""")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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" }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
25
platform/util/src/com/intellij/util/XmlElement.kt
Normal 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 }
|
||||
}
|
||||