[Analysis API] ExpressionMarkerProvider: do not expose implementation details

And add API for selected expressions

^KT-64805
This commit is contained in:
Dmitrii Gridin
2024-01-08 15:42:59 +01:00
committed by Space Team
parent 663b7fb76f
commit a7984c6794
2 changed files with 14 additions and 17 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -26,7 +26,7 @@ abstract class AbstractAnalysisApiKtTypeByPsiTypeProviderTest : AbstractAnalysis
val psiMethod = moduleStructure.modules.map { module -> val psiMethod = moduleStructure.modules.map { module ->
val psiFiles = testServices.ktModuleProvider.getModuleFiles(module).filterIsInstance<PsiJavaFile>() val psiFiles = testServices.ktModuleProvider.getModuleFiles(module).filterIsInstance<PsiJavaFile>()
val javaFile = psiFiles.first() val javaFile = psiFiles.first()
val offset = testServices.expressionMarkerProvider.carets.getCaretOffset(javaFile.name, null)!! val offset = testServices.expressionMarkerProvider.getCaretPosition(javaFile)
PsiTreeUtil.getParentOfType(javaFile.findElementAt(offset), PsiMethod::class.java) PsiTreeUtil.getParentOfType(javaFile.findElementAt(offset), PsiMethod::class.java)
}.single()!! }.single()!!
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -7,17 +7,16 @@ package org.jetbrains.kotlin.analysis.test.framework.services
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
@@ -29,6 +28,7 @@ import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
import org.jetbrains.kotlin.test.services.TestModuleStructure import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestService import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import kotlin.reflect.KClass import kotlin.reflect.KClass
@@ -76,25 +76,21 @@ internal class ExpressionMarkersSourceFilePreprocessor(testServices: TestService
class ExpressionMarkerProvider : TestService { class ExpressionMarkerProvider : TestService {
private val selected = mutableMapOf<String, TextRange>() private val selected = mutableMapOf<String, TextRange>()
private val carets = CaretProvider()
@PrivateForInline
val carets = CaretProvider()
fun addSelectedExpression(file: TestFile, range: TextRange) { fun addSelectedExpression(file: TestFile, range: TextRange) {
selected[file.relativePath] = range selected[file.relativePath] = range
} }
@OptIn(PrivateForInline::class)
fun addCaret(file: TestFile, caretTag: String?, caretOffset: Int) { fun addCaret(file: TestFile, caretTag: String?, caretOffset: Int) {
carets.addCaret(file.name, caretTag, caretOffset) carets.addCaret(file.name, caretTag, caretOffset)
} }
@OptIn(PrivateForInline::class) fun getCaretPositionOrNull(file: PsiFile, caretTag: String? = null): Int? {
fun getCaretPositionOrNull(file: KtFile, caretTag: String? = null): Int? {
return carets.getCaretOffset(file.name, caretTag) return carets.getCaretOffset(file.name, caretTag)
} }
fun getCaretPosition(file: KtFile, caretTag: String? = null): Int { fun getCaretPosition(file: PsiFile, caretTag: String? = null): Int {
return getCaretPositionOrNull(file, caretTag) return getCaretPositionOrNull(file, caretTag)
?: run { ?: run {
val caretName = "caret${caretTag?.let { "_$it" }.orEmpty()}" val caretName = "caret${caretTag?.let { "_$it" }.orEmpty()}"
@@ -102,6 +98,8 @@ class ExpressionMarkerProvider : TestService {
} }
} }
fun getSelectedRangeOrNull(file: PsiFile): TextRange? = selected[file.name]
fun getSelectedRange(file: PsiFile): TextRange = getSelectedRangeOrNull(file) ?: error("No selected expression found in file")
inline fun <reified P : KtElement> getElementOfTypeAtCaret(file: KtFile, caretTag: String? = null): P { inline fun <reified P : KtElement> getElementOfTypeAtCaret(file: KtFile, caretTag: String? = null): P {
val offset = getCaretPosition(file, caretTag) val offset = getCaretPosition(file, caretTag)
@@ -128,7 +126,7 @@ class ExpressionMarkerProvider : TestService {
caretTag: String? = null caretTag: String? = null
): Collection<Pair<P, KtFile>> { ): Collection<Pair<P, KtFile>> {
return files.mapNotNull { file -> return files.mapNotNull { file ->
carets.getCaretOffset(file.name, caretTag)?.let { offset -> getCaretPositionOrNull(file, caretTag)?.let { offset ->
file.findElementAt(offset)?.parentOfType<P>()?.let { element -> file.findElementAt(offset)?.parentOfType<P>()?.let { element ->
element to file element to file
} }
@@ -183,7 +181,7 @@ class ExpressionMarkerProvider : TestService {
fun getSelectedElementOrNull(file: KtFile): PsiElement? { fun getSelectedElementOrNull(file: KtFile): PsiElement? {
val range = selected[file.name] ?: return null val range = getSelectedRangeOrNull(file) ?: return null
val elements = file.elementsInRange(range).trimWhitespaces() val elements = file.elementsInRange(range).trimWhitespaces()
if (elements.size != 1) { if (elements.size != 1) {
error("Expected one element at rage but found ${elements.size} [${elements.joinToString { it::class.simpleName + ": " + it.text }}]") error("Expected one element at rage but found ${elements.size} [${elements.joinToString { it::class.simpleName + ": " + it.text }}]")
@@ -245,7 +243,7 @@ class ExpressionMarkerProvider : TestService {
* Find the bottommost element of [E] or its subtype wrapped in an '<expr>' selection tag. * Find the bottommost element of [E] or its subtype wrapped in an '<expr>' selection tag.
*/ */
fun <E : KtElement> getBottommostSelectedElementOfType(file: KtFile, elementType: Class<E>): E { fun <E : KtElement> getBottommostSelectedElementOfType(file: KtFile, elementType: Class<E>): E {
val range = selected[file.name] ?: error("No selected expression found in file") val range = getSelectedRange(file)
return getBottommostElementOfTypeInRange(file, range, elementType) return getBottommostElementOfTypeInRange(file, range, elementType)
} }
@@ -258,8 +256,7 @@ class ExpressionMarkerProvider : TestService {
} }
} }
@PrivateForInline private class CaretProvider {
class CaretProvider {
private val caretToFile = mutableMapOf<String, CaretsInFile>() private val caretToFile = mutableMapOf<String, CaretsInFile>()
fun getCaretOffset(filename: String, caretTag: String?): Int? { fun getCaretOffset(filename: String, caretTag: String?): Int? {