Commit 60465959 authored by Alexander.Kirsanov's avatar Alexander.Kirsanov
Browse files

Refactor EditorConfig line marker providers, and override finders. Fix some...

Refactor EditorConfig line marker providers, and override finders. Fix some bugs in inheritance detection
parent 4e2d6af5
Showing with 158 additions and 74 deletions
+158 -74
......@@ -8,9 +8,9 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.editorconfig.language.codeinsight.linemarker.EditorConfigOverridingHeaderFinder
import org.editorconfig.language.psi.EditorConfigFlatOptionKey
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.util.headers.EditorConfigOverridingHeaderSearcher
class EditorConfigGotoSuperHandler : GotoTargetHandler() {
override fun getFeatureUsedKey() = GotoSuperAction.FEATURE_ID
......@@ -44,7 +44,7 @@ class EditorConfigGotoSuperHandler : GotoTargetHandler() {
}
private fun findTargets(element: PsiElement) = when (element) {
is EditorConfigHeader -> EditorConfigOverridingHeaderFinder().getMatchingHeaders(element)
is EditorConfigHeader -> EditorConfigOverridingHeaderSearcher().getMatchingHeaders(element)
is EditorConfigFlatOptionKey -> element.reference.findParents()
else -> emptyList()
}
......
......@@ -4,13 +4,13 @@ package org.editorconfig.language.codeinsight.actions.navigation
import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.search.searches.DefinitionsScopedSearch
import com.intellij.util.Processor
import org.editorconfig.language.codeinsight.linemarker.EditorConfigOverriddenHeaderFinder
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.util.headers.EditorConfigOverriddenHeaderSearcher
class EditorConfigHeaderImplementationSearch : QueryExecutorBase<EditorConfigHeader, DefinitionsScopedSearch.SearchParameters>(true) {
override fun processQuery(queryParameters: DefinitionsScopedSearch.SearchParameters, consumer: Processor<in EditorConfigHeader>) {
val header = queryParameters.element as? EditorConfigHeader ?: return
EditorConfigOverriddenHeaderFinder().getMatchingHeaders(header).forEach {
EditorConfigOverriddenHeaderSearcher().getMatchingHeaders(header).forEach {
if (!consumer.process(it)) return
}
}
......
......@@ -11,66 +11,41 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.editorconfig.language.messages.EditorConfigBundle
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.psi.EditorConfigSection
import org.editorconfig.language.util.EditorConfigPsiTreeUtil
import org.editorconfig.language.util.headers.EditorConfigHeaderSearcher
import java.awt.event.MouseEvent
import javax.swing.Icon
abstract class EditorConfigHeaderLineMarkerProviderBase : LineMarkerProvider {
final override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? = null
final override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
val analyzedHeaders = elements.asSequence()
.mapNotNull { it as? EditorConfigHeader }
.filter { it.isValidGlob }
val headers = elements.mapNotNull { it as? EditorConfigHeader }
if (headers.isEmpty()) return
val currentFile = getCurrentFile(analyzedHeaders) ?: return
val validHeaders = getValidHigherHeaders(currentFile)
val matchingHeaders = getMatchingHeaders(analyzedHeaders, validHeaders)
validHeaders.zip(matchingHeaders).mapNotNull { (header, matchingHeaders) ->
if (matchingHeaders.isEmpty()) return@mapNotNull null
val identifier = PsiTreeUtil.firstChild(header)
LineMarkerInfo(
identifier,
identifier.textRange,
icon,
Pass.LINE_MARKERS,
createTooltipProvider(matchingHeaders),
createNavigationHandler(matchingHeaders),
GutterIconRenderer.Alignment.RIGHT
)
}.forEach { result += it }
val relevantHeaders = searcher.getRelevantHeaders(headers.first())
for (header in headers) {
if (!header.isValidGlob) continue
val matchingHeaders = searcher.getMatchingHeaders(header, relevantHeaders)
val info = createLineMarkerInfo(PsiTreeUtil.firstChild(header), matchingHeaders) ?: continue
result.add(info)
}
}
private fun getCurrentFile(headers: Sequence<EditorConfigHeader>) =
EditorConfigPsiTreeUtil.getOriginalFile(headers.firstOrNull()?.containingFile) as? EditorConfigPsiFile
private fun createLineMarkerInfo(identifier: PsiElement,
matchingHeaders: List<EditorConfigHeader>): LineMarkerInfo<PsiElement>? {
if (matchingHeaders.isEmpty()) return null
val tooltip =
if (matchingHeaders.size == 1) EditorConfigBundle.get(tooltipKeySingular, matchingHeaders.first().text)
else EditorConfigBundle[tooltipKeyPlural]
private fun getValidHigherHeaders(file: EditorConfigPsiFile) = findRelevantPsiFiles(file)
.asSequence()
.flatMap { it.sections.asSequence() }
.map(EditorConfigSection::getHeader)
.filter(EditorConfigHeader::isValidGlob)
private fun getMatchingHeaders(
headers: Sequence<EditorConfigHeader>,
validHeaders: Sequence<EditorConfigHeader>
) = headers.map {
validHeaders
.filter(createRelevantHeaderFilter(it))
.filter(createMatchingHeaderFilter(it))
.toList()
}
fun getMatchingHeaders(header: EditorConfigHeader): List<EditorConfigHeader> {
if (!header.isValidGlob) return emptyList()
val currentFile = getCurrentFile(sequenceOf(header)) ?: return emptyList()
val validHeaders = getValidHigherHeaders(currentFile)
return getMatchingHeaders(sequenceOf(header), validHeaders).single()
}
private fun createTooltipProvider(matchingHeaders: List<EditorConfigHeader>) = { _: PsiElement ->
matchingHeaders.singleOrNull()?.text?.let { EditorConfigBundle.get(tooltipKeySingular, it) }
?: EditorConfigBundle[tooltipKeyPlural]
return LineMarkerInfo(
identifier,
identifier.textRange,
icon,
Pass.LINE_MARKERS,
{ tooltip },
createNavigationHandler(matchingHeaders),
GutterIconRenderer.Alignment.RIGHT
)
}
private fun createNavigationHandler(matchingHeaders: List<EditorConfigHeader>) = { event: MouseEvent, psiElement: PsiElement ->
......@@ -80,9 +55,7 @@ abstract class EditorConfigHeaderLineMarkerProviderBase : LineMarkerProvider {
PsiElementListNavigator.openTargets(event, matchingHeaders.toTypedArray(), title, findUsagesTitle, renderer)
}
protected abstract fun findRelevantPsiFiles(file: EditorConfigPsiFile): List<EditorConfigPsiFile>
protected abstract fun createRelevantHeaderFilter(header: EditorConfigHeader): (EditorConfigHeader) -> Boolean
protected abstract fun createMatchingHeaderFilter(header: EditorConfigHeader): (EditorConfigHeader) -> Boolean
protected abstract val searcher: EditorConfigHeaderSearcher
protected abstract val navigationTitleKey: String
protected abstract val findUsagesTitleKey: String
protected abstract val tooltipKeySingular: String
......
......@@ -2,20 +2,11 @@
package org.editorconfig.language.codeinsight.linemarker
import com.intellij.icons.AllIcons
import org.editorconfig.language.codeinsight.linemarker.EditorConfigSectionLineMarkerProviderUtil.createActualChildFilter
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.util.EditorConfigPsiTreeUtil
import org.editorconfig.language.util.isSubcaseOf
import org.editorconfig.language.util.headers.EditorConfigOverriddenHeaderSearcher
import javax.swing.Icon
class EditorConfigOverriddenHeaderFinder : EditorConfigHeaderLineMarkerProviderBase() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = EditorConfigPsiTreeUtil.findAllChildrenFiles(file) + file
override fun createRelevantHeaderFilter(header: EditorConfigHeader) = createActualChildFilter(header)
override fun createMatchingHeaderFilter(header: EditorConfigHeader): (EditorConfigHeader) -> Boolean = {
it.isSubcaseOf(header)
}
class EditorConfigOverriddenHeaderLineMarkerProvider : EditorConfigHeaderLineMarkerProviderBase() {
override val searcher = EditorConfigOverriddenHeaderSearcher()
override val navigationTitleKey = "message.header.overridden.title"
override val findUsagesTitleKey = "message.header.overridden.find-usages-title"
override val tooltipKeySingular = "message.header.overridden.element"
......
......@@ -2,20 +2,11 @@
package org.editorconfig.language.codeinsight.linemarker
import com.intellij.icons.AllIcons
import org.editorconfig.language.codeinsight.linemarker.EditorConfigSectionLineMarkerProviderUtil.createActualParentFilter
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.util.EditorConfigPsiTreeUtil
import org.editorconfig.language.util.isSubcaseOf
import org.editorconfig.language.util.headers.EditorConfigOverridingHeaderSearcher
import javax.swing.Icon
class EditorConfigOverridingHeaderFinder : EditorConfigHeaderLineMarkerProviderBase() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = EditorConfigPsiTreeUtil.findAllParentsFiles(file)
override fun createRelevantHeaderFilter(header: EditorConfigHeader) = createActualParentFilter(header)
override fun createMatchingHeaderFilter(header: EditorConfigHeader): (EditorConfigHeader) -> Boolean = {
header.isSubcaseOf(it)
}
class EditorConfigOverridingHeaderLineMarkerProvider : EditorConfigHeaderLineMarkerProviderBase() {
override val searcher = EditorConfigOverridingHeaderSearcher()
override val navigationTitleKey = "message.header.overriding.title"
override val findUsagesTitleKey = "message.header.overriding.find-usages-title"
override val tooltipKeySingular = "message.header.overriding.element"
......
......@@ -2,20 +2,11 @@
package org.editorconfig.language.codeinsight.linemarker
import icons.EditorconfigIcons
import org.editorconfig.language.codeinsight.linemarker.EditorConfigSectionLineMarkerProviderUtil.createActualChildFilter
import org.editorconfig.language.codeinsight.linemarker.EditorConfigSectionLineMarkerProviderUtil.isPartialOverride
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.util.EditorConfigPsiTreeUtil
import org.editorconfig.language.util.headers.EditorConfigPartiallyOverriddenHeaderSearcher
import javax.swing.Icon
class EditorConfigPartiallyOverriddenHeaderFinder : EditorConfigHeaderLineMarkerProviderBase() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = EditorConfigPsiTreeUtil.findAllChildrenFiles(file) + file
override fun createRelevantHeaderFilter(header: EditorConfigHeader) = createActualChildFilter(header)
override fun createMatchingHeaderFilter(header: EditorConfigHeader): (EditorConfigHeader) -> Boolean = {
isPartialOverride(header, it)
}
class EditorConfigPartiallyOverriddenHeaderLineMarkerProvider : EditorConfigHeaderLineMarkerProviderBase() {
override val searcher = EditorConfigPartiallyOverriddenHeaderSearcher()
override val navigationTitleKey = "message.header.partially-overridden.title"
override val findUsagesTitleKey = "message.header.partially-overridden.find-usages-title"
override val tooltipKeySingular = "message.header.partially-overridden.element"
......
......@@ -2,20 +2,11 @@
package org.editorconfig.language.codeinsight.linemarker
import icons.EditorconfigIcons
import org.editorconfig.language.codeinsight.linemarker.EditorConfigSectionLineMarkerProviderUtil.createActualParentFilter
import org.editorconfig.language.codeinsight.linemarker.EditorConfigSectionLineMarkerProviderUtil.isPartialOverride
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.util.EditorConfigPsiTreeUtil
import org.editorconfig.language.util.headers.EditorConfigPartiallyOverridingHeaderSearcher
import javax.swing.Icon
class EditorConfigPartiallyOverridingHeaderFinder : EditorConfigHeaderLineMarkerProviderBase() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = EditorConfigPsiTreeUtil.findAllParentsFiles(file)
override fun createRelevantHeaderFilter(header: EditorConfigHeader) = createActualParentFilter(header)
override fun createMatchingHeaderFilter(header: EditorConfigHeader): (EditorConfigHeader) -> Boolean = {
isPartialOverride(it, header)
}
override val searcher = EditorConfigPartiallyOverridingHeaderSearcher()
override val navigationTitleKey = "message.header.partially-overriding.title"
override val findUsagesTitleKey = "message.header.partially-overriding.find-usages-title"
override val tooltipKeySingular = "message.header.partially-overriding.element"
......
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.editorconfig.language.util.headers
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.psi.EditorConfigSection
import org.editorconfig.language.util.EditorConfigPsiTreeUtil
abstract class EditorConfigHeaderSearcher {
fun getMatchingHeaders(header: EditorConfigHeader): List<EditorConfigHeader> {
if (!header.isValidGlob) return emptyList()
val relevantHeaders = getRelevantHeaders(header)
return getMatchingHeaders(header, relevantHeaders)
}
fun getMatchingHeaders(header: EditorConfigHeader, relevantHeaders: Sequence<EditorConfigHeader>): List<EditorConfigHeader> {
return relevantHeaders.filter { isMatchingHeader(header, it) }.toList()
}
fun getRelevantHeaders(header: EditorConfigHeader): Sequence<EditorConfigHeader> {
val currentFile = EditorConfigPsiTreeUtil.getOriginalFile(header.containingFile) as? EditorConfigPsiFile ?: return emptySequence()
return findRelevantPsiFiles(currentFile)
.map(EditorConfigPsiFile::sections)
.flatMap(List<EditorConfigSection>::asSequence)
.map(EditorConfigSection::getHeader)
.filter(EditorConfigHeader::isValidGlob)
}
protected abstract fun findRelevantPsiFiles(file: EditorConfigPsiFile): Sequence<EditorConfigPsiFile>
protected abstract fun isMatchingHeader(baseHeader: EditorConfigHeader, testedHeader: EditorConfigHeader): Boolean
}
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.editorconfig.language.codeinsight.linemarker
package org.editorconfig.language.util.headers
import com.intellij.psi.PsiElement
import org.editorconfig.core.EditorConfigAutomatonBuilder.getCachedHeaderAutomaton
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.util.isSubcaseOf
object EditorConfigSectionLineMarkerProviderUtil {
object EditorConfigHeaderSearcherUtil {
private fun isActualParent(parent: PsiElement, child: PsiElement) =
parent.containingFile != child.containingFile
|| parent.textOffset < child.textOffset
fun createActualParentFilter(child: EditorConfigHeader) = { parent: EditorConfigHeader -> isActualParent(parent, child) }
fun createActualChildFilter(parent: EditorConfigHeader) = { child: EditorConfigHeader -> isActualParent(parent, child) }
fun isPartialOverride(parent: EditorConfigHeader, child: EditorConfigHeader): Boolean {
if (child.isSubcaseOf(parent)) return false
if (!isActualParent(parent, child)) return false
if (child.isSubcaseOf(parent)) return false // todo replace with EditorConfigOverrideKind
val childAutomaton = getCachedHeaderAutomaton(child)
val parentAutomaton = getCachedHeaderAutomaton(parent)
val intersection = childAutomaton.intersection(parentAutomaton)
return !intersection.isEmpty
}
fun isOverride(parent: EditorConfigHeader, child: EditorConfigHeader): Boolean {
if (!isActualParent(parent, child)) return false
return child.isSubcaseOf(parent)
}
}
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.editorconfig.language.util.headers
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.util.EditorConfigPsiTreeUtil.findAllChildrenFiles
class EditorConfigOverriddenHeaderSearcher : EditorConfigHeaderSearcher() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = (findAllChildrenFiles(file) + file).asSequence()
override fun isMatchingHeader(baseHeader: EditorConfigHeader, testedHeader: EditorConfigHeader) =
EditorConfigHeaderSearcherUtil.isOverride(baseHeader, testedHeader)
}
class EditorConfigPartiallyOverriddenHeaderSearcher : EditorConfigHeaderSearcher() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = (findAllChildrenFiles(file) + file).asSequence()
override fun isMatchingHeader(baseHeader: EditorConfigHeader, testedHeader: EditorConfigHeader) =
EditorConfigHeaderSearcherUtil.isPartialOverride(baseHeader, testedHeader)
}
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.editorconfig.language.util.headers
import org.editorconfig.language.psi.EditorConfigHeader
import org.editorconfig.language.psi.EditorConfigPsiFile
import org.editorconfig.language.util.EditorConfigPsiTreeUtil.findAllParentsFiles
class EditorConfigOverridingHeaderSearcher : EditorConfigHeaderSearcher() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = findAllParentsFiles(file).asSequence()
override fun isMatchingHeader(baseHeader: EditorConfigHeader, testedHeader: EditorConfigHeader) =
EditorConfigHeaderSearcherUtil.isOverride(testedHeader, baseHeader)
}
class EditorConfigPartiallyOverridingHeaderSearcher : EditorConfigHeaderSearcher() {
override fun findRelevantPsiFiles(file: EditorConfigPsiFile) = findAllParentsFiles(file).asSequence()
override fun isMatchingHeader(baseHeader: EditorConfigHeader, testedHeader: EditorConfigHeader) =
EditorConfigHeaderSearcherUtil.isPartialOverride(testedHeader, baseHeader)
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment