mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-04 17:20:55 +07:00
[polySymbols] WEB-73289 PolySymbols: simplify PolySymbol interface - extract PolySymbolWithDocumentation interface
GitOrigin-RevId: 7eff7437c872855fa72415da703781057f2e935d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
997750ddad
commit
f56f2f22d5
@@ -20,14 +20,9 @@
|
||||
- sf:PROP_NO_DOC:java.lang.String
|
||||
- sf:PROP_READ_ONLY:java.lang.String
|
||||
- adjustNameForRefactoring(com.intellij.polySymbols.query.PolySymbolsQueryExecutor,java.lang.String,java.lang.String):java.lang.String
|
||||
- createDocumentation(com.intellij.psi.PsiElement):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- a:createPointer():com.intellij.model.Pointer
|
||||
- getApiStatus():com.intellij.polySymbols.PolySymbolApiStatus
|
||||
- getAttributeValue():com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
- getDefaultValue():java.lang.String
|
||||
- getDescription():java.lang.String
|
||||
- getDescriptionSections():java.util.Map
|
||||
- getDocUrl():java.lang.String
|
||||
- getDocumentationTarget(com.intellij.psi.PsiElement):com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
- getIcon():javax.swing.Icon
|
||||
- getModificationCount():J
|
||||
@@ -420,7 +415,7 @@
|
||||
- equals(java.lang.Object):Z
|
||||
- hashCode():I
|
||||
*:com.intellij.polySymbols.customElements.CustomElementsSymbol
|
||||
- com.intellij.polySymbols.PolySymbol
|
||||
- com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
- *sf:Companion:com.intellij.polySymbols.customElements.CustomElementsSymbol$Companion
|
||||
*f:com.intellij.polySymbols.customElements.CustomElementsSymbol$Companion
|
||||
- f:getCEM_DECLARATIONS():com.intellij.polySymbols.PolySymbolQualifiedKind
|
||||
@@ -441,6 +436,7 @@
|
||||
*:com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- *sf:Companion:com.intellij.polySymbols.documentation.PolySymbolDocumentation$Companion
|
||||
- appendFootnote(java.lang.String):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- a:build(com.intellij.polySymbols.PolySymbolOrigin):com.intellij.platform.backend.documentation.DocumentationResult
|
||||
- a:getApiStatus():com.intellij.polySymbols.PolySymbolApiStatus
|
||||
- a:getDefaultValue():java.lang.String
|
||||
- a:getDefinition():java.lang.String
|
||||
@@ -471,8 +467,8 @@
|
||||
- a:withName(java.lang.String):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- a:withRequired(Z):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
*f:com.intellij.polySymbols.documentation.PolySymbolDocumentation$Companion
|
||||
- f:create(com.intellij.polySymbols.PolySymbol,com.intellij.psi.PsiElement,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,com.intellij.polySymbols.PolySymbolApiStatus,Z,java.lang.String,java.lang.String,javax.swing.Icon,java.util.Map,java.lang.String):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- bs:create$default(com.intellij.polySymbols.documentation.PolySymbolDocumentation$Companion,com.intellij.polySymbols.PolySymbol,com.intellij.psi.PsiElement,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,com.intellij.polySymbols.PolySymbolApiStatus,Z,java.lang.String,java.lang.String,javax.swing.Icon,java.util.Map,java.lang.String,I,java.lang.Object):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- f:create(com.intellij.polySymbols.documentation.PolySymbolWithDocumentation,com.intellij.psi.PsiElement,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,com.intellij.polySymbols.PolySymbolApiStatus,Z,java.lang.String,java.lang.String,javax.swing.Icon,java.util.Map,java.lang.String):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- bs:create$default(com.intellij.polySymbols.documentation.PolySymbolDocumentation$Companion,com.intellij.polySymbols.documentation.PolySymbolWithDocumentation,com.intellij.psi.PsiElement,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,com.intellij.polySymbols.PolySymbolApiStatus,Z,java.lang.String,java.lang.String,javax.swing.Icon,java.util.Map,java.lang.String,I,java.lang.Object):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
*:com.intellij.polySymbols.documentation.PolySymbolDocumentationCustomizer
|
||||
- *sf:Companion:com.intellij.polySymbols.documentation.PolySymbolDocumentationCustomizer$Companion
|
||||
- a:customize(com.intellij.polySymbols.PolySymbol,com.intellij.psi.PsiElement,com.intellij.polySymbols.documentation.PolySymbolDocumentation):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
@@ -480,10 +476,18 @@
|
||||
- f:getEP_NAME():com.intellij.openapi.extensions.ExtensionPointName
|
||||
*:com.intellij.polySymbols.documentation.PolySymbolDocumentationTarget
|
||||
- com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
- computeDocumentation():com.intellij.platform.backend.documentation.DocumentationResult
|
||||
- computePresentation():com.intellij.platform.backend.presentation.TargetPresentation
|
||||
- a:getLocation():com.intellij.psi.PsiElement
|
||||
- a:getSymbol():com.intellij.polySymbols.PolySymbol
|
||||
*:com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
- com.intellij.polySymbols.PolySymbol
|
||||
- createDocumentation(com.intellij.psi.PsiElement):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- a:createPointer():com.intellij.model.Pointer
|
||||
- getDefaultValue():java.lang.String
|
||||
- getDescription():java.lang.String
|
||||
- getDescriptionSections():java.util.Map
|
||||
- getDocUrl():java.lang.String
|
||||
- getDocumentationTarget(com.intellij.psi.PsiElement):com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
*a:com.intellij.polySymbols.framework.PolySymbolsFramework
|
||||
- *sf:Companion:com.intellij.polySymbols.framework.PolySymbolsFramework$Companion
|
||||
- <init>():V
|
||||
@@ -880,20 +884,14 @@
|
||||
- createPointer():com.intellij.model.Pointer
|
||||
- f:getNavigationItem():com.intellij.navigation.NavigationItem
|
||||
- navigationRequest():com.intellij.platform.backend.navigation.NavigationRequest
|
||||
*a:com.intellij.polySymbols.utils.PolySymbolDelegate
|
||||
*:com.intellij.polySymbols.utils.PolySymbolDelegate
|
||||
- com.intellij.polySymbols.PolySymbol
|
||||
- *sf:Companion:com.intellij.polySymbols.utils.PolySymbolDelegate$Companion
|
||||
- <init>(com.intellij.polySymbols.PolySymbol):V
|
||||
- createDocumentation(com.intellij.psi.PsiElement):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- getAbstract():Z
|
||||
- getApiStatus():com.intellij.polySymbols.PolySymbolApiStatus
|
||||
- getAttributeValue():com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
- getCodeCompletions(com.intellij.polySymbols.PolySymbolQualifiedName,com.intellij.polySymbols.query.PolySymbolsCodeCompletionQueryParams,com.intellij.util.containers.Stack):java.util.List
|
||||
- getDefaultValue():java.lang.String
|
||||
- f:getDelegate():com.intellij.polySymbols.PolySymbol
|
||||
- getDescription():java.lang.String
|
||||
- getDescriptionSections():java.util.Map
|
||||
- getDocUrl():java.lang.String
|
||||
- a:getDelegate():com.intellij.polySymbols.PolySymbol
|
||||
- getDocumentationTarget(com.intellij.psi.PsiElement):com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
- getExtension():Z
|
||||
- getIcon():javax.swing.Icon
|
||||
@@ -916,9 +914,18 @@
|
||||
- getType():java.lang.Object
|
||||
- getVirtual():Z
|
||||
- isExclusiveFor(com.intellij.polySymbols.PolySymbolQualifiedKind):Z
|
||||
- sf:unwrapAllDelegates(com.intellij.polySymbols.PolySymbol):com.intellij.polySymbols.PolySymbol
|
||||
- s:unwrapAllDelegates(com.intellij.polySymbols.PolySymbol):com.intellij.polySymbols.PolySymbol
|
||||
*f:com.intellij.polySymbols.utils.PolySymbolDelegate$Companion
|
||||
- f:unwrapAllDelegates(com.intellij.polySymbols.PolySymbol):com.intellij.polySymbols.PolySymbol
|
||||
*:com.intellij.polySymbols.utils.PolySymbolDelegateWithDocumentation
|
||||
- com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
- com.intellij.polySymbols.utils.PolySymbolDelegate
|
||||
- createDocumentation(com.intellij.psi.PsiElement):com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
- getDefaultValue():java.lang.String
|
||||
- getDescription():java.lang.String
|
||||
- getDescriptionSections():java.util.Map
|
||||
- getDocUrl():java.lang.String
|
||||
- getDocumentationTarget(com.intellij.psi.PsiElement):com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
*:com.intellij.polySymbols.utils.PolySymbolTypeSupport
|
||||
- a:resolve(java.util.List):java.lang.Object
|
||||
*:com.intellij.polySymbols.utils.PolySymbolTypeSupport$TypeReference
|
||||
@@ -1025,10 +1032,9 @@
|
||||
*:com.intellij.polySymbols.utils.PolySymbolsStructuredScope$PolySymbolsPsiScopesHolder$ScopeModifier
|
||||
- a:addSymbol(com.intellij.polySymbols.PolySymbol):V
|
||||
- a:addSymbols(java.util.List):V
|
||||
*a:com.intellij.polySymbols.utils.PsiSourcedPolySymbolDelegate
|
||||
- com.intellij.polySymbols.utils.PolySymbolDelegate
|
||||
*:com.intellij.polySymbols.utils.PsiSourcedPolySymbolDelegate
|
||||
- com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
- <init>(com.intellij.polySymbols.search.PsiSourcedPolySymbol):V
|
||||
- com.intellij.polySymbols.utils.PolySymbolDelegate
|
||||
- getNavigationTargets(com.intellij.openapi.project.Project):java.util.Collection
|
||||
- getPsiContext():com.intellij.psi.PsiElement
|
||||
- getSource():com.intellij.psi.PsiElement
|
||||
@@ -1052,8 +1058,11 @@
|
||||
- f:create(com.intellij.polySymbols.PolySymbolQualifiedKind,java.lang.String,com.intellij.polySymbols.PolySymbolOrigin,com.intellij.polySymbols.PolySymbolQualifiedKind[],com.intellij.polySymbols.PolySymbol$Priority,java.util.List):com.intellij.polySymbols.utils.ReferencingPolySymbol
|
||||
- bs:create$default(com.intellij.polySymbols.utils.ReferencingPolySymbol$Companion,com.intellij.polySymbols.PolySymbolQualifiedKind,java.lang.String,com.intellij.polySymbols.PolySymbolOrigin,com.intellij.polySymbols.PolySymbolQualifiedKind[],com.intellij.polySymbols.PolySymbol$Priority,java.util.List,I,java.lang.Object):com.intellij.polySymbols.utils.ReferencingPolySymbol
|
||||
*:com.intellij.polySymbols.webTypes.WebTypesSymbol
|
||||
- com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
- com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
- *sf:Companion:com.intellij.polySymbols.webTypes.WebTypesSymbol$Companion
|
||||
- a:createPointer():com.intellij.model.Pointer
|
||||
- getDocumentationTarget(com.intellij.psi.PsiElement):com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
- a:getLocation():com.intellij.polySymbols.webTypes.WebTypesSymbol$Location
|
||||
*f:com.intellij.polySymbols.webTypes.WebTypesSymbol$Companion
|
||||
*:com.intellij.polySymbols.webTypes.WebTypesSymbol$FileExport
|
||||
|
||||
@@ -73,26 +73,6 @@ interface PolySymbol : PolySymbolsScope, Symbol, NavigatableSymbol, PolySymbolsP
|
||||
*/
|
||||
val name: @NlsSafe String
|
||||
|
||||
/**
|
||||
* An optional text, which describes the symbol purpose and usage.
|
||||
* It is rendered in the documentation popup or view.
|
||||
*/
|
||||
val description: @Nls String?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* Additional sections, to be rendered in the symbols’ documentation.
|
||||
* Each section should have a name, but the contents can be empty.
|
||||
*/
|
||||
val descriptionSections: Map<@Nls String, @Nls String>
|
||||
get() = emptyMap()
|
||||
|
||||
/**
|
||||
* An optional URL to a website with detailed symbol's documentation
|
||||
*/
|
||||
val docUrl: @NlsSafe String?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* An optional icon associated with the symbol, which is going to be used across the IDE.
|
||||
* If none is specified, a default icon of the origin will be used and if that’s not available,
|
||||
@@ -101,13 +81,6 @@ interface PolySymbol : PolySymbolsScope, Symbol, NavigatableSymbol, PolySymbolsP
|
||||
val icon: Icon?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* If the symbol represents some property, variable or anything that can hold a value,
|
||||
* this property documents what is the default value.
|
||||
*/
|
||||
val defaultValue: @NlsSafe String?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* The type of the symbol. The type can be interpreted only within the context of symbol origin
|
||||
* and in regard to its namespace and kind. The type may be a language type,
|
||||
@@ -279,33 +252,18 @@ interface PolySymbol : PolySymbolsScope, Symbol, NavigatableSymbol, PolySymbolsP
|
||||
|
||||
/**
|
||||
* Used by Poly Symbols framework to get a [DocumentationTarget], which handles documentation
|
||||
* rendering for the symbol. Default implementation will use [createDocumentation]
|
||||
* to render the documentation.
|
||||
* rendering for the symbol. You may implement [com.intellij.polySymbols.documentation.PolySymbolWithDocumentation]
|
||||
* interface, which will provide a default implementation to render the documentation.
|
||||
*/
|
||||
fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
if (properties[PROP_NO_DOC] != true)
|
||||
PolySymbolDocumentationTargetImpl(this, location)
|
||||
else
|
||||
null
|
||||
|
||||
/**
|
||||
* Returns [PolySymbolDocumentation] - an interface holding information required to render documentation for the symbol.
|
||||
* By default, it's contents are build from the available Poly Symbol information.
|
||||
*
|
||||
* To customize symbols documentation, one can override the method, or implement [PolySymbolDocumentationCustomizer].
|
||||
*
|
||||
* [PolySymbolDocumentation] interface provides builder methods for customizing the documentation.
|
||||
* `with*` methods return a copy of the documentation with customized fields.
|
||||
*/
|
||||
fun createDocumentation(location: PsiElement?): PolySymbolDocumentation? =
|
||||
PolySymbolDocumentation.create(this, location)
|
||||
null
|
||||
|
||||
override fun getNavigationTargets(project: Project): Collection<NavigationTarget> =
|
||||
emptyList()
|
||||
|
||||
/**
|
||||
* Symbols can be used in [CachedValue]s as dependencies.
|
||||
* If a symbol instance can mutate over the time, it should properly implement this method.
|
||||
* If a symbol instance can mutate over time, it should properly implement this method.
|
||||
*/
|
||||
override fun getModificationCount(): Long = 0
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ import com.intellij.psi.createSmartPointer
|
||||
* for code completion.
|
||||
*/
|
||||
class CodeCompletionPolySymbolWithDocumentation(
|
||||
delegate: PolySymbol,
|
||||
override val delegate: PolySymbol,
|
||||
private val location: PsiElement,
|
||||
) : PolySymbolDelegate<PolySymbol>(delegate), DocumentationSymbol {
|
||||
) : PolySymbolDelegate<PolySymbol>, DocumentationSymbol {
|
||||
|
||||
override fun createPointer(): Pointer<CodeCompletionPolySymbolWithDocumentation> {
|
||||
val delegatePtr = delegate.createPointer()
|
||||
@@ -40,9 +40,9 @@ class CodeCompletionPolySymbolWithDocumentation(
|
||||
}
|
||||
|
||||
class PsiSourcedCodeCompletionPolySymbolWithDocumentation(
|
||||
delegate: PsiSourcedPolySymbol,
|
||||
override val delegate: PsiSourcedPolySymbol,
|
||||
private val location: PsiElement,
|
||||
) : PsiSourcedPolySymbolDelegate<PsiSourcedPolySymbol>(delegate), DocumentationSymbol {
|
||||
) : PsiSourcedPolySymbolDelegate<PsiSourcedPolySymbol>, DocumentationSymbol {
|
||||
|
||||
override fun createPointer(): Pointer<PsiSourcedCodeCompletionPolySymbolWithDocumentation> {
|
||||
val delegatePtr = delegate.createPointer()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.customElements
|
||||
|
||||
import com.intellij.polySymbols.PolySymbol
|
||||
import com.intellij.polySymbols.PolySymbolQualifiedKind
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
|
||||
interface CustomElementsSymbol : PolySymbol {
|
||||
interface CustomElementsSymbol : PolySymbolWithDocumentation {
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.intellij.polySymbols.customElements.CustomElementsSymbol
|
||||
import com.intellij.polySymbols.customElements.json.CustomElementClassOrMixinDeclaration
|
||||
import com.intellij.polySymbols.customElements.json.resolve
|
||||
import com.intellij.polySymbols.customElements.json.toApiStatus
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.impl.StaticPolySymbolsScopeBase
|
||||
import com.intellij.polySymbols.patterns.PolySymbolsPattern
|
||||
import com.intellij.polySymbols.query.PolySymbolsCodeCompletionQueryParams
|
||||
@@ -81,7 +82,9 @@ class CustomElementsClassOrMixinDeclarationAdapter private constructor(
|
||||
override val description: String?
|
||||
get() = (base.declaration.description?.takeIf { it.isNotBlank() } ?: base.declaration.summary)
|
||||
?.let { origin.renderDescription(it) }
|
||||
?: superContributions.asSequence().mapNotNull { it.description }.firstOrNull()
|
||||
?: superContributions.asSequence()
|
||||
.mapNotNull { (it as? PolySymbolWithDocumentation)?.description }
|
||||
.firstOrNull()
|
||||
|
||||
override val apiStatus: PolySymbolApiStatus
|
||||
get() = base.declaration.deprecated.toApiStatus(origin) ?: PolySymbolApiStatus.Stable
|
||||
|
||||
@@ -29,7 +29,7 @@ class CustomElementsCustomElementExportSymbol private constructor(
|
||||
override fun withQueryExecutorContext(queryExecutor: PolySymbolsQueryExecutor): PolySymbol =
|
||||
this
|
||||
|
||||
override fun createPointer(): Pointer<out PolySymbol> =
|
||||
override fun createPointer(): Pointer<out CustomElementsSymbol> =
|
||||
Pointer.hardPointer(this)
|
||||
|
||||
override fun matchContext(context: PolyContext): Boolean =
|
||||
|
||||
@@ -3,10 +3,11 @@ package com.intellij.polySymbols.documentation
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.util.text.Strings
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.polySymbols.PolySymbol
|
||||
import com.intellij.platform.backend.documentation.DocumentationResult
|
||||
import com.intellij.polySymbols.PolySymbolApiStatus
|
||||
import com.intellij.polySymbols.PolySymbolOrigin
|
||||
import com.intellij.polySymbols.documentation.impl.PolySymbolDocumentationImpl
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.Nls
|
||||
import javax.swing.Icon
|
||||
@@ -113,18 +114,20 @@ interface PolySymbolDocumentation {
|
||||
|
||||
fun withHeader(header: @Nls String?): PolySymbolDocumentation
|
||||
|
||||
fun with(name: @NlsSafe String = this.name,
|
||||
definition: @NlsSafe String = this.definition,
|
||||
definitionDetails: @Nls String? = this.definitionDetails,
|
||||
description: @Nls String? = this.description,
|
||||
docUrl: @NlsSafe String? = this.docUrl,
|
||||
apiStatus: PolySymbolApiStatus? = this.apiStatus,
|
||||
required: Boolean = this.required,
|
||||
defaultValue: @NlsSafe String? = this.defaultValue,
|
||||
library: @NlsSafe String? = this.library,
|
||||
icon: Icon? = this.icon,
|
||||
additionalSections: Map<@Nls String, @Nls String> = emptyMap(),
|
||||
footnote: @Nls String? = this.footnote): PolySymbolDocumentation
|
||||
fun with(
|
||||
name: @NlsSafe String = this.name,
|
||||
definition: @NlsSafe String = this.definition,
|
||||
definitionDetails: @Nls String? = this.definitionDetails,
|
||||
description: @Nls String? = this.description,
|
||||
docUrl: @NlsSafe String? = this.docUrl,
|
||||
apiStatus: PolySymbolApiStatus? = this.apiStatus,
|
||||
required: Boolean = this.required,
|
||||
defaultValue: @NlsSafe String? = this.defaultValue,
|
||||
library: @NlsSafe String? = this.library,
|
||||
icon: Icon? = this.icon,
|
||||
additionalSections: Map<@Nls String, @Nls String> = emptyMap(),
|
||||
footnote: @Nls String? = this.footnote,
|
||||
): PolySymbolDocumentation
|
||||
|
||||
fun appendFootnote(footnote: @Nls String?): PolySymbolDocumentation =
|
||||
if (footnote != null)
|
||||
@@ -132,26 +135,30 @@ interface PolySymbolDocumentation {
|
||||
else
|
||||
this
|
||||
|
||||
fun build(origin: PolySymbolOrigin): DocumentationResult
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(symbol: PolySymbol,
|
||||
location: PsiElement?,
|
||||
name: String = symbol.name,
|
||||
definition: String = Strings.escapeXmlEntities(symbol.name),
|
||||
definitionDetails: String? = null,
|
||||
description: @Nls String? = symbol.description,
|
||||
docUrl: String? = symbol.docUrl,
|
||||
apiStatus: PolySymbolApiStatus? = symbol.apiStatus,
|
||||
required: Boolean = symbol.required ?: false,
|
||||
defaultValue: String? = symbol.defaultValue ?: symbol.attributeValue?.default,
|
||||
library: String? = symbol.origin.takeIf { it.library != null }
|
||||
?.let { context ->
|
||||
context.library +
|
||||
if (context.version?.takeIf { it != "0.0.0" } != null) "@${context.version}" else ""
|
||||
},
|
||||
icon: Icon? = symbol.icon,
|
||||
descriptionSections: Map<@Nls String, @Nls String> = symbol.descriptionSections,
|
||||
footnote: @Nls String? = null): PolySymbolDocumentation =
|
||||
fun create(
|
||||
symbol: PolySymbolWithDocumentation,
|
||||
location: PsiElement?,
|
||||
name: String = symbol.name,
|
||||
definition: String = Strings.escapeXmlEntities(symbol.name),
|
||||
definitionDetails: String? = null,
|
||||
description: @Nls String? = symbol.description,
|
||||
docUrl: String? = symbol.docUrl,
|
||||
apiStatus: PolySymbolApiStatus? = symbol.apiStatus,
|
||||
required: Boolean = symbol.required ?: false,
|
||||
defaultValue: String? = symbol.defaultValue ?: symbol.attributeValue?.default,
|
||||
library: String? = symbol.origin.takeIf { it.library != null }
|
||||
?.let { context ->
|
||||
context.library +
|
||||
if (context.version?.takeIf { it != "0.0.0" } != null) "@${context.version}" else ""
|
||||
},
|
||||
icon: Icon? = symbol.icon,
|
||||
descriptionSections: Map<@Nls String, @Nls String> = symbol.descriptionSections,
|
||||
footnote: @Nls String? = null,
|
||||
): PolySymbolDocumentation =
|
||||
PolySymbolDocumentationImpl(name, definition, definitionDetails, description, docUrl, apiStatus, required, defaultValue, library, icon,
|
||||
descriptionSections, footnote, null)
|
||||
.let { doc: PolySymbolDocumentation ->
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.documentation
|
||||
|
||||
import com.intellij.platform.backend.documentation.DocumentationResult
|
||||
import com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
import com.intellij.platform.backend.presentation.TargetPresentation
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.polySymbols.PolySymbol
|
||||
import com.intellij.polySymbols.documentation.impl.PolySymbolDocumentationTargetImpl
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
interface PolySymbolDocumentationTarget : DocumentationTarget {
|
||||
|
||||
@@ -20,8 +18,4 @@ interface PolySymbolDocumentationTarget : DocumentationTarget {
|
||||
.presentation()
|
||||
}
|
||||
|
||||
override fun computeDocumentation(): DocumentationResult? =
|
||||
symbol.createDocumentation(location)
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let { doc -> PolySymbolDocumentationTargetImpl.buildDocumentation(symbol.origin, doc) }
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.documentation
|
||||
|
||||
import com.intellij.model.Pointer
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
import com.intellij.polySymbols.PolySymbol
|
||||
import com.intellij.polySymbols.documentation.impl.PolySymbolDocumentationTargetImpl
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
interface PolySymbolWithDocumentation : PolySymbol {
|
||||
|
||||
/**
|
||||
* An optional text, which describes the symbol purpose and usage.
|
||||
* It is rendered in the documentation popup or view.
|
||||
*/
|
||||
val description: @Nls String?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* Additional sections, to be rendered in the symbols’ documentation.
|
||||
* Each section should have a name, but the contents can be empty.
|
||||
*/
|
||||
val descriptionSections: Map<@Nls String, @Nls String>
|
||||
get() = emptyMap()
|
||||
|
||||
/**
|
||||
* An optional URL to a website with detailed symbol's documentation
|
||||
*/
|
||||
val docUrl: @NlsSafe String?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* If the symbol represents some property, variable or anything that can hold a value,
|
||||
* this property documents what is the default value.
|
||||
*/
|
||||
val defaultValue: @NlsSafe String?
|
||||
get() = null
|
||||
|
||||
/**
|
||||
* Returns a default target implementation, which uses [createDocumentation] method to render the documentation.
|
||||
*/
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
PolySymbolDocumentationTargetImpl(this, location)
|
||||
|
||||
/**
|
||||
* Returns [PolySymbolDocumentation] - an interface holding information required to render documentation for the symbol.
|
||||
* By default, it's contents are build from the available Poly Symbol information.
|
||||
*
|
||||
* To customize symbols documentation, one can override the method, or implement [PolySymbolDocumentationCustomizer].
|
||||
*
|
||||
* [PolySymbolDocumentation] interface provides builder methods for customizing the documentation.
|
||||
* `with*` methods return a copy of the documentation with customized fields.
|
||||
*/
|
||||
fun createDocumentation(location: PsiElement?): PolySymbolDocumentation? =
|
||||
PolySymbolDocumentation.create(this, location)
|
||||
|
||||
override fun createPointer(): Pointer<out PolySymbolWithDocumentation>
|
||||
|
||||
}
|
||||
@@ -1,23 +1,37 @@
|
||||
package com.intellij.polySymbols.documentation.impl
|
||||
|
||||
import com.intellij.lang.documentation.DocumentationMarkup
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.platform.backend.documentation.DocumentationResult
|
||||
import com.intellij.polySymbols.PolySymbolApiStatus
|
||||
import com.intellij.polySymbols.PolySymbolOrigin
|
||||
import com.intellij.polySymbols.PolySymbolsBundle
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
import com.intellij.polySymbols.impl.scaleToHeight
|
||||
import com.intellij.ui.scale.ScaleContext
|
||||
import com.intellij.ui.scale.ScaleType
|
||||
import com.intellij.util.IconUtil
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.annotations.Nls
|
||||
import java.awt.Image
|
||||
import java.awt.image.BufferedImage
|
||||
import javax.swing.Icon
|
||||
|
||||
internal data class PolySymbolDocumentationImpl(override val name: String,
|
||||
override val definition: String,
|
||||
override val definitionDetails: String?,
|
||||
override val description: @Nls String?,
|
||||
override val docUrl: String?,
|
||||
override val apiStatus: PolySymbolApiStatus?,
|
||||
override val required: Boolean,
|
||||
override val defaultValue: String?,
|
||||
override val library: String?,
|
||||
override val icon: Icon?,
|
||||
override val descriptionSections: Map<@Nls String, @Nls String>,
|
||||
override val footnote: @Nls String?,
|
||||
override val header: @Nls String?) : PolySymbolDocumentation {
|
||||
internal data class PolySymbolDocumentationImpl(
|
||||
override val name: String,
|
||||
override val definition: String,
|
||||
override val definitionDetails: String?,
|
||||
override val description: @Nls String?,
|
||||
override val docUrl: String?,
|
||||
override val apiStatus: PolySymbolApiStatus?,
|
||||
override val required: Boolean,
|
||||
override val defaultValue: String?,
|
||||
override val library: String?,
|
||||
override val icon: Icon?,
|
||||
override val descriptionSections: Map<@Nls String, @Nls String>,
|
||||
override val footnote: @Nls String?,
|
||||
override val header: @Nls String?,
|
||||
) : PolySymbolDocumentation {
|
||||
override fun isNotEmpty(): Boolean =
|
||||
name != definition || description != null || docUrl != null || (apiStatus != null && apiStatus != PolySymbolApiStatus.Stable)
|
||||
|| required || defaultValue != null || library != null || descriptionSections.isNotEmpty() || footnote != null
|
||||
@@ -62,20 +76,172 @@ internal data class PolySymbolDocumentationImpl(override val name: String,
|
||||
override fun withHeader(header: @Nls String?): PolySymbolDocumentation =
|
||||
copy(header = header)
|
||||
|
||||
override fun with(name: String,
|
||||
definition: String,
|
||||
definitionDetails: String?,
|
||||
description: @Nls String?,
|
||||
docUrl: String?,
|
||||
apiStatus: PolySymbolApiStatus?,
|
||||
required: Boolean,
|
||||
defaultValue: String?,
|
||||
library: String?,
|
||||
icon: Icon?,
|
||||
additionalSections: Map<@Nls String, @Nls String>,
|
||||
footnote: @Nls String?): PolySymbolDocumentation =
|
||||
override fun with(
|
||||
name: String,
|
||||
definition: String,
|
||||
definitionDetails: String?,
|
||||
description: @Nls String?,
|
||||
docUrl: String?,
|
||||
apiStatus: PolySymbolApiStatus?,
|
||||
required: Boolean,
|
||||
defaultValue: String?,
|
||||
library: String?,
|
||||
icon: Icon?,
|
||||
additionalSections: Map<@Nls String, @Nls String>,
|
||||
footnote: @Nls String?,
|
||||
): PolySymbolDocumentation =
|
||||
copy(name = name, definition = definition, definitionDetails = definitionDetails, description = description,
|
||||
docUrl = docUrl, apiStatus = apiStatus, required = required, defaultValue = defaultValue, library = library, icon = icon,
|
||||
descriptionSections = descriptionSections + additionalSections, footnote = footnote)
|
||||
|
||||
override fun build(origin: PolySymbolOrigin): DocumentationResult {
|
||||
val url2ImageMap = mutableMapOf<String, Image>()
|
||||
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
val contents = StringBuilder()
|
||||
.appendHeader()
|
||||
.appendDefinition(url2ImageMap)
|
||||
.appendDescription()
|
||||
.appendSections()
|
||||
.appendFootnote()
|
||||
.toString()
|
||||
.loadLocalImages(origin, url2ImageMap)
|
||||
return DocumentationResult.documentation(contents).images(url2ImageMap).externalUrl(docUrl)
|
||||
.definitionDetails(definitionDetails)
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendDefinition(url2ImageMap: MutableMap<String, Image>): StringBuilder =
|
||||
append(DocumentationMarkup.DEFINITION_START)
|
||||
.also {
|
||||
icon?.let { appendIcon(it, url2ImageMap).append(" ") }
|
||||
}
|
||||
.append(definition)
|
||||
.append(DocumentationMarkup.DEFINITION_END)
|
||||
.append('\n')
|
||||
|
||||
private fun StringBuilder.appendDescription(): StringBuilder =
|
||||
description?.let {
|
||||
append(DocumentationMarkup.CONTENT_START).append('\n')
|
||||
.append(it).append('\n')
|
||||
.append(DocumentationMarkup.CONTENT_END)
|
||||
}
|
||||
?: this
|
||||
|
||||
private fun StringBuilder.appendSections(): StringBuilder =
|
||||
buildSections().let { sections ->
|
||||
if (sections.isNotEmpty()) {
|
||||
append(DocumentationMarkup.SECTIONS_START)
|
||||
.append('\n')
|
||||
sections.entries.forEach { (name, value) ->
|
||||
append(DocumentationMarkup.SECTION_HEADER_START)
|
||||
.append(StringUtil.capitalize(name))
|
||||
if (value.isNotBlank()) {
|
||||
if (!name.endsWith(":"))
|
||||
append(':')
|
||||
append(DocumentationMarkup.SECTION_SEPARATOR)
|
||||
.append(value)
|
||||
}
|
||||
append(DocumentationMarkup.SECTION_END)
|
||||
.append('\n')
|
||||
}
|
||||
append(DocumentationMarkup.SECTIONS_END)
|
||||
.append('\n')
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendFootnote(): StringBuilder =
|
||||
footnote?.let {
|
||||
append(DocumentationMarkup.CONTENT_START)
|
||||
.append(it)
|
||||
.append(DocumentationMarkup.CONTENT_END)
|
||||
.append('\n')
|
||||
} ?: this
|
||||
|
||||
private fun StringBuilder.appendHeader(): StringBuilder =
|
||||
header?.let {
|
||||
append("<div class='" + DocumentationMarkup.CLASS_TOP + "'>")
|
||||
.append(it)
|
||||
.append("</div>\n")
|
||||
} ?: this
|
||||
|
||||
private fun buildSections(): Map<String, String> =
|
||||
LinkedHashMap(descriptionSections).also { sections ->
|
||||
if (required) sections[PolySymbolsBundle.message("mdn.documentation.section.isRequired")] = ""
|
||||
apiStatus?.let { status ->
|
||||
when (status) {
|
||||
is PolySymbolApiStatus.Deprecated -> {
|
||||
sections[PolySymbolsBundle.message("mdn.documentation.section.status.Deprecated")] = status.message ?: ""
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.DeprecatedSince")] = it }
|
||||
}
|
||||
is PolySymbolApiStatus.Obsolete -> {
|
||||
sections[PolySymbolsBundle.message("mdn.documentation.section.status.Obsolete")] = status.message ?: ""
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.ObsoleteSince")] = it }
|
||||
}
|
||||
is PolySymbolApiStatus.Experimental -> {
|
||||
sections[PolySymbolsBundle.message("mdn.documentation.section.status.Experimental")] = status.message ?: ""
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.Since")] = it }
|
||||
}
|
||||
is PolySymbolApiStatus.Stable -> {
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.Since")] = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultValue?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.defaultValue")] = "<p><code>$it</code>" }
|
||||
library?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.library")] = "<p>$it" }
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendIcon(icon: Icon, url2ImageMap: MutableMap<String, Image>): StringBuilder {
|
||||
// TODO adjust it to the actual component being used
|
||||
@Suppress("UndesirableClassUsage")
|
||||
val bufferedImage = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
|
||||
val g = bufferedImage.createGraphics()
|
||||
g.font = UIUtil.getToolTipFont()
|
||||
val height = (g.fontMetrics.getStringBounds("a", g).height / ScaleContext.create().getScale(ScaleType.USR_SCALE)).toInt()
|
||||
g.dispose()
|
||||
val image = try {
|
||||
IconUtil.toBufferedImage(icon.scaleToHeight(height))
|
||||
}
|
||||
catch (e: Exception) {
|
||||
// ignore
|
||||
return this
|
||||
}
|
||||
val url = "https://img${url2ImageMap.size}"
|
||||
url2ImageMap[url] = image
|
||||
val screenHeight = height * ScaleContext.create().getScale(ScaleType.SYS_SCALE)
|
||||
append("<img src='$url' height=\"$screenHeight\" width=\"${(screenHeight * icon.iconWidth) / icon.iconHeight}\" border=0 />")
|
||||
return this
|
||||
}
|
||||
|
||||
private val imgSrcRegex = Regex("<img [^>]*src\\s*=\\s*['\"]([^'\"]+)['\"]")
|
||||
|
||||
private fun String.loadLocalImages(origin: PolySymbolOrigin, url2ImageMap: MutableMap<String, Image>): String {
|
||||
val replaces = imgSrcRegex.findAll(this)
|
||||
.mapNotNull { it.groups[1] }
|
||||
.filter { !it.value.contains(':') }
|
||||
.mapNotNull { group ->
|
||||
origin.loadIcon(group.value)
|
||||
?.let { IconUtil.toBufferedImage(it, true) }
|
||||
?.let {
|
||||
val url = "https://img${url2ImageMap.size}"
|
||||
url2ImageMap[url] = it
|
||||
Pair(group.range, url)
|
||||
}
|
||||
}
|
||||
.sortedBy { it.first.first }
|
||||
.toList()
|
||||
if (replaces.isEmpty()) return this
|
||||
val result = StringBuilder()
|
||||
var lastIndex = 0
|
||||
for (replace in replaces) {
|
||||
result.appendRange(this, lastIndex, replace.first.first)
|
||||
result.append(replace.second)
|
||||
lastIndex = replace.first.last + 1
|
||||
}
|
||||
if (lastIndex < this.length) {
|
||||
result.appendRange(this, lastIndex, this.length)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,13 +18,14 @@ import com.intellij.polySymbols.PolySymbolOrigin
|
||||
import com.intellij.polySymbols.PolySymbolsBundle
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentationTarget
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.impl.scaleToHeight
|
||||
import java.awt.Image
|
||||
import java.awt.image.BufferedImage
|
||||
import javax.swing.Icon
|
||||
|
||||
internal class PolySymbolDocumentationTargetImpl(
|
||||
override val symbol: PolySymbol,
|
||||
override val symbol: PolySymbolWithDocumentation,
|
||||
override val location: PsiElement?,
|
||||
)
|
||||
: PolySymbolDocumentationTarget {
|
||||
@@ -37,158 +38,9 @@ internal class PolySymbolDocumentationTargetImpl(
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun buildDocumentation(origin: PolySymbolOrigin, doc: PolySymbolDocumentation): DocumentationResult {
|
||||
val url2ImageMap = mutableMapOf<String, Image>()
|
||||
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
val contents = StringBuilder()
|
||||
.appendHeader(doc)
|
||||
.appendDefinition(doc, url2ImageMap)
|
||||
.appendDescription(doc)
|
||||
.appendSections(doc)
|
||||
.appendFootnote(doc)
|
||||
.toString()
|
||||
.loadLocalImages(origin, url2ImageMap)
|
||||
return DocumentationResult.documentation(contents).images(url2ImageMap).externalUrl(doc.docUrl)
|
||||
.definitionDetails(doc.definitionDetails)
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendDefinition(doc: PolySymbolDocumentation, url2ImageMap: MutableMap<String, Image>): StringBuilder =
|
||||
append(DocumentationMarkup.DEFINITION_START)
|
||||
.also {
|
||||
doc.icon?.let { appendIcon(it, url2ImageMap).append(" ") }
|
||||
}
|
||||
.append(doc.definition)
|
||||
.append(DocumentationMarkup.DEFINITION_END)
|
||||
.append('\n')
|
||||
|
||||
private fun StringBuilder.appendDescription(doc: PolySymbolDocumentation): StringBuilder =
|
||||
doc.description?.let {
|
||||
append(DocumentationMarkup.CONTENT_START).append('\n')
|
||||
.append(it).append('\n')
|
||||
.append(DocumentationMarkup.CONTENT_END)
|
||||
}
|
||||
?: this
|
||||
|
||||
private fun StringBuilder.appendSections(doc: PolySymbolDocumentation): StringBuilder =
|
||||
buildSections(doc).let { sections ->
|
||||
if (sections.isNotEmpty()) {
|
||||
append(DocumentationMarkup.SECTIONS_START)
|
||||
.append('\n')
|
||||
sections.entries.forEach { (name, value) ->
|
||||
append(DocumentationMarkup.SECTION_HEADER_START)
|
||||
.append(StringUtil.capitalize(name))
|
||||
if (value.isNotBlank()) {
|
||||
if (!name.endsWith(":"))
|
||||
append(':')
|
||||
append(DocumentationMarkup.SECTION_SEPARATOR)
|
||||
.append(value)
|
||||
}
|
||||
append(DocumentationMarkup.SECTION_END)
|
||||
.append('\n')
|
||||
}
|
||||
append(DocumentationMarkup.SECTIONS_END)
|
||||
.append('\n')
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendFootnote(doc: PolySymbolDocumentation): StringBuilder =
|
||||
doc.footnote?.let {
|
||||
append(DocumentationMarkup.CONTENT_START)
|
||||
.append(it)
|
||||
.append(DocumentationMarkup.CONTENT_END)
|
||||
.append('\n')
|
||||
} ?: this
|
||||
|
||||
private fun StringBuilder.appendHeader(doc: PolySymbolDocumentation): StringBuilder =
|
||||
doc.header?.let {
|
||||
append("<div class='" + DocumentationMarkup.CLASS_TOP + "'>")
|
||||
.append(it)
|
||||
.append("</div>\n")
|
||||
} ?: this
|
||||
|
||||
private fun buildSections(doc: PolySymbolDocumentation): Map<String, String> =
|
||||
LinkedHashMap(doc.descriptionSections).also { sections ->
|
||||
if (doc.required) sections[PolySymbolsBundle.message("mdn.documentation.section.isRequired")] = ""
|
||||
doc.apiStatus?.let { status ->
|
||||
when (status) {
|
||||
is PolySymbolApiStatus.Deprecated -> {
|
||||
sections[PolySymbolsBundle.message("mdn.documentation.section.status.Deprecated")] = status.message ?: ""
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.DeprecatedSince")] = it }
|
||||
}
|
||||
is PolySymbolApiStatus.Obsolete -> {
|
||||
sections[PolySymbolsBundle.message("mdn.documentation.section.status.Obsolete")] = status.message ?: ""
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.ObsoleteSince")] = it }
|
||||
}
|
||||
is PolySymbolApiStatus.Experimental -> {
|
||||
sections[PolySymbolsBundle.message("mdn.documentation.section.status.Experimental")] = status.message ?: ""
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.Since")] = it }
|
||||
}
|
||||
is PolySymbolApiStatus.Stable -> {
|
||||
status.since?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.status.Since")] = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
doc.defaultValue?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.defaultValue")] = "<p><code>$it</code>" }
|
||||
doc.library?.let { sections[PolySymbolsBundle.message("mdn.documentation.section.library")] = "<p>$it" }
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendIcon(icon: Icon, url2ImageMap: MutableMap<String, Image>): StringBuilder {
|
||||
// TODO adjust it to the actual component being used
|
||||
@Suppress("UndesirableClassUsage")
|
||||
val bufferedImage = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
|
||||
val g = bufferedImage.createGraphics()
|
||||
g.font = UIUtil.getToolTipFont()
|
||||
val height = (g.fontMetrics.getStringBounds("a", g).height / ScaleContext.create().getScale(ScaleType.USR_SCALE)).toInt()
|
||||
g.dispose()
|
||||
val image = try {
|
||||
IconUtil.toBufferedImage(icon.scaleToHeight(height))
|
||||
}
|
||||
catch (e: Exception) {
|
||||
// ignore
|
||||
return this
|
||||
}
|
||||
val url = "https://img${url2ImageMap.size}"
|
||||
url2ImageMap[url] = image
|
||||
val screenHeight = height * ScaleContext.create().getScale(ScaleType.SYS_SCALE)
|
||||
append("<img src='$url' height=\"$screenHeight\" width=\"${(screenHeight * icon.iconWidth) / icon.iconHeight}\" border=0 />")
|
||||
return this
|
||||
}
|
||||
|
||||
private val imgSrcRegex = Regex("<img [^>]*src\\s*=\\s*['\"]([^'\"]+)['\"]")
|
||||
|
||||
private fun String.loadLocalImages(origin: PolySymbolOrigin, url2ImageMap: MutableMap<String, Image>): String {
|
||||
val replaces = imgSrcRegex.findAll(this)
|
||||
.mapNotNull { it.groups[1] }
|
||||
.filter { !it.value.contains(':') }
|
||||
.mapNotNull { group ->
|
||||
origin.loadIcon(group.value)
|
||||
?.let { IconUtil.toBufferedImage(it, true) }
|
||||
?.let {
|
||||
val url = "https://img${url2ImageMap.size}"
|
||||
url2ImageMap[url] = it
|
||||
Pair(group.range, url)
|
||||
}
|
||||
}
|
||||
.sortedBy { it.first.first }
|
||||
.toList()
|
||||
if (replaces.isEmpty()) return this
|
||||
val result = StringBuilder()
|
||||
var lastIndex = 0
|
||||
for (replace in replaces) {
|
||||
result.appendRange(this, lastIndex, replace.first.first)
|
||||
result.append(replace.second)
|
||||
lastIndex = replace.first.last + 1
|
||||
}
|
||||
if (lastIndex < this.length) {
|
||||
result.appendRange(this, lastIndex, this.length)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
}
|
||||
override fun computeDocumentation(): DocumentationResult? =
|
||||
symbol.createDocumentation(location)
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.build(symbol.origin)
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package com.intellij.polySymbols.query
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.polySymbols.*
|
||||
import com.intellij.polySymbols.query.impl.PolySymbolMatchImpl
|
||||
import com.intellij.polySymbols.query.impl.PolySymbolMatchBase
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
@@ -26,7 +26,7 @@ interface PolySymbolMatch : CompositePolySymbol {
|
||||
explicitPriority: PolySymbol.Priority? = null,
|
||||
explicitProximity: Int? = null,
|
||||
): PolySymbolMatch =
|
||||
PolySymbolMatchImpl.BuilderImpl(matchedName, qualifiedKind, origin)
|
||||
PolySymbolMatchBase.BuilderImpl(matchedName, qualifiedKind, origin)
|
||||
.also { builder ->
|
||||
builder.addNameSegments(nameSegments)
|
||||
explicitProximity?.let { builder.explicitProximity(it) }
|
||||
@@ -41,7 +41,7 @@ interface PolySymbolMatch : CompositePolySymbol {
|
||||
origin: PolySymbolOrigin,
|
||||
builder: (PolySymbolMatchBuilder.() -> Unit),
|
||||
): PolySymbolMatch =
|
||||
PolySymbolMatchImpl.BuilderImpl(matchedName, qualifiedKind, origin)
|
||||
PolySymbolMatchBase.BuilderImpl(matchedName, qualifiedKind, origin)
|
||||
.also { builder.invoke(it) }
|
||||
.build()
|
||||
|
||||
@@ -52,7 +52,7 @@ interface PolySymbolMatch : CompositePolySymbol {
|
||||
origin: PolySymbolOrigin,
|
||||
vararg nameSegments: PolySymbolNameSegment,
|
||||
): PolySymbolMatch =
|
||||
PolySymbolMatchImpl.BuilderImpl(matchedName, qualifiedKind, origin)
|
||||
PolySymbolMatchBase.BuilderImpl(matchedName, qualifiedKind, origin)
|
||||
.also { it.addNameSegments(*nameSegments) }
|
||||
.build()
|
||||
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.query.impl
|
||||
|
||||
import com.intellij.model.Pointer
|
||||
import com.intellij.model.Symbol
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
import com.intellij.platform.backend.navigation.NavigationTarget
|
||||
import com.intellij.polySymbols.*
|
||||
import com.intellij.polySymbols.PolySymbol.Priority
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.documentation.impl.PolySymbolDocumentationTargetImpl
|
||||
import com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
import com.intellij.polySymbols.query.PolySymbolMatch
|
||||
import com.intellij.polySymbols.query.PolySymbolMatchBuilder
|
||||
import com.intellij.polySymbols.refactoring.PolySymbolRenameTarget
|
||||
import com.intellij.polySymbols.search.PolySymbolSearchTarget
|
||||
import com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
import com.intellij.polySymbols.utils.coalesceApiStatus
|
||||
import com.intellij.polySymbols.utils.merge
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.createSmartPointer
|
||||
import javax.swing.Icon
|
||||
|
||||
internal open class PolySymbolMatchBase internal constructor(
|
||||
override val matchedName: String,
|
||||
override val nameSegments: List<PolySymbolNameSegment>,
|
||||
override val qualifiedKind: PolySymbolQualifiedKind,
|
||||
override val origin: PolySymbolOrigin,
|
||||
override val explicitPriority: Priority?,
|
||||
override val explicitProximity: Int?,
|
||||
override val additionalProperties: Map<String, Any>,
|
||||
) : PolySymbolMatchMixin {
|
||||
|
||||
init {
|
||||
require(nameSegments.isNotEmpty()) { "nameSegments must not be empty" }
|
||||
}
|
||||
|
||||
internal fun withSegments(segments: List<PolySymbolNameSegment>): PolySymbolMatch =
|
||||
create(matchedName, segments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties)
|
||||
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is PolySymbolMatch
|
||||
&& other.name == name
|
||||
&& other.origin == origin
|
||||
&& other.qualifiedKind == qualifiedKind
|
||||
&& other.nameSegments.equalsIgnoreOffset(nameSegments)
|
||||
|
||||
override fun hashCode(): Int = name.hashCode()
|
||||
|
||||
override fun createPointer(): Pointer<out PolySymbolMatchBase> =
|
||||
PolySymbolMatchPointer<PolySymbolMatchBase>(this, ::PolySymbolMatchBase)
|
||||
|
||||
class BuilderImpl(
|
||||
private var matchedName: String,
|
||||
private var qualifiedKind: PolySymbolQualifiedKind,
|
||||
private var origin: PolySymbolOrigin,
|
||||
) : PolySymbolMatchBuilder {
|
||||
|
||||
private var nameSegments = mutableListOf<PolySymbolNameSegment>()
|
||||
private var properties = mutableMapOf<String, Any>()
|
||||
private var explicitPriority: Priority? = null
|
||||
private var explicitProximity: Int? = null
|
||||
|
||||
fun build(): PolySymbolMatch =
|
||||
create(matchedName, nameSegments, qualifiedKind,
|
||||
origin, explicitPriority, explicitProximity, properties)
|
||||
|
||||
override fun addNameSegments(value: List<PolySymbolNameSegment>): PolySymbolMatchBuilder = this.also {
|
||||
nameSegments.addAll(value)
|
||||
}
|
||||
|
||||
override fun addNameSegments(vararg value: PolySymbolNameSegment): PolySymbolMatchBuilder = this.also {
|
||||
nameSegments.addAll(value)
|
||||
}
|
||||
|
||||
override fun addNameSegment(value: PolySymbolNameSegment): PolySymbolMatchBuilder = this.also {
|
||||
nameSegments.add(value)
|
||||
}
|
||||
|
||||
override fun explicitPriority(value: Priority): PolySymbolMatchBuilder = this.also {
|
||||
explicitPriority = value
|
||||
}
|
||||
|
||||
override fun explicitProximity(value: Int): PolySymbolMatchBuilder = this.also {
|
||||
explicitProximity = value
|
||||
}
|
||||
|
||||
override fun setProperty(name: String, value: Any): PolySymbolMatchBuilder = this.also {
|
||||
properties[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class PsiSourcedPolySymbolMatch(
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
) : PolySymbolMatchBase(matchedName, nameSegments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties),
|
||||
PsiSourcedPolySymbolMatchMixin {
|
||||
|
||||
override fun createPointer(): Pointer<PsiSourcedPolySymbolMatch> =
|
||||
PolySymbolMatchPointer<PsiSourcedPolySymbolMatch>(this, ::PsiSourcedPolySymbolMatch)
|
||||
|
||||
}
|
||||
|
||||
private class PsiSourcedPolySymbolMatchWithDocumentation(
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
) : PolySymbolMatchBase(matchedName, nameSegments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties),
|
||||
PsiSourcedPolySymbolMatchMixin, PolySymbolMatchWithDocumentationMixin {
|
||||
|
||||
override fun createPointer(): Pointer<PsiSourcedPolySymbolMatchWithDocumentation> =
|
||||
PolySymbolMatchPointer<PsiSourcedPolySymbolMatchWithDocumentation>(this, ::PsiSourcedPolySymbolMatchWithDocumentation)
|
||||
|
||||
}
|
||||
|
||||
private class PolySymbolMatchWithDocumentation(
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
) : PolySymbolMatchBase(matchedName, nameSegments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties),
|
||||
PolySymbolMatchWithDocumentationMixin {
|
||||
|
||||
override fun createPointer(): Pointer<PolySymbolMatchWithDocumentation> =
|
||||
PolySymbolMatchPointer<PolySymbolMatchWithDocumentation>(this, ::PolySymbolMatchWithDocumentation)
|
||||
|
||||
}
|
||||
|
||||
private fun create(
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
): PolySymbolMatch {
|
||||
val psiSourcedMixin = nameSegments.all { it.start == it.end || (it.symbols.isNotEmpty() && it.symbols.any { symbol -> symbol is PsiSourcedPolySymbol }) }
|
||||
val withDocumentationMixin = nameSegments.any { it.symbols.any { symbol -> symbol is PolySymbolWithDocumentation } }
|
||||
|
||||
return if (psiSourcedMixin) {
|
||||
if (withDocumentationMixin) {
|
||||
PsiSourcedPolySymbolMatchWithDocumentation(matchedName, nameSegments, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, additionalProperties)
|
||||
}
|
||||
else {
|
||||
PsiSourcedPolySymbolMatch(matchedName, nameSegments, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, additionalProperties)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (withDocumentationMixin) {
|
||||
PolySymbolMatchWithDocumentation(matchedName, nameSegments, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, additionalProperties)
|
||||
}
|
||||
else {
|
||||
PolySymbolMatchBase(matchedName, nameSegments, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, additionalProperties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface PolySymbolMatchMixin : PolySymbolMatch {
|
||||
|
||||
val explicitPriority: Priority?
|
||||
val explicitProximity: Int?
|
||||
val additionalProperties: Map<String, Any>
|
||||
|
||||
fun reversedSegments() = Sequence { ReverseListIterator(nameSegments) }
|
||||
|
||||
override fun withCustomProperties(properties: Map<String, Any>): PolySymbolMatch =
|
||||
create(matchedName, nameSegments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties + properties)
|
||||
|
||||
override val psiContext: PsiElement?
|
||||
get() = reversedSegments().flatMap { it.symbols.asSequence() }
|
||||
.mapNotNull { it.psiContext }.firstOrNull()
|
||||
|
||||
|
||||
override val name: String
|
||||
get() = matchedName.substring(nameSegments.firstOrNull()?.start ?: 0,
|
||||
nameSegments.lastOrNull()?.end ?: 0)
|
||||
|
||||
override val virtual: Boolean
|
||||
get() = nameSegments.any { segment -> segment.symbols.any { it.virtual } }
|
||||
|
||||
override val extension: Boolean
|
||||
get() = nameSegments.isNotEmpty() && nameSegments.all { segment -> segment.symbols.isNotEmpty() && segment.symbols.all { it.extension } }
|
||||
|
||||
override val priority: Priority?
|
||||
get() = explicitPriority ?: reversedSegments().mapNotNull { it.priority }.firstOrNull()
|
||||
|
||||
override val proximity: Int?
|
||||
get() = explicitProximity ?: reversedSegments().mapNotNull { it.proximity }.firstOrNull()
|
||||
|
||||
override val queryScope: List<PolySymbolsScope>
|
||||
get() = nameSegments.asSequence()
|
||||
.flatMap { it.symbols }
|
||||
.flatMap { it.queryScope }
|
||||
.toList()
|
||||
|
||||
override val type: Any?
|
||||
get() = reversedSegments().flatMap { it.symbols }
|
||||
.mapNotNull { it.type }.firstOrNull()
|
||||
|
||||
override val attributeValue: PolySymbolHtmlAttributeValue?
|
||||
get() = reversedSegments().flatMap { it.symbols }.mapNotNull { it.attributeValue }.merge()
|
||||
|
||||
override val required: Boolean?
|
||||
get() = reversedSegments().flatMap { it.symbols }.mapNotNull { it.required }.firstOrNull()
|
||||
|
||||
override val apiStatus: PolySymbolApiStatus
|
||||
get() = coalesceApiStatus(reversedSegments().flatMap { it.symbols }) { it.apiStatus }
|
||||
|
||||
override val icon: Icon?
|
||||
get() = reversedSegments().flatMap { it.symbols }.mapNotNull { it.icon }.firstOrNull()
|
||||
|
||||
override val properties: Map<String, Any>
|
||||
get() = nameSegments.asSequence().flatMap { it.symbols }
|
||||
.flatMap { it.properties.entries }
|
||||
.filter { it.key != PolySymbol.PROP_HIDE_FROM_COMPLETION }
|
||||
.plus(additionalProperties.entries)
|
||||
.map { Pair(it.key, it.value) }
|
||||
.toMap()
|
||||
|
||||
override fun getNavigationTargets(project: Project): Collection<NavigationTarget> =
|
||||
if (nameSegments.size == 1)
|
||||
nameSegments[0].symbols.asSequence()
|
||||
.flatMap { it.getNavigationTargets(project) }
|
||||
.distinct()
|
||||
.toList()
|
||||
else emptyList()
|
||||
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
reversedSegments()
|
||||
.flatMap { it.symbols.asSequence() }
|
||||
.map {
|
||||
if (it === this) null
|
||||
else it.getDocumentationTarget(location)
|
||||
}
|
||||
.filter { it !is PolySymbolDocumentationTargetImpl || it.symbol.createDocumentation(location)?.isNotEmpty() == true }
|
||||
.firstOrNull()
|
||||
?: if (this is PolySymbolWithDocumentation) PolySymbolDocumentationTargetImpl(this, location) else null
|
||||
|
||||
override fun isEquivalentTo(symbol: Symbol): Boolean =
|
||||
super<PolySymbolMatch>.isEquivalentTo(symbol)
|
||||
|| nameSegments.filter { it.start != it.end }
|
||||
.let { nonEmptySegments ->
|
||||
nonEmptySegments.size == 1
|
||||
&& nonEmptySegments[0].symbols.any { it.isEquivalentTo(symbol) }
|
||||
}
|
||||
|
||||
override val searchTarget: PolySymbolSearchTarget?
|
||||
get() = nameSegments.filter { it.start != it.end }
|
||||
.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.symbols
|
||||
?.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.searchTarget
|
||||
|
||||
override val renameTarget: PolySymbolRenameTarget?
|
||||
get() = nameSegments.filter { it.start != it.end }
|
||||
.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.symbols
|
||||
?.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.renameTarget
|
||||
|
||||
}
|
||||
|
||||
|
||||
private interface PolySymbolMatchWithDocumentationMixin : PolySymbolMatchMixin, PolySymbolWithDocumentation {
|
||||
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
super<PolySymbolMatchMixin>.getDocumentationTarget(location)
|
||||
|
||||
override fun createDocumentation(location: PsiElement?): PolySymbolDocumentation? =
|
||||
reversedSegments().flatMap { it.symbols.asSequence() }
|
||||
.firstNotNullOfOrNull { (it as? PolySymbolWithDocumentation)?.createDocumentation(location) }
|
||||
|
||||
override val description: String?
|
||||
get() = nameSegments.takeIf { it.size == 1 }
|
||||
?.get(0)?.symbols?.asSequence()?.mapNotNull { (it as? PolySymbolWithDocumentation)?.description }?.firstOrNull()
|
||||
|
||||
override val docUrl: String?
|
||||
get() = nameSegments.takeIf { it.size == 1 }
|
||||
?.get(0)?.symbols?.asSequence()?.mapNotNull { (it as? PolySymbolWithDocumentation)?.docUrl }?.firstOrNull()
|
||||
|
||||
override val descriptionSections: Map<String, String>
|
||||
get() = nameSegments.takeIf { it.size == 1 }
|
||||
?.get(0)?.symbols?.asSequence()
|
||||
?.flatMap { (it as? PolySymbolWithDocumentation)?.descriptionSections?.asSequence() ?: emptySequence() }
|
||||
?.distinct()
|
||||
?.associateBy({ it.key }, { it.value })
|
||||
?: emptyMap()
|
||||
|
||||
}
|
||||
|
||||
private interface PsiSourcedPolySymbolMatchMixin : PolySymbolMatchMixin, PsiSourcedPolySymbol {
|
||||
|
||||
override val psiContext: PsiElement?
|
||||
get() = reversedSegments().flatMap { it.symbols.asSequence() }
|
||||
.mapNotNull { it.psiContext }.firstOrNull()
|
||||
|
||||
override val source: PsiElement?
|
||||
get() = reversedSegments().flatMap { it.symbols }
|
||||
.mapNotNull { (it as? PsiSourcedPolySymbol)?.source }.singleOrNull()
|
||||
|
||||
override fun getNavigationTargets(project: Project): Collection<NavigationTarget> =
|
||||
super<PolySymbolMatchMixin>.getNavigationTargets(project)
|
||||
|
||||
override fun isEquivalentTo(symbol: Symbol): Boolean =
|
||||
super<PolySymbolMatchMixin>.isEquivalentTo(symbol)
|
||||
|
||||
}
|
||||
|
||||
private fun List<PolySymbolNameSegment>.equalsIgnoreOffset(other: List<PolySymbolNameSegment>): Boolean {
|
||||
if (size != other.size) return false
|
||||
if (this.isEmpty()) return true
|
||||
val startOffset1 = this[0].start
|
||||
val startOffset2 = other[0].start
|
||||
|
||||
for (i in indices) {
|
||||
val segment1 = this[i]
|
||||
val segment2 = other[i]
|
||||
if (segment1.start - startOffset1 != segment2.start - startOffset2
|
||||
|| segment1.end - startOffset1 != segment2.end - startOffset2
|
||||
|| segment1.apiStatus != segment2.apiStatus
|
||||
|| segment1.symbols != segment2.symbols
|
||||
|| segment1.problem != segment2.problem
|
||||
|| segment1.displayName != segment2.displayName
|
||||
|| segment1.priority != segment2.priority
|
||||
|| segment1.proximity != segment2.proximity) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private class ReverseListIterator<T>(list: List<T>) : Iterator<T> {
|
||||
|
||||
private val iterator = list.listIterator(list.size)
|
||||
|
||||
override operator fun hasNext(): Boolean {
|
||||
return iterator.hasPrevious()
|
||||
}
|
||||
|
||||
override operator fun next(): T {
|
||||
return iterator.previous()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class PolySymbolMatchPointer<T : PolySymbolMatch>(
|
||||
polySymbolMatch: PolySymbolMatchBase,
|
||||
private val newInstanceProvider: (
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
) -> T,
|
||||
) : Pointer<T> {
|
||||
|
||||
private val matchedName = polySymbolMatch.matchedName
|
||||
private val nameSegments = polySymbolMatch.nameSegments
|
||||
.map { it.createPointer() }
|
||||
private val qualifiedKind = polySymbolMatch.qualifiedKind
|
||||
private val origin = polySymbolMatch.origin
|
||||
private val explicitPriority = polySymbolMatch.explicitPriority
|
||||
private val explicitProximity = polySymbolMatch.explicitProximity
|
||||
private val additionalProperties = polySymbolMatch.additionalProperties
|
||||
.createPointers()
|
||||
|
||||
override fun dereference(): T? =
|
||||
nameSegments.map { it.dereference() }
|
||||
.takeIf { it.all { segment -> segment != null } }
|
||||
?.let {
|
||||
val dereferencingProblems = Ref(false)
|
||||
val dereferencedProperties = additionalProperties.dereferencePointers(dereferencingProblems)
|
||||
if (dereferencingProblems.get()) return null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
newInstanceProvider(matchedName, it as List<PolySymbolNameSegment>, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, dereferencedProperties)
|
||||
}
|
||||
|
||||
private fun Map<String, Any>.createPointers(): Map<String, Any> =
|
||||
mapValues { (_, value) -> value.createPointers() }
|
||||
|
||||
private fun Any.createPointers(): Any =
|
||||
when (this) {
|
||||
is Symbol -> createPointer()
|
||||
is PsiElement -> createSmartPointer()
|
||||
is Map<*, *> -> mapValues { (_, value) -> value?.createPointers() }
|
||||
is List<*> -> map { it?.createPointers() }
|
||||
is Set<*> -> mapTo(HashSet()) { it?.createPointers() }
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun Map<String, Any>.dereferencePointers(anyProblems: Ref<Boolean>): Map<String, Any> =
|
||||
mapValues { (_, value) -> value.dereferencePointers(anyProblems) }
|
||||
|
||||
private fun Any.dereferencePointers(anyProblems: Ref<Boolean>): Any =
|
||||
when (this) {
|
||||
is Pointer<*> -> dereference().also { if (it == null) anyProblems.set(true) } ?: this
|
||||
is Map<*, *> -> mapValues { (_, value) -> value?.dereferencePointers(anyProblems) }
|
||||
is List<*> -> map { it?.dereferencePointers(anyProblems) }
|
||||
is Set<*> -> mapTo(HashSet()) { it?.dereferencePointers(anyProblems) }
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.query.impl
|
||||
|
||||
import com.intellij.model.Pointer
|
||||
import com.intellij.model.Symbol
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
import com.intellij.platform.backend.navigation.NavigationTarget
|
||||
import com.intellij.polySymbols.*
|
||||
import com.intellij.polySymbols.PolySymbol.Priority
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentationTarget
|
||||
import com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
import com.intellij.polySymbols.query.PolySymbolMatch
|
||||
import com.intellij.polySymbols.query.PolySymbolMatchBuilder
|
||||
import com.intellij.polySymbols.refactoring.PolySymbolRenameTarget
|
||||
import com.intellij.polySymbols.search.PolySymbolSearchTarget
|
||||
import com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
import com.intellij.polySymbols.utils.coalesceApiStatus
|
||||
import com.intellij.polySymbols.utils.merge
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.createSmartPointer
|
||||
import javax.swing.Icon
|
||||
|
||||
internal open class PolySymbolMatchImpl private constructor(
|
||||
override val matchedName: String,
|
||||
override val nameSegments: List<PolySymbolNameSegment>,
|
||||
override val qualifiedKind: PolySymbolQualifiedKind,
|
||||
override val origin: PolySymbolOrigin,
|
||||
private val explicitPriority: Priority?,
|
||||
private val explicitProximity: Int?,
|
||||
private val additionalProperties: Map<String, Any>,
|
||||
) : PolySymbolMatch {
|
||||
|
||||
init {
|
||||
require(nameSegments.isNotEmpty()) { "nameSegments must not be empty" }
|
||||
}
|
||||
|
||||
protected fun reversedSegments() = Sequence { ReverseListIterator(nameSegments) }
|
||||
|
||||
override fun withCustomProperties(properties: Map<String, Any>): PolySymbolMatch =
|
||||
create(matchedName, nameSegments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties + properties)
|
||||
|
||||
override val psiContext: PsiElement?
|
||||
get() = reversedSegments().flatMap { it.symbols.asSequence() }
|
||||
.mapNotNull { it.psiContext }.firstOrNull()
|
||||
|
||||
override fun createDocumentation(location: PsiElement?): PolySymbolDocumentation? =
|
||||
reversedSegments().flatMap { it.symbols.asSequence() }
|
||||
.firstNotNullOfOrNull { it.createDocumentation(location) }
|
||||
|
||||
override val name: String
|
||||
get() = matchedName.substring(nameSegments.firstOrNull()?.start ?: 0,
|
||||
nameSegments.lastOrNull()?.end ?: 0)
|
||||
|
||||
override val description: String?
|
||||
get() = nameSegments.takeIf { it.size == 1 }
|
||||
?.get(0)?.symbols?.asSequence()?.map { it.description }?.firstOrNull()
|
||||
|
||||
override val docUrl: String?
|
||||
get() = nameSegments.takeIf { it.size == 1 }
|
||||
?.get(0)?.symbols?.asSequence()?.map { it.docUrl }?.firstOrNull()
|
||||
|
||||
override val descriptionSections: Map<String, String>
|
||||
get() = nameSegments.takeIf { it.size == 1 }
|
||||
?.get(0)?.symbols?.asSequence()
|
||||
?.flatMap { it.descriptionSections.asSequence() }
|
||||
?.distinct()
|
||||
?.associateBy({ it.key }, { it.value })
|
||||
?: emptyMap()
|
||||
|
||||
override val virtual: Boolean
|
||||
get() = nameSegments.any { segment -> segment.symbols.any { it.virtual } }
|
||||
|
||||
override val extension: Boolean
|
||||
get() = nameSegments.isNotEmpty() && nameSegments.all { segment -> segment.symbols.isNotEmpty() && segment.symbols.all { it.extension } }
|
||||
|
||||
override val priority: Priority?
|
||||
get() = explicitPriority ?: reversedSegments().mapNotNull { it.priority }.firstOrNull()
|
||||
|
||||
override val proximity: Int?
|
||||
get() = explicitProximity ?: reversedSegments().mapNotNull { it.proximity }.firstOrNull()
|
||||
|
||||
override val queryScope: List<PolySymbolsScope>
|
||||
get() = nameSegments.asSequence()
|
||||
.flatMap { it.symbols }
|
||||
.flatMap { it.queryScope }
|
||||
.toList()
|
||||
|
||||
override val type: Any?
|
||||
get() = reversedSegments().flatMap { it.symbols }
|
||||
.mapNotNull { it.type }.firstOrNull()
|
||||
|
||||
override val attributeValue: PolySymbolHtmlAttributeValue?
|
||||
get() = reversedSegments().flatMap { it.symbols }.mapNotNull { it.attributeValue }.merge()
|
||||
|
||||
override val required: Boolean?
|
||||
get() = reversedSegments().flatMap { it.symbols }.mapNotNull { it.required }.firstOrNull()
|
||||
|
||||
override val apiStatus: PolySymbolApiStatus
|
||||
get() = coalesceApiStatus(reversedSegments().flatMap { it.symbols }) { it.apiStatus }
|
||||
|
||||
override val icon: Icon?
|
||||
get() = reversedSegments().flatMap { it.symbols }.mapNotNull { it.icon }.firstOrNull()
|
||||
|
||||
override val properties: Map<String, Any>
|
||||
get() = nameSegments.asSequence().flatMap { it.symbols }
|
||||
.flatMap { it.properties.entries }
|
||||
.filter { it.key != PolySymbol.PROP_HIDE_FROM_COMPLETION }
|
||||
.plus(additionalProperties.entries)
|
||||
.map { Pair(it.key, it.value) }
|
||||
.toMap()
|
||||
|
||||
override fun createPointer(): Pointer<out PolySymbolMatchImpl> =
|
||||
PolySymbolMatchPointer<PolySymbolMatchImpl>(this, ::PolySymbolMatchImpl)
|
||||
|
||||
override fun getNavigationTargets(project: Project): Collection<NavigationTarget> =
|
||||
if (nameSegments.size == 1)
|
||||
nameSegments[0].symbols.asSequence()
|
||||
.flatMap { it.getNavigationTargets(project) }
|
||||
.distinct()
|
||||
.toList()
|
||||
else emptyList()
|
||||
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
reversedSegments()
|
||||
.flatMap { it.symbols.asSequence() }
|
||||
.map {
|
||||
if (it === this) super<PolySymbolMatch>.getDocumentationTarget(location)
|
||||
else it.getDocumentationTarget(location)
|
||||
}
|
||||
.filter { it !is PolySymbolDocumentationTarget || it.symbol.createDocumentation(location)?.isNotEmpty() == true }
|
||||
.firstOrNull()
|
||||
?: super<PolySymbolMatch>.getDocumentationTarget(location)
|
||||
|
||||
override fun isEquivalentTo(symbol: Symbol): Boolean =
|
||||
super<PolySymbolMatch>.isEquivalentTo(symbol)
|
||||
|| nameSegments.filter { it.start != it.end }
|
||||
.let { nonEmptySegments ->
|
||||
nonEmptySegments.size == 1
|
||||
&& nonEmptySegments[0].symbols.any { it.isEquivalentTo(symbol) }
|
||||
}
|
||||
|
||||
override val searchTarget: PolySymbolSearchTarget?
|
||||
get() = nameSegments.filter { it.start != it.end }
|
||||
.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.symbols
|
||||
?.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.searchTarget
|
||||
|
||||
override val renameTarget: PolySymbolRenameTarget?
|
||||
get() = nameSegments.filter { it.start != it.end }
|
||||
.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.symbols
|
||||
?.takeIf { it.size == 1 }
|
||||
?.get(0)
|
||||
?.renameTarget
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is PolySymbolMatch
|
||||
&& other.name == name
|
||||
&& other.origin == origin
|
||||
&& other.qualifiedKind == qualifiedKind
|
||||
&& other.nameSegments.equalsIgnoreOffset(nameSegments)
|
||||
|
||||
override fun hashCode(): Int = name.hashCode()
|
||||
|
||||
internal fun withSegments(segments: List<PolySymbolNameSegment>): PolySymbolMatch =
|
||||
create(matchedName, segments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties)
|
||||
|
||||
class ReverseListIterator<T>(list: List<T>) : Iterator<T> {
|
||||
|
||||
private val iterator = list.listIterator(list.size)
|
||||
|
||||
override operator fun hasNext(): Boolean {
|
||||
return iterator.hasPrevious()
|
||||
}
|
||||
|
||||
override operator fun next(): T {
|
||||
return iterator.previous()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun create(
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
): PolySymbolMatch =
|
||||
if (nameSegments.all { it.start == it.end || (it.symbols.isNotEmpty() && it.symbols.any { symbol -> symbol is PsiSourcedPolySymbol }) })
|
||||
PsiSourcedPolySymbolMatch(matchedName, nameSegments, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, additionalProperties)
|
||||
else PolySymbolMatchImpl(matchedName, nameSegments, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, additionalProperties)
|
||||
|
||||
private fun List<PolySymbolNameSegment>.equalsIgnoreOffset(other: List<PolySymbolNameSegment>): Boolean {
|
||||
if (size != other.size) return false
|
||||
if (this.isEmpty()) return true
|
||||
val startOffset1 = this[0].start
|
||||
val startOffset2 = other[0].start
|
||||
|
||||
for (i in indices) {
|
||||
val segment1 = this[i]
|
||||
val segment2 = other[i]
|
||||
if (segment1.start - startOffset1 != segment2.start - startOffset2
|
||||
|| segment1.end - startOffset1 != segment2.end - startOffset2
|
||||
|| segment1.apiStatus != segment2.apiStatus
|
||||
|| segment1.symbols != segment2.symbols
|
||||
|| segment1.problem != segment2.problem
|
||||
|| segment1.displayName != segment2.displayName
|
||||
|| segment1.priority != segment2.priority
|
||||
|| segment1.proximity != segment2.proximity) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class PsiSourcedPolySymbolMatch(
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
) : PolySymbolMatchImpl(matchedName, nameSegments, qualifiedKind, origin, explicitPriority, explicitProximity, additionalProperties),
|
||||
PsiSourcedPolySymbol {
|
||||
|
||||
override val psiContext: PsiElement?
|
||||
get() = reversedSegments().flatMap { it.symbols.asSequence() }
|
||||
.mapNotNull { it.psiContext }.firstOrNull()
|
||||
|
||||
override val source: PsiElement?
|
||||
get() = reversedSegments().flatMap { it.symbols }
|
||||
.mapNotNull { (it as? PsiSourcedPolySymbol)?.source }.singleOrNull()
|
||||
|
||||
override fun createPointer(): Pointer<PsiSourcedPolySymbolMatch> =
|
||||
PolySymbolMatchPointer<PsiSourcedPolySymbolMatch>(this, ::PsiSourcedPolySymbolMatch)
|
||||
|
||||
override fun getNavigationTargets(project: Project): Collection<NavigationTarget> =
|
||||
super<PolySymbolMatchImpl>.getNavigationTargets(project)
|
||||
|
||||
override fun isEquivalentTo(symbol: Symbol): Boolean =
|
||||
super<PolySymbolMatchImpl>.isEquivalentTo(symbol)
|
||||
|
||||
}
|
||||
|
||||
class BuilderImpl(
|
||||
private var matchedName: String,
|
||||
private var qualifiedKind: PolySymbolQualifiedKind,
|
||||
private var origin: PolySymbolOrigin,
|
||||
) : PolySymbolMatchBuilder {
|
||||
|
||||
private var nameSegments = mutableListOf<PolySymbolNameSegment>()
|
||||
private var properties = mutableMapOf<String, Any>()
|
||||
private var explicitPriority: Priority? = null
|
||||
private var explicitProximity: Int? = null
|
||||
|
||||
fun build(): PolySymbolMatch =
|
||||
create(matchedName, nameSegments, qualifiedKind,
|
||||
origin, explicitPriority, explicitProximity, properties)
|
||||
|
||||
override fun addNameSegments(value: List<PolySymbolNameSegment>): PolySymbolMatchBuilder = this.also {
|
||||
nameSegments.addAll(value)
|
||||
}
|
||||
|
||||
override fun addNameSegments(vararg value: PolySymbolNameSegment): PolySymbolMatchBuilder = this.also {
|
||||
nameSegments.addAll(value)
|
||||
}
|
||||
|
||||
override fun addNameSegment(value: PolySymbolNameSegment): PolySymbolMatchBuilder = this.also {
|
||||
nameSegments.add(value)
|
||||
}
|
||||
|
||||
override fun explicitPriority(value: Priority): PolySymbolMatchBuilder = this.also {
|
||||
explicitPriority = value
|
||||
}
|
||||
|
||||
override fun explicitProximity(value: Int): PolySymbolMatchBuilder = this.also {
|
||||
explicitProximity = value
|
||||
}
|
||||
|
||||
override fun setProperty(name: String, value: Any): PolySymbolMatchBuilder = this.also {
|
||||
properties[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
private class PolySymbolMatchPointer<T : PolySymbolMatch>(
|
||||
polySymbolMatch: PolySymbolMatchImpl,
|
||||
private val newInstanceProvider: (
|
||||
matchedName: String,
|
||||
nameSegments: List<PolySymbolNameSegment>,
|
||||
qualifiedKind: PolySymbolQualifiedKind,
|
||||
origin: PolySymbolOrigin,
|
||||
explicitPriority: Priority?,
|
||||
explicitProximity: Int?,
|
||||
additionalProperties: Map<String, Any>,
|
||||
) -> T,
|
||||
) : Pointer<T> {
|
||||
|
||||
private val matchedName = polySymbolMatch.matchedName
|
||||
private val nameSegments = polySymbolMatch.nameSegments
|
||||
.map { it.createPointer() }
|
||||
private val qualifiedKind = polySymbolMatch.qualifiedKind
|
||||
private val origin = polySymbolMatch.origin
|
||||
private val explicitPriority = polySymbolMatch.explicitPriority
|
||||
private val explicitProximity = polySymbolMatch.explicitProximity
|
||||
private val additionalProperties = polySymbolMatch.additionalProperties
|
||||
.createPointers()
|
||||
|
||||
override fun dereference(): T? =
|
||||
nameSegments.map { it.dereference() }
|
||||
.takeIf { it.all { segment -> segment != null } }
|
||||
?.let {
|
||||
var dereferencingProblems = Ref(false)
|
||||
val dereferencedProperties = additionalProperties.dereferencePointers(dereferencingProblems)
|
||||
if (dereferencingProblems.get()) return null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
newInstanceProvider(matchedName, it as List<PolySymbolNameSegment>, qualifiedKind, origin,
|
||||
explicitPriority, explicitProximity, dereferencedProperties)
|
||||
}
|
||||
|
||||
private fun Map<String, Any>.createPointers(): Map<String, Any> =
|
||||
mapValues { (_, value) -> value.createPointers() }
|
||||
|
||||
private fun Any.createPointers(): Any =
|
||||
when (this) {
|
||||
is Symbol -> createPointer()
|
||||
is PsiElement -> createSmartPointer()
|
||||
is Map<*, *> -> mapValues { (_, value) -> value?.createPointers() }
|
||||
is List<*> -> map { it?.createPointers() }
|
||||
is Set<*> -> mapTo(HashSet()) { it?.createPointers() }
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun Map<String, Any>.dereferencePointers(anyProblems: Ref<Boolean>): Map<String, Any> =
|
||||
mapValues { (_, value) -> value.dereferencePointers(anyProblems) }
|
||||
|
||||
private fun Any.dereferencePointers(anyProblems: Ref<Boolean>): Any =
|
||||
when (this) {
|
||||
is Pointer<*> -> dereference().also { if (it == null) anyProblems.set(true) } ?: this
|
||||
is Map<*, *> -> mapValues { (_, value) -> value?.dereferencePointers(anyProblems) }
|
||||
is List<*> -> map { it?.dereferencePointers(anyProblems) }
|
||||
is Set<*> -> mapTo(HashSet()) { it?.dereferencePointers(anyProblems) }
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,9 @@ import com.intellij.refactoring.rename.api.RenameTarget
|
||||
import com.intellij.util.containers.Stack
|
||||
import javax.swing.Icon
|
||||
|
||||
abstract class PolySymbolDelegate<T : PolySymbol>(val delegate: T) : PolySymbol {
|
||||
interface PolySymbolDelegate<T : PolySymbol> : PolySymbol {
|
||||
|
||||
val delegate: T
|
||||
|
||||
override val psiContext: PsiElement?
|
||||
get() = delegate.psiContext
|
||||
@@ -44,12 +46,6 @@ abstract class PolySymbolDelegate<T : PolySymbol>(val delegate: T) : PolySymbol
|
||||
get() = delegate.queryScope
|
||||
override val name: String
|
||||
get() = delegate.name
|
||||
override val description: String?
|
||||
get() = delegate.description
|
||||
override val descriptionSections: Map<String, String>
|
||||
get() = delegate.descriptionSections
|
||||
override val docUrl: String?
|
||||
get() = delegate.docUrl
|
||||
override val icon: Icon?
|
||||
get() = delegate.icon
|
||||
override val apiStatus: PolySymbolApiStatus
|
||||
@@ -62,8 +58,6 @@ abstract class PolySymbolDelegate<T : PolySymbol>(val delegate: T) : PolySymbol
|
||||
get() = delegate.extension
|
||||
override val required: Boolean?
|
||||
get() = delegate.required
|
||||
override val defaultValue: String?
|
||||
get() = delegate.defaultValue
|
||||
override val priority: PolySymbol.Priority?
|
||||
get() = delegate.priority
|
||||
override val proximity: Int?
|
||||
@@ -77,9 +71,6 @@ abstract class PolySymbolDelegate<T : PolySymbol>(val delegate: T) : PolySymbol
|
||||
override val properties: Map<String, Any>
|
||||
get() = delegate.properties
|
||||
|
||||
override fun createDocumentation(location: PsiElement?): PolySymbolDocumentation? =
|
||||
delegate.createDocumentation(location)
|
||||
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
delegate.getDocumentationTarget(location)
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.utils
|
||||
|
||||
import com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
interface PolySymbolDelegateWithDocumentation<T : PolySymbolWithDocumentation> : PolySymbolDelegate<T>, PolySymbolWithDocumentation {
|
||||
|
||||
override val description: String?
|
||||
get() = delegate.description
|
||||
override val descriptionSections: Map<String, String>
|
||||
get() = delegate.descriptionSections
|
||||
override val docUrl: String?
|
||||
get() = delegate.docUrl
|
||||
override val defaultValue: String?
|
||||
get() = delegate.defaultValue
|
||||
|
||||
override fun createDocumentation(location: PsiElement?): PolySymbolDocumentation? =
|
||||
delegate.createDocumentation(location)
|
||||
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
delegate.getDocumentationTarget(location)
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import com.intellij.polySymbols.impl.withOffset
|
||||
import com.intellij.polySymbols.impl.withRange
|
||||
import com.intellij.polySymbols.patterns.impl.applyIcons
|
||||
import com.intellij.polySymbols.query.*
|
||||
import com.intellij.polySymbols.query.impl.PolySymbolMatchImpl
|
||||
import com.intellij.polySymbols.query.impl.PolySymbolMatchBase
|
||||
import com.intellij.polySymbols.references.PolySymbolReferenceProblem.ProblemKind
|
||||
import com.intellij.pom.Navigatable
|
||||
import com.intellij.psi.PsiElement
|
||||
@@ -84,7 +84,10 @@ fun PolySymbol.withMatchedKind(qualifiedKind: PolySymbolQualifiedKind): PolySymb
|
||||
else this
|
||||
|
||||
fun PolySymbol.withNavigationTarget(target: PsiElement): PolySymbol =
|
||||
object : PolySymbolDelegate<PolySymbol>(this@withNavigationTarget) {
|
||||
object : PolySymbolDelegate<PolySymbol> {
|
||||
override val delegate: PolySymbol
|
||||
get() = this@withNavigationTarget
|
||||
|
||||
override fun getNavigationTargets(project: Project): Collection<NavigationTarget> =
|
||||
listOf(SymbolNavigationService.getInstance().psiElementNavigationTarget(target))
|
||||
|
||||
@@ -139,7 +142,7 @@ fun PolySymbolNameSegment.withSymbols(symbols: List<PolySymbol>): PolySymbolName
|
||||
(this as PolySymbolNameSegmentImpl).withSymbols(symbols)
|
||||
|
||||
fun PolySymbolMatch.withSegments(segments: List<PolySymbolNameSegment>): PolySymbolMatch =
|
||||
(this as PolySymbolMatchImpl).withSegments(segments)
|
||||
(this as PolySymbolMatchBase).withSegments(segments)
|
||||
|
||||
fun PolySymbol.match(
|
||||
nameToMatch: String,
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.intellij.platform.backend.navigation.NavigationTarget
|
||||
import com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
abstract class PsiSourcedPolySymbolDelegate<T : PsiSourcedPolySymbol>(delegate: T) : PolySymbolDelegate<T>(delegate), PsiSourcedPolySymbol {
|
||||
interface PsiSourcedPolySymbolDelegate<T : PsiSourcedPolySymbol> : PolySymbolDelegate<T>, PsiSourcedPolySymbol {
|
||||
|
||||
override val source: PsiElement?
|
||||
get() = delegate.source
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.polySymbols.webTypes
|
||||
|
||||
import com.intellij.model.Pointer
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.platform.backend.documentation.DocumentationTarget
|
||||
import com.intellij.polySymbols.PolySymbol
|
||||
import com.intellij.polySymbols.PolySymbol.Companion.PROP_NO_DOC
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.documentation.impl.PolySymbolDocumentationTargetImpl
|
||||
import com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
interface WebTypesSymbol : PsiSourcedPolySymbol {
|
||||
interface WebTypesSymbol : PsiSourcedPolySymbol, PolySymbolWithDocumentation {
|
||||
|
||||
val location: Location?
|
||||
|
||||
override fun getDocumentationTarget(location: PsiElement?): DocumentationTarget? =
|
||||
if (properties[PROP_NO_DOC] != true)
|
||||
PolySymbolDocumentationTargetImpl(this, location)
|
||||
else
|
||||
null
|
||||
|
||||
override fun createPointer(): Pointer<out WebTypesSymbol>
|
||||
|
||||
sealed interface Location
|
||||
|
||||
sealed interface FileLocation {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.model.Symbol
|
||||
import com.intellij.polySymbols.*
|
||||
import com.intellij.polySymbols.completion.PolySymbolCodeCompletionItem
|
||||
import com.intellij.polySymbols.context.PolyContext
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
import com.intellij.polySymbols.patterns.PolySymbolsPattern
|
||||
import com.intellij.polySymbols.query.PolySymbolsCodeCompletionQueryParams
|
||||
@@ -17,6 +18,7 @@ import com.intellij.polySymbols.webTypes.impl.WebTypesJsonContributionAdapter
|
||||
import com.intellij.polySymbols.webTypes.impl.wrap
|
||||
import com.intellij.polySymbols.webTypes.json.*
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.asSafely
|
||||
import com.intellij.util.containers.Stack
|
||||
import javax.swing.Icon
|
||||
|
||||
@@ -111,17 +113,21 @@ open class WebTypesSymbolBase : WebTypesSymbol {
|
||||
final override val description: String?
|
||||
get() = base.contribution.description
|
||||
?.let { base.jsonOrigin.renderDescription(base.contribution.description) }
|
||||
?: superContributions.asSequence().mapNotNull { it.description }.firstOrNull()
|
||||
?: superContributions.asSequence()
|
||||
.mapNotNull { it.asSafely<PolySymbolWithDocumentation>()?.description }
|
||||
.firstOrNull()
|
||||
|
||||
final override val descriptionSections: Map<String, String>
|
||||
get() = (base.contribution.descriptionSections?.additionalProperties?.asSequence() ?: emptySequence())
|
||||
.plus(superContributions.asSequence().flatMap { it.descriptionSections.asSequence() })
|
||||
.plus(superContributions.asSequence().flatMap {
|
||||
it.asSafely<PolySymbolWithDocumentation>()?.descriptionSections?.asSequence() ?: emptySequence()
|
||||
})
|
||||
.distinctBy { it.key }
|
||||
.associateBy({ it.key }, { base.jsonOrigin.renderDescription(it.value) })
|
||||
|
||||
final override val docUrl: String?
|
||||
get() = base.contribution.docUrl
|
||||
?: superContributions.asSequence().mapNotNull { it.docUrl }.firstOrNull()
|
||||
?: superContributions.asSequence().mapNotNull { it.asSafely<PolySymbolWithDocumentation>()?.docUrl }.firstOrNull()
|
||||
|
||||
final override val icon: Icon?
|
||||
get() = base.icon ?: superContributions.asSequence().mapNotNull { it.icon }.firstOrNull()
|
||||
@@ -180,12 +186,11 @@ open class WebTypesSymbolBase : WebTypesSymbol {
|
||||
final override val defaultValue: String?
|
||||
get() = (base.contribution as? GenericContribution)?.default
|
||||
?: (base.contribution as? HtmlAttribute)?.default
|
||||
?: superContributions.firstOrNull()?.defaultValue
|
||||
?: superContributions.firstNotNullOfOrNull { it.asSafely<PolySymbolWithDocumentation>()?.defaultValue }
|
||||
|
||||
final override val pattern: PolySymbolsPattern?
|
||||
get() = base.jsonPattern?.wrap(base.contribution.name, origin as WebTypesJsonOrigin)
|
||||
|
||||
|
||||
final override val queryScope: List<PolySymbolsScope>
|
||||
get() = superContributions.asSequence()
|
||||
.flatMap { it.queryScope }
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.polySymbols.PolySymbolApiStatus
|
||||
import com.intellij.polySymbols.PolySymbolNameSegment
|
||||
import com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
import com.intellij.polySymbols.completion.PolySymbolCodeCompletionItem
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
import com.intellij.polySymbols.testFramework.DebugOutputPrinter
|
||||
import com.intellij.polySymbols.utils.completeMatch
|
||||
@@ -70,9 +71,11 @@ open class PolySymbolsDebugOutputPrinter : DebugOutputPrinter() {
|
||||
printProperty(level, "type", source.type)
|
||||
printProperty(level, "attrValue", source.attributeValue)
|
||||
printProperty(level, "complete", source.completeMatch)
|
||||
printProperty(level, "description", source.description?.ellipsis(45))
|
||||
printProperty(level, "docUrl", source.docUrl)
|
||||
printProperty(level, "descriptionSections", source.descriptionSections.takeIf { it.isNotEmpty() })
|
||||
if (source is PolySymbolWithDocumentation) {
|
||||
printProperty(level, "description", source.description?.ellipsis(45))
|
||||
printProperty(level, "docUrl", source.docUrl)
|
||||
printProperty(level, "descriptionSections", source.descriptionSections.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
printProperty(level, "abstract", source.abstract.takeIf { it })
|
||||
printProperty(level, "virtual", source.virtual.takeIf { it })
|
||||
printProperty(level, "apiStatus", source.apiStatus.takeIf { it !is PolySymbolApiStatus.Stable || it.since != null })
|
||||
|
||||
@@ -5,8 +5,9 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.polySymbols.PolySymbol
|
||||
import com.intellij.polySymbols.PolySymbolApiStatus
|
||||
import com.intellij.polySymbols.documentation.PolySymbolDocumentation
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
|
||||
abstract class MdnDocumentedSymbol : PolySymbol {
|
||||
abstract class MdnDocumentedSymbol : PolySymbolWithDocumentation {
|
||||
|
||||
private val mdnDoc by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
getMdnDocumentation()
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.intellij.polySymbols.PolySymbol.Companion.HTML_ATTRIBUTE_VALUES
|
||||
import com.intellij.polySymbols.completion.PolySymbolCodeCompletionItem
|
||||
import com.intellij.polySymbols.completion.PolySymbolCodeCompletionItemCustomizer
|
||||
import com.intellij.polySymbols.context.PolyContext
|
||||
import com.intellij.polySymbols.documentation.PolySymbolWithDocumentation
|
||||
import com.intellij.polySymbols.html.PolySymbolHtmlAttributeValue
|
||||
import com.intellij.polySymbols.query.*
|
||||
import com.intellij.polySymbols.search.PsiSourcedPolySymbol
|
||||
@@ -182,7 +183,9 @@ class PolySymbolsHtmlQueryConfigurator : PolySymbolsQueryConfigurator {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class StandardHtmlSymbol : MdnDocumentedSymbol(), PsiSourcedPolySymbol
|
||||
abstract class StandardHtmlSymbol : MdnDocumentedSymbol(), PsiSourcedPolySymbol {
|
||||
abstract override fun createPointer(): Pointer<out StandardHtmlSymbol>
|
||||
}
|
||||
|
||||
class HtmlElementDescriptorBasedSymbol(
|
||||
val descriptor: XmlElementDescriptor,
|
||||
|
||||
Reference in New Issue
Block a user