From 1605b5a5852492108a52c42f971edb195138b6fd Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 7 Feb 2021 04:26:57 +0300 Subject: [PATCH 01/52] build rplugin with R6 base classes on linux --- build.gradle.kts | 1 + grammars/library_summary.proto | 24 +++++ grammars/r.bnf | 2 +- resources/META-INF/rplugin-common.xml | 1 + src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 93 +++++++++++++++++++ .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 92 ++++++++++++++++++ .../jetbrains/r/classes/s4/RS4ClassInfo.kt | 22 +++++ .../r/classes/s4/RS4ClassInfoUtil.kt | 4 +- .../r/psi/RCallExpressionElementType.kt | 24 +++-- src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt | 7 ++ .../jetbrains/r/psi/stubs/R6ClassNameIndex.kt | 47 ++++++++++ .../r/psi/stubs/RCallExpressionStub.kt | 2 + .../r/psi/stubs/RCallExpressionStubImpl.kt | 6 +- .../r/skeleton/RSkeletonFileStubBuilder.kt | 40 ++++++-- .../r/skeleton/psi/RSkeletonCallExpression.kt | 30 ++---- .../psi/RSkeletonCallExpressionElementType.kt | 23 +++-- .../psi/RSkeletonCallExpressionStub.kt | 15 ++- test/org/jetbrains/r/RUsefulTestCase.kt | 1 + .../r/classes/RClassesUtilTestsBase.kt | 31 +++++++ .../r/classes/s4/RS4ClassInfoUtilTests.kt | 46 +++++++++ 20 files changed, 463 insertions(+), 48 deletions(-) create mode 100644 src/org/jetbrains/r/classes/r6/R6ClassInfo.kt create mode 100644 src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt create mode 100644 src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt create mode 100644 test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt create mode 100644 test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt diff --git a/build.gradle.kts b/build.gradle.kts index 034606cb0..104b65371 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { compile("com.google.protobuf:protobuf-java:$protobufVersion") compile("io.grpc:grpc-stub:$grpcVersion") compile("io.grpc:grpc-protobuf:$grpcVersion") + compile("org.assertj:assertj-core:3.18.1") runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") protobuf(files("protos/", "grammars/")) } diff --git a/grammars/library_summary.proto b/grammars/library_summary.proto index f06f90fc7..b1a373e03 100644 --- a/grammars/library_summary.proto +++ b/grammars/library_summary.proto @@ -15,6 +15,7 @@ message RLibrarySymbol { PRIMITIVE = 2; DATASET = 3; S4CLASS = 4; + R6CLASS = 5; } message FunctionRepresentation { message ExtraNamedArguments { @@ -36,12 +37,35 @@ message RLibrarySymbol { bool isVirtual = 4; } + message R6ClassRepresentation { + message R6ClassField { + string name = 1; + string Any = 2; + bool isPublic = 3; + } + message R6ClassMethod { + string name = 1; + string Any = 2; + bool isPublic = 3; + } + message R6ClassActiveBinding { + string name = 1; + bool isPublic = 3; + } + string packageName = 1; + string superClass = 2; + repeated R6ClassField fields = 3; + repeated R6ClassField methods = 4; + repeated R6ClassField activeBindings = 5; + } + string name = 1; Type type = 2; bool exported = 3; oneof representation { FunctionRepresentation functionRepresentation = 4; S4ClassRepresentation s4ClassRepresentation = 5; + R6ClassRepresentation r6ClassRepresentation = 6; } } diff --git a/grammars/r.bnf b/grammars/r.bnf index b21eaf514..289b7e5d1 100644 --- a/grammars/r.bnf +++ b/grammars/r.bnf @@ -130,7 +130,7 @@ fake assignment_statement ::= { // Direct usage produce "employs left-recursion unsupported by generator" warning fake call_expression ::= { - methods=[ getArgumentList getExpression getAssociatedS4ClassInfo ] + methods=[ getArgumentList getExpression getAssociatedS4ClassInfo getAssociatedR6ClassInfo ] extends="org.jetbrains.r.psi.impl.RCallExpressionBase" stubClass="org.jetbrains.r.psi.stubs.RCallExpressionStub" implements="org.jetbrains.r.psi.api.RExpression" diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index 876211292..20df44d6a 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -433,6 +433,7 @@ You can find the source code in the following repositories: + diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt new file mode 100644 index 000000000..f9566d3cd --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.openapi.util.io.DataInputOutputUtilRt +import com.intellij.psi.stubs.StubInputStream +import com.intellij.psi.stubs.StubOutputStream +import com.intellij.util.io.StringRef + +data class R6ClassField(val name: String, val defaultValue: Any, val isPublic: Boolean = true) +data class R6ClassMethod(val name: String, val parameters: Map, val isPublic: Boolean = true) +data class R6ClassActiveBinding(val name: String, val isPublic: Boolean = true) + +// initialize() and print() and finalize() methods override ??? +// inherit ??? + +// PUBLIC PRIVATE !!! +data class R6ClassInfo(val className: String, + val packageName: String, + val superClass: String, + val fields: List, + val methods: List, + val activeBindings: List) { + + fun serialize(dataStream: StubOutputStream) { + dataStream.writeName(className) + dataStream.writeName(packageName) + dataStream.writeName(superClass) + + DataInputOutputUtilRt.writeSeq(dataStream, fields) { + dataStream.writeName(it.name); + dataStream.writeBoolean(it.isPublic) + } + + DataInputOutputUtilRt.writeSeq(dataStream, methods) { + dataStream.writeName(it.name) + dataStream.writeBoolean(it.isPublic); + } + + DataInputOutputUtilRt.writeSeq(dataStream, activeBindings) { + dataStream.writeName(it.name) + dataStream.writeBoolean(it.isPublic); + } + } + + companion object { + fun createDummyFromCoupleParameters(className: String, packageName: String = "test_package"): R6ClassInfo { + return R6ClassInfo(className = className, + packageName = packageName, + superClass = "", + fields = emptyList(), + methods = emptyList(), + activeBindings = emptyList()) + } + + fun empty(): R6ClassInfo { + return R6ClassInfo(className = "", + packageName = "", + superClass = "", + fields = emptyList(), + methods = emptyList(), + activeBindings = emptyList()) + } + + fun deserialize(dataStream: StubInputStream): R6ClassInfo { + val className = StringRef.toString(dataStream.readName()) + val packageName = StringRef.toString(dataStream.readName()) + val superClass = StringRef.toString(dataStream.readName()) + + val fields = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + val isPublic = dataStream.readBoolean() + R6ClassField(name, defaultValue = 0, isPublic = isPublic) + } + + val methods = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + val isPublic = dataStream.readBoolean() + R6ClassMethod(name, parameters = emptyMap(), isPublic = isPublic) + } + + val activeBindings = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + val isPublic = dataStream.readBoolean() + R6ClassActiveBinding(name, isPublic = isPublic) + } + + return R6ClassInfo(className, packageName, superClass, fields, methods, activeBindings) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt new file mode 100644 index 000000000..55960830e --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.openapi.util.Key +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.packages.RPackageProjectManager +import org.jetbrains.r.psi.RElementFactory +import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RStringLiteralExpression +import org.jetbrains.r.psi.isFunctionFromLibrarySoft + +object R6ClassInfoUtil { + public const val R6PackageName = "R6" + public const val R6CreateClassMethod = "R6Class" + + private const val argumentClassName = "classname" + private const val argumentSuperClass = "inherit" + private const val argumentPublic = "public" + private const val argumentPrivate = "private" + + private val INSTANTIATE_CLASS_DEFINITION_KEY: Key = Key.create("R6_INSTANTIATE_CLASS_DEFINITION") + + private val INSTANTIATE_CLASS_DEFINITION = + """R6Class <- function (classname = NULL, public = list(), private = NULL, + active = NULL, inherit = NULL, lock_objects = TRUE, class = TRUE, + portable = TRUE, lock_class = FALSE, cloneable = TRUE, + parent_env = parent.frame(), lock) {}""".trimIndent() + + fun getAssociatedClassName(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val arg = argumentInfo.getArgumentPassedToParameter(argumentClassName) as? RStringLiteralExpression + return arg?.name + } + + fun getAssociatedSuperClassName(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + return (argumentInfo.getArgumentPassedToParameter(argumentSuperClass) as? RStringLiteralExpression)?.name + } + + fun getAssociatedFields(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + return emptyList() + } + + fun getAssociatedMethods(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + return emptyList() + } + + fun getAssociatedActiveBindings(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + return emptyList() + } + + fun parseR6ClassInfo(callExpression: RCallExpression): R6ClassInfo? { + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val project = callExpression.project + var definition = project.getUserData(INSTANTIATE_CLASS_DEFINITION_KEY) + + if (definition == null || !definition.isValid) { + val instantiateClassDefinition = + RElementFactory.createRPsiElementFromText(callExpression.project, INSTANTIATE_CLASS_DEFINITION) as RAssignmentStatement + definition = instantiateClassDefinition.also { project.putUserData(INSTANTIATE_CLASS_DEFINITION_KEY, it) } + } + + val argumentInfo = RParameterInfoUtil.getArgumentInfo(callExpression, definition) ?: return null + val className = getAssociatedClassName(callExpression, argumentInfo) ?: return null + val superClassName = getAssociatedSuperClassName(callExpression, argumentInfo) ?: "" + val fields = getAssociatedFields(callExpression, argumentInfo) ?: emptyList() + val methods = getAssociatedMethods(callExpression, argumentInfo) ?: emptyList() + val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) ?: emptyList() + + val packageName = RPackageProjectManager.getInstance(project).getProjectPackageDescriptionInfo()?.packageName ?: "" + // return R6ClassInfo(className, packageName, superClassName, fields, methods, activeBindings) + return R6ClassInfo.createDummyFromCoupleParameters(className, packageName) + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt index 56237871f..68afa50bc 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt @@ -37,4 +37,26 @@ data class RS4ClassInfo(val className: String, return RS4ClassInfo(className, packageName, slots, superClasses, isVirtual) } } + + override fun toString() : String { + return buildString { + append("setClass('").append(className).append("', ") + append("slots = c(") + slots.forEachIndexed { ind, slot -> + if (ind != 0) append(", ") + append(slot.name).append(" = '").append(slot.type).append("'") + } + append("), ") + append("contains = c(") + superClasses.forEachIndexed { ind, superClass -> + if (ind != 0) append(", ") + append("'").append(superClass).append("'") + } + if (isVirtual) { + if (superClasses.isNotEmpty()) append(", ") + append("'VIRTUAL'") + } + append("))") + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt index 57cd09047..6ec65f907 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt @@ -35,7 +35,7 @@ object RS4ClassInfoUtil { if (callExpression == null) return emptyList() if (callExpression is RSkeletonCallExpression) { // S4 classes from packages and so contains all slots - return callExpression.associatedS4ClassInfo.slots + return callExpression.associatedS4ClassInfo!!.slots } if (!callExpression.isFunctionFromLibrary("setClass", "methods")) return emptyList() return CachedValuesManager.getProjectPsiDependentCache(callExpression) { @@ -53,7 +53,7 @@ object RS4ClassInfoUtil { if (callExpression == null) return emptyList() if (callExpression is RSkeletonCallExpression) { // S4 classes from packages and so contains all super classes - return callExpression.associatedS4ClassInfo.superClasses + return callExpression.associatedS4ClassInfo!!.superClasses } if (!callExpression.isFunctionFromLibrary("setClass", "methods")) return emptyList() return CachedValuesManager.getProjectPsiDependentCache(callExpression) { diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index aec97b347..1983118c6 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -9,14 +9,13 @@ import com.intellij.psi.stubs.IndexSink import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubInputStream import com.intellij.psi.stubs.StubOutputStream +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfoUtil import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.impl.RCallExpressionImpl -import org.jetbrains.r.psi.stubs.RCallExpressionStub -import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex -import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.* import java.io.IOException class RCallExpressionElementType(debugName: String) : RStubElementType(debugName) { @@ -25,29 +24,40 @@ class RCallExpressionElementType(debugName: String) : RStubElementType): RCallExpressionStub { - return RCallExpressionStubImpl(parentStub, this, RS4ClassInfoUtil.parseS4ClassInfo(psi)) + return RCallExpressionStubImpl(parentStub, this, RS4ClassInfoUtil.parseS4ClassInfo(psi), R6ClassInfoUtil.parseR6ClassInfo(psi)) } @Throws(IOException::class) override fun serialize(stub: RCallExpressionStub, dataStream: StubOutputStream) { dataStream.writeBoolean(stub.s4ClassInfo != null) + dataStream.writeBoolean(stub.r6ClassInfo != null) + stub.s4ClassInfo?.serialize(dataStream) + stub.r6ClassInfo?.serialize(dataStream) } @Throws(IOException::class) override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): RCallExpressionStub { val s4ClassExists = dataStream.readBoolean() + val r6ClassExists = dataStream.readBoolean() + val s4ClassInfo = if (s4ClassExists) RS4ClassInfo.deserialize(dataStream) else null - return RCallExpressionStubImpl(parentStub, this, s4ClassInfo) + val r6ClassInfo = if (r6ClassExists) R6ClassInfo.deserialize(dataStream) else null + return RCallExpressionStubImpl(parentStub, this, s4ClassInfo, r6ClassInfo) } override fun indexStub(stub: RCallExpressionStub, sink: IndexSink) { stub.s4ClassInfo?.className?.let { RS4ClassNameIndex.sink(sink, it) } + + stub.r6ClassInfo?.className?.let { + R6ClassNameIndex.sink(sink, it) + } } override fun shouldCreateStub(node: ASTNode?): Boolean { - return (node?.psi as? RCallExpression)?.isFunctionFromLibrarySoft("setClass", "methods") == true + return (node?.psi as? RCallExpression)?.isFunctionFromLibrarySoft("setClass", "methods") == true || + (node?.psi as? RCallExpression)?.isFunctionFromLibrarySoft(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName) == true } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt b/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt index 19a1aa601..5d0878a87 100644 --- a/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt +++ b/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt @@ -21,6 +21,8 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import com.intellij.util.IncorrectOperationException import org.jetbrains.r.RElementGenerator +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfoUtil import org.jetbrains.r.parsing.RElementTypes.* @@ -402,6 +404,11 @@ internal object RPsiImplUtil { return callExpression.greenStub?.s4ClassInfo ?: RS4ClassInfoUtil.parseS4ClassInfo(callExpression) } + @JvmStatic + fun getAssociatedR6ClassInfo(callExpression: RCallExpressionImpl): R6ClassInfo? { + return callExpression.greenStub?.r6ClassInfo ?: R6ClassInfoUtil.parseR6ClassInfo(callExpression) + } + private fun getLoopImpl(element: RPsiElement): RLoopStatement? { val loop = PsiTreeUtil.getParentOfType(element, RLoopStatement::class.java, RFunctionExpression::class.java) return if (loop is RLoopStatement) loop else null diff --git a/src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt new file mode 100644 index 000000000..c4b89ee36 --- /dev/null +++ b/src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.psi.stubs + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndex +import com.intellij.psi.stubs.StubIndexKey +import com.intellij.util.Processor +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.s4.RS4ClassInfo +import org.jetbrains.r.psi.api.RCallExpression + +class R6ClassNameIndex : StringStubIndexExtension() { + override fun getKey(): StubIndexKey { + return KEY + } + + companion object { + private val KEY = StubIndexKey.createIndexKey("R.r6class.shortName") + + fun processAllS4ClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { + val stubIndex = StubIndex.getInstance() + stubIndex.processAllKeys(KEY, project) { key -> + stubIndex.processElements(KEY, key, project, scope, RCallExpression::class.java) { declaration -> + declaration.associatedR6ClassInfo?.let { processor.process(declaration to it) } ?: true + } + } + } + + fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { + return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedR6ClassInfo } + } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) + } + + fun sink(sink: IndexSink, name: String) { + sink.occurrence(KEY, name) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt b/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt index 469214c1f..dcba5410b 100644 --- a/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt +++ b/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt @@ -4,9 +4,11 @@ package org.jetbrains.r.psi.stubs import com.intellij.psi.stubs.StubElement +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression interface RCallExpressionStub : StubElement { val s4ClassInfo: RS4ClassInfo? + val r6ClassInfo: R6ClassInfo? } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt b/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt index 69f2ff206..508e4c70d 100644 --- a/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt +++ b/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt @@ -6,15 +6,17 @@ package org.jetbrains.r.psi.stubs import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.stubs.StubBase import com.intellij.psi.stubs.StubElement +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression class RCallExpressionStubImpl(parent: StubElement<*>, stubElementType: IStubElementType<*, *>, - override val s4ClassInfo: RS4ClassInfo?) + override val s4ClassInfo: RS4ClassInfo?, + override val r6ClassInfo: R6ClassInfo?) : StubBase(parent, stubElementType), RCallExpressionStub { override fun toString(): String { - return "RCallExpressionStub(${s4ClassInfo?.className})" + return "RCallExpressionStub(${s4ClassInfo?.className};${r6ClassInfo?.className})" } } \ No newline at end of file diff --git a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt index 2f96b06de..0d54dc373 100644 --- a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt +++ b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt @@ -8,6 +8,10 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.stubs.BinaryFileStubBuilder import com.intellij.psi.stubs.Stub import com.intellij.util.indexing.FileContent +import org.jetbrains.r.classes.r6.R6ClassActiveBinding +import org.jetbrains.r.classes.r6.R6ClassField +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.r6.R6ClassMethod import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassSlot import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo @@ -31,15 +35,35 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { LibrarySummary.RLibraryPackage.parseFrom(it) } for (symbol in binPackage.symbolsList) { + when (symbol.representationCase){ + RepresentationCase.S4CLASSREPRESENTATION -> { + val s4ClassRepresentation = symbol.s4ClassRepresentation + RSkeletonCallExpressionStub(skeletonFileStub, + R_SKELETON_CALL_EXPRESSION, + RS4ClassInfo(symbol.name, + s4ClassRepresentation.packageName, + s4ClassRepresentation.slotsList.map { RS4ClassSlot(it.name, it.type) }, + s4ClassRepresentation.superClassesList, + s4ClassRepresentation.isVirtual), + null) + } + + RepresentationCase.R6CLASSREPRESENTATION -> { + val r6ClassRepresentation = symbol.r6ClassRepresentation + RSkeletonCallExpressionStub(skeletonFileStub, + R_SKELETON_CALL_EXPRESSION, + null, + R6ClassInfo(symbol.name, + r6ClassRepresentation.packageName, + r6ClassRepresentation.superClass, + r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.any ) }, + r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, emptyMap()) }, + r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) + } + } + if (symbol.representationCase == RepresentationCase.S4CLASSREPRESENTATION) { - val s4ClassRepresentation = symbol.s4ClassRepresentation - RSkeletonCallExpressionStub(skeletonFileStub, - R_SKELETON_CALL_EXPRESSION, - RS4ClassInfo(symbol.name, - s4ClassRepresentation.packageName, - s4ClassRepresentation.slotsList.map { RS4ClassSlot(it.name, it.type) }, - s4ClassRepresentation.superClassesList, - s4ClassRepresentation.isVirtual)) + } else { val functionRepresentation = symbol.functionRepresentation diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt index b1d39a6b5..579373dfe 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt @@ -8,6 +8,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.stubs.StubElement import com.intellij.util.IncorrectOperationException +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RArgumentList import org.jetbrains.r.psi.api.RCallExpression @@ -27,30 +28,17 @@ class RSkeletonCallExpression(private val myStub: RSkeletonCallExpressionStub) : override fun getElementType(): IStubElementType, *> = stub.stubType - override fun getName(): String = myStub.s4ClassInfo.className + override fun getName(): String = myStub.s4ClassInfo!!.className override fun canNavigate(): Boolean = false override fun getText(): String { - val info = stub.s4ClassInfo + val s4Info = stub.s4ClassInfo + val r6Info = stub.r6ClassInfo + return buildString { - append("setClass('").append(info.className).append("', ") - append("slots = c(") - info.slots.forEachIndexed { ind, slot -> - if (ind != 0) append(", ") - append(slot.name).append(" = '").append(slot.type).append("'") - } - append("), ") - append("contains = c(") - info.superClasses.forEachIndexed { ind, superClass -> - if (ind != 0) append(", ") - append("'").append(superClass).append("'") - } - if (info.isVirtual) { - if (info.superClasses.isNotEmpty()) append(", ") - append("'VIRTUAL'") - } - append("))") + if (s4Info != null) { append(s4Info.toString()) } + if (r6Info != null) { append(r6Info.toString()) } } } @@ -58,7 +46,9 @@ class RSkeletonCallExpression(private val myStub: RSkeletonCallExpressionStub) : throw IncorrectOperationException("Operation not supported in: $javaClass") } - override fun getAssociatedS4ClassInfo(): RS4ClassInfo = myStub.s4ClassInfo + override fun getAssociatedS4ClassInfo(): RS4ClassInfo? = myStub.s4ClassInfo + + override fun getAssociatedR6ClassInfo(): R6ClassInfo? = myStub.r6ClassInfo override fun getReference(): RReferenceBase<*>? = null diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt index 85b14e492..bb84727a9 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt @@ -11,23 +11,34 @@ import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubInputStream import com.intellij.psi.stubs.StubOutputStream import com.intellij.util.IncorrectOperationException +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.stubs.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl import org.jetbrains.r.psi.stubs.RS4ClassNameIndex import org.jetbrains.r.psi.stubs.RStubElementType -class RSkeletonCallExpressionElementType : RStubElementType("R bin s4") { +class RSkeletonCallExpressionElementType : RStubElementType("R bin s4 r6") { override fun createPsi(stub: RSkeletonCallExpressionStub): RCallExpression { return RSkeletonCallExpression(stub) } override fun serialize(stub: RSkeletonCallExpressionStub, dataStream: StubOutputStream) { - stub.s4ClassInfo.serialize(dataStream) + dataStream.writeBoolean(stub.s4ClassInfo != null) + dataStream.writeBoolean(stub.r6ClassInfo != null) + + stub.s4ClassInfo?.serialize(dataStream) + stub.r6ClassInfo?.serialize(dataStream) } override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): RSkeletonCallExpressionStub { - val s4ClassInfo = RS4ClassInfo.deserialize(dataStream) - return RSkeletonCallExpressionStub(parentStub, this, s4ClassInfo) + val s4ClassExists = dataStream.readBoolean() + val r6ClassExists = dataStream.readBoolean() + + val s4ClassInfo = if (s4ClassExists) RS4ClassInfo.deserialize(dataStream) else null + val r6ClassInfo = if (r6ClassExists) R6ClassInfo.deserialize(dataStream) else null + return RSkeletonCallExpressionStub(parentStub, this, s4ClassInfo, r6ClassInfo) } override fun createStub(psi: RCallExpression, parentStub: StubElement<*>?): RSkeletonCallExpressionStub { @@ -39,7 +50,7 @@ class RSkeletonCallExpressionElementType : RStubElementType, elementType: RSkeletonCallExpressionElementType, - override val s4ClassInfo: RS4ClassInfo) - : StubBase(parent, elementType), RCallExpressionStub + override val s4ClassInfo: RS4ClassInfo?, + override val r6ClassInfo: R6ClassInfo?) + : StubBase(parent, elementType), RCallExpressionStub { + + override fun toString(): String { + return buildString { + append("RSkeletonCallExpressionStub:") + if (s4ClassInfo != null) { append("s4='${s4ClassInfo.className}'") } + if (r6ClassInfo != null) { append("r6='${r6ClassInfo.className}'") } + } + } +} diff --git a/test/org/jetbrains/r/RUsefulTestCase.kt b/test/org/jetbrains/r/RUsefulTestCase.kt index 5a4d6a5d3..897b5b3f9 100644 --- a/test/org/jetbrains/r/RUsefulTestCase.kt +++ b/test/org/jetbrains/r/RUsefulTestCase.kt @@ -259,6 +259,7 @@ abstract class RUsefulTestCase : BasePlatformTestCase() { grDevices magrittr methods + R6 stats utils roxygen2 diff --git a/test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt b/test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt new file mode 100644 index 000000000..74a6bd30d --- /dev/null +++ b/test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes + +import com.intellij.psi.PsiElement +import org.jetbrains.r.RLanguage +import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RFile +import org.jetbrains.r.run.RProcessHandlerBaseTestCase + +abstract class RClassesUtilTestsBase : RProcessHandlerBaseTestCase() { + override fun setUp() { + super.setUp() + addLibraries() + } + + protected fun getRCallExpressionFromAssignment(assignmentStatement: RAssignmentStatement): RCallExpression? { + if (assignmentStatement.children?.size!! < 3) return null + return assignmentStatement.children[2] as RCallExpression; + } + + protected fun getRootElementOfPsi(code: String): PsiElement { + val rFile = myFixture.configureByText("foo.R", code) as RFile + val viewProvider = rFile.getViewProvider(); + val rPsi = viewProvider.getPsi(RLanguage.INSTANCE) + return rPsi.originalElement.firstChild + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt new file mode 100644 index 000000000..15fdb6eb4 --- /dev/null +++ b/test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.s4 + +import org.jetbrains.r.classes.RClassesUtilTestsBase +import org.jetbrains.r.psi.api.RCallExpression + +class RS4ClassInfoUtilTests : RClassesUtilTestsBase() { + private val setClassCode = """ + setClass("Person", + contains = "Being", + slots = c( + name = "character", + age = "numeric" + ) + ) + """.trimIndent() + + fun testGetAssociatedClassName(){ + val setClassExpression = getRootElementOfPsi(setClassCode) as RCallExpression + val className = RS4ClassInfoUtil.getAssociatedClassName(setClassExpression) + + assertEquals("Person", className) + } + + fun testGetAllAssociatedSlots(){ + val setClassExpression = getRootElementOfPsi(setClassCode) as RCallExpression + val classSlots = RS4ClassInfoUtil.getAllAssociatedSlots(setClassExpression) + + assertEquals(2, classSlots.size) + assertEquals(classSlots[0].name, "name") + assertEquals(classSlots[0].type, "character") + assertEquals(classSlots[1].name, "age") + assertEquals(classSlots[1].type, "numeric") + } + + fun testGetAllAssociatedSuperClasses(){ + val setClassExpression = getRootElementOfPsi(setClassCode) as RCallExpression + val superClasses = RS4ClassInfoUtil.getAllAssociatedSuperClasses(setClassExpression) + + assertEquals(1, superClasses.size) + assertEquals(superClasses[0], "Being") + } +} \ No newline at end of file From 050ba6209c2a5ff0e1772c82541ad48c95785674 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 7 Feb 2021 19:13:15 +0300 Subject: [PATCH 02/52] loaded all R packages and success in dummy tests --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 3 +- .../r/classes/r6/R6ClassInfoUtilTests.kt | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 55960830e..e8819ad41 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -11,6 +11,7 @@ import org.jetbrains.r.packages.RPackageProjectManager import org.jetbrains.r.psi.RElementFactory import org.jetbrains.r.psi.api.RAssignmentStatement import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RIdentifierExpression import org.jetbrains.r.psi.api.RStringLiteralExpression import org.jetbrains.r.psi.isFunctionFromLibrarySoft @@ -43,7 +44,7 @@ object R6ClassInfoUtil { argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - return (argumentInfo.getArgumentPassedToParameter(argumentSuperClass) as? RStringLiteralExpression)?.name + return (argumentInfo.getArgumentPassedToParameter(argumentSuperClass) as? RIdentifierExpression)?.name } fun getAssociatedFields(callExpression: RCallExpression, diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt new file mode 100644 index 000000000..101e1d594 --- /dev/null +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import org.jetbrains.r.classes.RClassesUtilTestsBase +import org.jetbrains.r.psi.api.RAssignmentStatement + +class R6ClassInfoUtilTests : RClassesUtilTestsBase() { + private val instantiateClassCode = """ + Car <- R6Class("Car", + inherit = Vehicle, + list( + weight, + speed, + accelerate = function(acc = 1){ + self${"$"}speed <- self${"$"}speed + acc + invisible(self) + }, + slowDown = function(neg_acc = 1){ + self${"$"}speed <- self${"$"}speed - acc + invisible(self) + } + )) + """.trimIndent() + + fun testGetAssociatedClassName(){ + val rAssignmentStatement = getRootElementOfPsi(instantiateClassCode) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val className = R6ClassInfoUtil.getAssociatedClassName(rCallExpression!!) + assertEquals("Car", className) + } + + fun testGetAssociatedSuperClassName(){ + val rAssignmentStatement = getRootElementOfPsi(instantiateClassCode) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val superClassName = R6ClassInfoUtil.getAssociatedSuperClassName(rCallExpression!!) + assertEquals("Vehicle", superClassName) + } +} \ No newline at end of file From 61ceab9effa803fab6e5ab51b183be8e4f5d5376 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Mon, 8 Feb 2021 02:24:56 +0300 Subject: [PATCH 03/52] add test and full r6 class definition --- .../r/classes/r6/R6ClassInfoUtilTests.kt | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index 101e1d594..8c41c3c1e 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -4,16 +4,17 @@ package org.jetbrains.r.classes.r6 +import junit.framework.TestCase import org.jetbrains.r.classes.RClassesUtilTestsBase import org.jetbrains.r.psi.api.RAssignmentStatement class R6ClassInfoUtilTests : RClassesUtilTestsBase() { - private val instantiateClassCode = """ + private val fullClassCodeDefinition = """ Car <- R6Class("Car", inherit = Vehicle, - list( - weight, - speed, + public = list( + weight = 0, + speed = 0, accelerate = function(acc = 1){ self${"$"}speed <- self${"$"}speed + acc invisible(self) @@ -22,20 +23,37 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { self${"$"}speed <- self${"$"}speed - acc invisible(self) } - )) + ), + private = list( + engine_rpm = 1000, + maximize = function(rmp = 1000) { + self${"$"}engine_rpm <- self${"$"}engine_rpm + rmp + invisible(self) + } + ) + ) """.trimIndent() fun testGetAssociatedClassName(){ - val rAssignmentStatement = getRootElementOfPsi(instantiateClassCode) as RAssignmentStatement + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) val className = R6ClassInfoUtil.getAssociatedClassName(rCallExpression!!) assertEquals("Car", className) } fun testGetAssociatedSuperClassName(){ - val rAssignmentStatement = getRootElementOfPsi(instantiateClassCode) as RAssignmentStatement + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) val superClassName = R6ClassInfoUtil.getAssociatedSuperClassName(rCallExpression!!) assertEquals("Vehicle", superClassName) } + + fun testGetAssociatedFields(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) + + TestCase.assertNotNull(classFields) + assertEquals(classFields!!.size, 2) + } } \ No newline at end of file From 091de92e2845920b53d4bbf4130f25390473e60a Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 13 Feb 2021 21:47:19 +0300 Subject: [PATCH 04/52] changed definitions of R6 content due to unsupported overloading --- grammars/library_summary.proto | 3 --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 18 ++++++++---------- .../r/skeleton/RSkeletonFileStubBuilder.kt | 4 ++-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/grammars/library_summary.proto b/grammars/library_summary.proto index b1a373e03..6be4f3c99 100644 --- a/grammars/library_summary.proto +++ b/grammars/library_summary.proto @@ -40,17 +40,14 @@ message RLibrarySymbol { message R6ClassRepresentation { message R6ClassField { string name = 1; - string Any = 2; bool isPublic = 3; } message R6ClassMethod { string name = 1; - string Any = 2; bool isPublic = 3; } message R6ClassActiveBinding { string name = 1; - bool isPublic = 3; } string packageName = 1; string superClass = 2; diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index f9566d3cd..d21ca84cc 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -9,12 +9,11 @@ import com.intellij.psi.stubs.StubInputStream import com.intellij.psi.stubs.StubOutputStream import com.intellij.util.io.StringRef -data class R6ClassField(val name: String, val defaultValue: Any, val isPublic: Boolean = true) -data class R6ClassMethod(val name: String, val parameters: Map, val isPublic: Boolean = true) -data class R6ClassActiveBinding(val name: String, val isPublic: Boolean = true) - -// initialize() and print() and finalize() methods override ??? -// inherit ??? +// no need to care about overloads because R6 lib doesn't support it: +// "All items in public, private, and active must have unique names." +data class R6ClassField(val name: String, val isPublic: Boolean = true) +data class R6ClassMethod(val name: String, val isPublic: Boolean = true) +data class R6ClassActiveBinding(val name: String) // PUBLIC PRIVATE !!! data class R6ClassInfo(val className: String, @@ -41,7 +40,6 @@ data class R6ClassInfo(val className: String, DataInputOutputUtilRt.writeSeq(dataStream, activeBindings) { dataStream.writeName(it.name) - dataStream.writeBoolean(it.isPublic); } } @@ -72,19 +70,19 @@ data class R6ClassInfo(val className: String, val fields = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassField(name, defaultValue = 0, isPublic = isPublic) + R6ClassField(name, isPublic = isPublic) } val methods = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassMethod(name, parameters = emptyMap(), isPublic = isPublic) + R6ClassMethod(name, isPublic = isPublic) } val activeBindings = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassActiveBinding(name, isPublic = isPublic) + R6ClassActiveBinding(name) } return R6ClassInfo(className, packageName, superClass, fields, methods, activeBindings) diff --git a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt index 0d54dc373..8e11cc69d 100644 --- a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt +++ b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt @@ -56,8 +56,8 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { R6ClassInfo(symbol.name, r6ClassRepresentation.packageName, r6ClassRepresentation.superClass, - r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.any ) }, - r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, emptyMap()) }, + r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic ) }, + r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) } } From dff992e035ea2f377f57f8829d641c3ba90b3288 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 14 Feb 2021 18:43:03 +0300 Subject: [PATCH 05/52] added getting r6ClassFields API --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 32 ++++++++++++++++--- .../r/classes/r6/R6ClassInfoUtilTests.kt | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index e8819ad41..2c607e3b4 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -9,10 +9,8 @@ import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.packages.RPackageProjectManager import org.jetbrains.r.psi.RElementFactory -import org.jetbrains.r.psi.api.RAssignmentStatement -import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.api.RIdentifierExpression -import org.jetbrains.r.psi.api.RStringLiteralExpression +import org.jetbrains.r.psi.api.* +import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft object R6ClassInfoUtil { @@ -51,7 +49,15 @@ object R6ClassInfoUtil { argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - return emptyList() + + var r6ClassFields = mutableListOf() + val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + + if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) + if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) + + return r6ClassFields } fun getAssociatedMethods(callExpression: RCallExpression, @@ -90,4 +96,20 @@ object R6ClassInfoUtil { // return R6ClassInfo(className, packageName, superClassName, fields, methods, activeBindings) return R6ClassInfo.createDummyFromCoupleParameters(className, packageName) } + + private fun getFieldsFromExpressionList(r6ClassFields: MutableList, callExpressions: List, isPublicScope: Boolean){ + callExpressions.forEach { + var isField = true + for (child in it.children){ + if (child is RFunctionExpression) { + isField = false + break + } + } + + if (isField && !it.name.isNullOrEmpty()){ + r6ClassFields.add(R6ClassField(it.name!!, isPublicScope)) + } + } + } } \ No newline at end of file diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index 8c41c3c1e..f49ecb5f5 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -54,6 +54,6 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) TestCase.assertNotNull(classFields) - assertEquals(classFields!!.size, 2) + assertEquals(classFields!!.size, 3) } } \ No newline at end of file From 9c3a01340b7747c4e64784afa29043417d8b42de Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 14 Feb 2021 19:54:33 +0300 Subject: [PATCH 06/52] added getting r6ClassMethods API --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 26 ++++++++++++++++--- .../r/classes/r6/R6ClassInfoUtilTests.kt | 19 ++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 2c607e3b4..c59ec89b8 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -64,7 +64,15 @@ object R6ClassInfoUtil { argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - return emptyList() + + var r6ClassMethods = mutableListOf() + val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + + if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) + if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) + + return r6ClassMethods } fun getAssociatedActiveBindings(callExpression: RCallExpression, @@ -97,7 +105,7 @@ object R6ClassInfoUtil { return R6ClassInfo.createDummyFromCoupleParameters(className, packageName) } - private fun getFieldsFromExpressionList(r6ClassFields: MutableList, callExpressions: List, isPublicScope: Boolean){ + private fun getFieldsFromExpressionList(r6ClassFields: MutableList, callExpressions: List, isFromPublicScope: Boolean){ callExpressions.forEach { var isField = true for (child in it.children){ @@ -106,10 +114,20 @@ object R6ClassInfoUtil { break } } + if (isField && !it.name.isNullOrEmpty()) { r6ClassFields.add(R6ClassField(it.name!!, isFromPublicScope)) } + } + } - if (isField && !it.name.isNullOrEmpty()){ - r6ClassFields.add(R6ClassField(it.name!!, isPublicScope)) + private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, callExpressions: List, isFromPublicScope: Boolean){ + callExpressions.forEach { + var isMethod = false + for (child in it.children){ + if (child is RFunctionExpression) { + isMethod = true + break + } } + if (isMethod && !it.name.isNullOrEmpty()) { r6ClassMethods.add(R6ClassMethod(it.name!!, isFromPublicScope)) } } } } \ No newline at end of file diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index f49ecb5f5..60a92219a 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -55,5 +55,24 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { TestCase.assertNotNull(classFields) assertEquals(classFields!!.size, 3) + + val classFieldsNames = classFields.map { it.name } + assertContainsElements(classFieldsNames, "weight") + assertContainsElements(classFieldsNames, "speed") + assertContainsElements(classFieldsNames, "engine_rpm") + } + + fun testGetAssociatedMethods(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) + + TestCase.assertNotNull(classMethods) + assertEquals(classMethods!!.size, 3) + + val classMethodsNames = classMethods.map { it.name } + assertContainsElements(classMethodsNames, "accelerate") + assertContainsElements(classMethodsNames, "slowDown") + assertContainsElements(classMethodsNames, "maximize") } } \ No newline at end of file From 723a055c87f233a6398afa38c1c88d01df559ca3 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Mon, 15 Feb 2021 01:45:39 +0300 Subject: [PATCH 07/52] r6 utils implemented and tested --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 23 +------ .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 44 ++++++------ .../r/psi/RCallExpressionElementType.kt | 9 +-- .../r/classes/r6/R6ClassInfoUtilTests.kt | 68 +++++++++++++++++++ 4 files changed, 91 insertions(+), 53 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index d21ca84cc..7b3c30414 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -44,24 +44,6 @@ data class R6ClassInfo(val className: String, } companion object { - fun createDummyFromCoupleParameters(className: String, packageName: String = "test_package"): R6ClassInfo { - return R6ClassInfo(className = className, - packageName = packageName, - superClass = "", - fields = emptyList(), - methods = emptyList(), - activeBindings = emptyList()) - } - - fun empty(): R6ClassInfo { - return R6ClassInfo(className = "", - packageName = "", - superClass = "", - fields = emptyList(), - methods = emptyList(), - activeBindings = emptyList()) - } - fun deserialize(dataStream: StubInputStream): R6ClassInfo { val className = StringRef.toString(dataStream.readName()) val packageName = StringRef.toString(dataStream.readName()) @@ -70,18 +52,17 @@ data class R6ClassInfo(val className: String, val fields = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassField(name, isPublic = isPublic) + R6ClassField(name, isPublic) } val methods = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassMethod(name, isPublic = isPublic) + R6ClassMethod(name, isPublic) } val activeBindings = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) - val isPublic = dataStream.readBoolean() R6ClassActiveBinding(name) } diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index c59ec89b8..c5a586b88 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -14,13 +14,14 @@ import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft object R6ClassInfoUtil { - public const val R6PackageName = "R6" - public const val R6CreateClassMethod = "R6Class" + const val R6PackageName = "R6" + const val R6CreateClassMethod = "R6Class" private const val argumentClassName = "classname" private const val argumentSuperClass = "inherit" private const val argumentPublic = "public" private const val argumentPrivate = "private" + private const val argumentActive = "active" private val INSTANTIATE_CLASS_DEFINITION_KEY: Key = Key.create("R6_INSTANTIATE_CLASS_DEFINITION") @@ -50,13 +51,12 @@ object R6ClassInfoUtil { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - var r6ClassFields = mutableListOf() + val r6ClassFields = mutableListOf() val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) - return r6ClassFields } @@ -65,13 +65,12 @@ object R6ClassInfoUtil { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - var r6ClassMethods = mutableListOf() + val r6ClassMethods = mutableListOf() val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) - return r6ClassMethods } @@ -79,7 +78,11 @@ object R6ClassInfoUtil { argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - return emptyList() + val r6ClassActiveBindings = mutableListOf() + + val activeBindings = (argumentInfo.getArgumentPassedToParameter(argumentActive) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!activeBindings.isNullOrEmpty()) getActiveBindingsFromExpressionList(r6ClassActiveBindings, activeBindings) + return r6ClassActiveBindings } fun parseR6ClassInfo(callExpression: RCallExpression): R6ClassInfo? { @@ -101,33 +104,24 @@ object R6ClassInfoUtil { val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) ?: emptyList() val packageName = RPackageProjectManager.getInstance(project).getProjectPackageDescriptionInfo()?.packageName ?: "" - // return R6ClassInfo(className, packageName, superClassName, fields, methods, activeBindings) - return R6ClassInfo.createDummyFromCoupleParameters(className, packageName) + return R6ClassInfo(className, packageName, superClassName, fields, methods, activeBindings) } private fun getFieldsFromExpressionList(r6ClassFields: MutableList, callExpressions: List, isFromPublicScope: Boolean){ callExpressions.forEach { - var isField = true - for (child in it.children){ - if (child is RFunctionExpression) { - isField = false - break - } - } - if (isField && !it.name.isNullOrEmpty()) { r6ClassFields.add(R6ClassField(it.name!!, isFromPublicScope)) } + if (it.lastChild !is RFunctionExpression && !it.name.isNullOrEmpty()) { r6ClassFields.add(R6ClassField(it.name!!, isFromPublicScope)) } } } private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, callExpressions: List, isFromPublicScope: Boolean){ callExpressions.forEach { - var isMethod = false - for (child in it.children){ - if (child is RFunctionExpression) { - isMethod = true - break - } - } - if (isMethod && !it.name.isNullOrEmpty()) { r6ClassMethods.add(R6ClassMethod(it.name!!, isFromPublicScope)) } + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { r6ClassMethods.add(R6ClassMethod(it.name!!, isFromPublicScope)) } + } + } + + private fun getActiveBindingsFromExpressionList(r6ClassActiveBindings: MutableList, callExpressions: List){ + callExpressions.forEach { + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { r6ClassActiveBindings.add(R6ClassActiveBinding(it.name!!)) } } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index 1983118c6..7375c939a 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -47,13 +47,8 @@ class RCallExpressionElementType(debugName: String) : RStubElementType Date: Mon, 22 Feb 2021 21:42:41 +0300 Subject: [PATCH 08/52] defined classes for completions and context --- resources/META-INF/rplugin-common.xml | 7 ++ .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 2 + .../r/classes/r6/context/R6Context.kt | 16 ++++ .../r/classes/r6/context/R6ContextProvider.kt | 42 +++++++++ .../r6/context/R6NewObjectContextProvider.kt | 58 ++++++++++++ .../r6/context/R6SetClassContextProvider.kt | 88 +++++++++++++++++++ .../r/editor/RCompletionContributor.kt | 24 ++++- src/org/jetbrains/r/psi/RElementFilters.kt | 13 ++- .../r/completion/R6ClassCompletionTest.kt | 58 ++++++++++++ 9 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 src/org/jetbrains/r/classes/r6/context/R6Context.kt create mode 100644 src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt create mode 100644 src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt create mode 100644 src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt create mode 100644 test/org/jetbrains/r/completion/R6ClassCompletionTest.kt diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index 20df44d6a..cd0ddd698 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -80,6 +80,9 @@ You can find the source code in the following repositories: + + messages.RPluginBundle @@ -732,6 +735,10 @@ You can find the source code in the following repositories: + + + + diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index c5a586b88..9e70a8c00 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -17,6 +17,8 @@ object R6ClassInfoUtil { const val R6PackageName = "R6" const val R6CreateClassMethod = "R6Class" + const val functionNew = "new" + private const val argumentClassName = "classname" private const val argumentSuperClass = "inherit" private const val argumentPublic = "public" diff --git a/src/org/jetbrains/r/classes/r6/context/R6Context.kt b/src/org/jetbrains/r/classes/r6/context/R6Context.kt new file mode 100644 index 000000000..6dcaf2bb9 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6Context.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RPsiElement + +interface R6Context { + val functionName: String + val functionCall: RCallExpression + val argumentInfo: RArgumentInfo + val originalElement: RPsiElement +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt new file mode 100644 index 000000000..c1793d3be --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.openapi.extensions.ExtensionPointName +import org.jetbrains.r.classes.s4.context.RS4Context +import org.jetbrains.r.classes.s4.context.RS4ContextProvider +import org.jetbrains.r.psi.api.RPsiElement +import java.lang.reflect.ParameterizedType + +abstract class R6ContextProvider { + abstract fun getR6Context(element: RPsiElement): T? + + @Suppress("UNCHECKED_CAST") + private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class + + companion object { + private val EP_NAME: ExtensionPointName> = + ExtensionPointName.create("com.intellij.r6ContextProvider") + + fun getProviders(): List> = EP_NAME.extensionList + + fun getR6Context(element: RPsiElement): R6Context? { + return getR6Context(element, R6Context::class.java) + } + + fun getR6Context(element: RPsiElement, vararg searchedContexts: Class): T? { + for (provider in getProviders()) { + if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { + val r6Context = provider.getR6Context(element) + if (r6Context != null) { + @Suppress("UNCHECKED_CAST") + return r6Context as T? + } + } + } + return null + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt new file mode 100644 index 000000000..5d25873c7 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.psi.RPsiUtil +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RNamedArgument +import org.jetbrains.r.psi.api.RPsiElement +import org.jetbrains.r.psi.isFunctionFromLibrary + +sealed class R6NewObjectContext : R6Context { + override val functionName = "new" +} + +// Calculator <- Accumulator$new() +data class R6NewObjectClassNameContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6NewObjectContext() + +class R6NewObjectContextProvider : R6ContextProvider() { + override fun getR6Context(element: RPsiElement): R6NewObjectContext? { + return CachedValuesManager.getCachedValue(element) { + CachedValueProvider.Result.create(getR6ContextInner(element), element) + } + } + + private fun getR6ContextInner(element: RPsiElement): R6NewObjectContext? { + val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null + if (!parentCall.isFunctionFromLibrary(R6ClassInfoUtil.functionNew, R6ClassInfoUtil.R6PackageName)) return null + val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null + + // TODO implement it normally + return if (parentArgumentInfo.getArgumentPassedToParameter("Class") == element) { + // new("") + R6NewObjectClassNameContext(element, parentCall, parentArgumentInfo) + } + else { + val currentArgument = + if (element.parent is RNamedArgument) RPsiUtil.getNamedArgumentByNameIdentifier(element) ?: return null + else element + val arguments = parentCall.argumentList.expressionList + if (!arguments.contains(currentArgument)) return null + else { + // new("ClassName", slot_name) + // new("ClassName", slot_name = slot_value) + R6NewObjectClassNameContext(currentArgument, parentCall, parentArgumentInfo) + } + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt new file mode 100644 index 000000000..b916e2a6f --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt @@ -0,0 +1,88 @@ +package org.jetbrains.r.classes.r6.context + +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RNamedArgument +import org.jetbrains.r.psi.api.RPsiElement +import org.jetbrains.r.psi.isFunctionFromLibrary + +sealed class R6SetClassContext : R6Context { + override val functionName = "setClass" +} + +// setClass("MyClass", "") +data class R6SetClassRepresentationContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassContext() + +// setClass("MyClass", , , "") +data class R6SetClassContainsContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassContext() + +// setClass("MyClass", contains = "") +// setClass("MyClass", contains = c("")) +// setClass("MyClass", contains = c(smt = "") +// setClass("MyClass", representation = representation(smt = "") +// setClass("MyClass", representation = representation("") +// setClass("MyClass", slots = c(name = "")) +data class R6SetClassDependencyClassNameContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassContext() + +class R6SetClassContextProvider : R6ContextProvider() { + override fun getR6Context(element: RPsiElement): R6SetClassContext? { + return CachedValuesManager.getCachedValue(element) { + CachedValueProvider.Result.create(getR6ContextInner(element), element) + } + } + + private fun getR6ContextInner(element: RPsiElement): R6SetClassContext? { + val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null + return if (parentCall.isFunctionFromLibrary("setClass", "methods")) { + val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null + when (element) { + parentArgumentInfo.getArgumentPassedToParameter("representation") -> { + // setClass("MyClass", "") + R6SetClassRepresentationContext(element, parentCall, parentArgumentInfo) + } + parentArgumentInfo.getArgumentPassedToParameter("contains") -> { + // setClass("MyClass", , , "") + R6SetClassContainsContext(element, parentCall, parentArgumentInfo) + } + else -> null + } + } + else { + val superParentCall = PsiTreeUtil.getParentOfType(parentCall, RCallExpression::class.java) ?: return null + if (!superParentCall.isFunctionFromLibrary("setClass", "methods")) return null + + val superParentArgumentInfo = RParameterInfoUtil.getArgumentInfo(superParentCall) ?: return null + return when { + // setClass("MyClass", contains = "") + // setClass("MyClass", contains = c("")) + // setClass("MyClass", contains = c(smt = "") + // setClass("MyClass", representation = representation(smt = "") + // setClass("MyClass", representation = representation("") + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter("contains"), element, false) || + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter("representation"), element, false) -> { + val parent = element.parent + if (parent is RNamedArgument && parent.nameIdentifier == element) null + else R6SetClassDependencyClassNameContext(element, superParentCall, superParentArgumentInfo) + } + + // setClass("MyClass", slots = c(name = "")) + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter("slots"), element, false) -> { + val parent = element.parent + if (parent !is RNamedArgument || parent.assignedValue != element) null + else R6SetClassDependencyClassNameContext(element, superParentCall, superParentArgumentInfo) + } + else -> null + } + } + } +} diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 24b55f4de..59d5d6580 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -53,6 +53,7 @@ class RCompletionContributor : CompletionContributor() { addMemberAccessCompletion() addAtAccessCompletion() addS4ClassContextCompletion() + addR6ClassContextCompletion() addIdentifierCompletion() } @@ -93,9 +94,14 @@ class RCompletionContributor : CompletionContributor() { .and(RElementFilters.S4_CONTEXT_FILTER), S4ClassContextCompletionProvider()) } + private fun addR6ClassContextCompletion() { + extend(CompletionType.BASIC, psiElement().withLanguage(RLanguage.INSTANCE) + .and(RElementFilters.R6_CONTEXT_FILTER), R6ClassContextCompletionProvider()) + } + private fun addStringLiteralCompletion() { extend(CompletionType.BASIC, psiElement().withLanguage(RLanguage.INSTANCE) - .and(RElementFilters.STRING_EXCEPT_S4_CONTEXT_FILTER), StringLiteralCompletionProvider()) + .and(RElementFilters.STRING_EXCEPT_OTHER_LIBRARIES_CONTEXT_FILTER), StringLiteralCompletionProvider()) } private class MemberAccessCompletionProvider : CompletionProvider() { @@ -550,6 +556,22 @@ class RCompletionContributor : CompletionContributor() { } } + private class R6ClassContextCompletionProvider : CompletionProvider() { + override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { + val expression = PsiTreeUtil.getParentOfType(parameters.position, RExpression::class.java, false) ?: return + val file = parameters.originalFile + addR6ClassNameCompletion(expression, file, result) + addR6SlotNameCompletion(expression, file, result) + } + + private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ + // TODO fill completion + } + + private fun addR6SlotNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ + // TODO fill completion + } + } private class StringLiteralCompletionProvider : CompletionProvider() { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { diff --git a/src/org/jetbrains/r/psi/RElementFilters.kt b/src/org/jetbrains/r/psi/RElementFilters.kt index 2205d6ef1..d2e5215a1 100644 --- a/src/org/jetbrains/r/psi/RElementFilters.kt +++ b/src/org/jetbrains/r/psi/RElementFilters.kt @@ -11,6 +11,7 @@ import com.intellij.psi.filters.NotFilter import com.intellij.psi.filters.position.FilterPattern import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType +import org.jetbrains.r.classes.r6.context.R6ContextProvider import org.jetbrains.r.classes.s4.context.RS4ContextProvider import org.jetbrains.r.parsing.RElementTypes import org.jetbrains.r.psi.api.* @@ -26,7 +27,8 @@ object RElementFilters { val IDENTIFIER_OR_STRING_FILTER = FilterPattern(IdentifierOrStringFilter()) val STRING_FILTER = FilterPattern(StringFilter()) val S4_CONTEXT_FILTER = FilterPattern(S4ContextFilter()) - val STRING_EXCEPT_S4_CONTEXT_FILTER = FilterPattern(AndFilter(StringFilter(), NotFilter(S4ContextFilter()))) + val R6_CONTEXT_FILTER = FilterPattern(R6ContextFilter()) + val STRING_EXCEPT_OTHER_LIBRARIES_CONTEXT_FILTER = FilterPattern(AndFilter(StringFilter(), NotFilter(S4ContextFilter()), NotFilter(R6ContextFilter()))) } class MemberFilter : ElementFilter { @@ -124,3 +126,12 @@ class S4ContextFilter : ElementFilter { override fun isClassAcceptable(hintClass: Class<*>?) = true } + +class R6ContextFilter : ElementFilter { + override fun isAcceptable(element: Any?, context: PsiElement?): Boolean { + val expression = PsiTreeUtil.getParentOfType(context, RExpression::class.java, false) ?: return false + return R6ContextProvider.getR6Context(expression) != null + } + + override fun isClassAcceptable(hintClass: Class<*>?) = true +} \ No newline at end of file diff --git a/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt new file mode 100644 index 000000000..0b53ed27a --- /dev/null +++ b/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.completion + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementPresentation +import org.jetbrains.r.console.RConsoleRuntimeInfoImpl +import org.jetbrains.r.console.RConsoleView +import org.jetbrains.r.console.addRuntimeInfo +import org.jetbrains.r.run.RProcessHandlerBaseTestCase + +class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { + + override fun setUp() { + super.setUp() + addLibraries() + } + + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { + val result = doTestBase(text, withRuntimeInfo, inConsole) + assertNotNull(result) + val lookupStrings = result.map { it.lookupString } + assertDoesntContain(lookupStrings, *variants) + } + + private fun doTest(text: String, + vararg variants: Pair, // + strict: Boolean = true, + withRuntimeInfo: Boolean = false, + inConsole: Boolean = false) { + val result = doTestBase(text, withRuntimeInfo, inConsole) + assertNotNull(result) + val lookupStrings = result.map { + val elementPresentation = LookupElementPresentation() + it.renderElement(elementPresentation) + elementPresentation.itemText to elementPresentation.typeText + } + if (strict) { + assertOrderedEquals(lookupStrings, *variants) + } + else { + assertContainsOrdered(lookupStrings, *variants) + } + } + + private fun doTestBase(text: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false): Array { + myFixture.configureByText("foo.R", text) + if (inConsole) { + myFixture.file.putUserData(RConsoleView.IS_R_CONSOLE_KEY, true) + } + if (withRuntimeInfo) { + myFixture.file.addRuntimeInfo(RConsoleRuntimeInfoImpl(rInterop)) + } + return myFixture.completeBasic() + } +} From 768d7e72521a9fd36cf41cfeb8030536f16e6355 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Fri, 26 Feb 2021 21:56:12 +0300 Subject: [PATCH 09/52] add r6 description to service.proto of RKernel --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 1 - .../r/console/RConsoleRuntimeInfo.kt | 31 +++++++++++++++ src/org/jetbrains/r/rinterop/RInterop.kt | 38 +++++++++++++++++++ .../jetbrains/r/completion/AutoPopupTest.kt | 13 +++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index 7b3c30414..2fb2a2fd9 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -15,7 +15,6 @@ data class R6ClassField(val name: String, val isPublic: Boolean = true) data class R6ClassMethod(val name: String, val isPublic: Boolean = true) data class R6ClassActiveBinding(val name: String) -// PUBLIC PRIVATE !!! data class R6ClassInfo(val className: String, val packageName: String, val superClass: String, diff --git a/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt b/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt index 724dbde7a..a8fa53411 100644 --- a/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt +++ b/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.psi.PsiFile import org.jetbrains.annotations.TestOnly +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo import org.jetbrains.r.psi.TableInfo @@ -31,8 +32,11 @@ interface RConsoleRuntimeInfo { fun loadExtraNamedArguments(functionName: String): RExtraNamedArgumentsInfo fun loadExtraNamedArguments(functionName: String, functionExpression: RFunctionExpression): RExtraNamedArgumentsInfo fun loadShortS4ClassInfos(): List + fun loadShortR6ClassInfos(): List fun loadS4ClassInfoByObjectName(objectName: String): RS4ClassInfo? fun loadS4ClassInfoByClassName(className: String): RS4ClassInfo? + fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? + fun loadR6ClassInfoByClassName(className: String): R6ClassInfo? fun getFormalArguments(expression: String) : List fun loadTableColumns(expression: String): TableInfo val rInterop: RInterop @@ -61,7 +65,10 @@ class RConsoleRuntimeInfoImpl(override val rInterop: RInterop) : RConsoleRuntime private val tableColumnsCache by rInterop.Cached { mutableMapOf() } private val s4ClassInfosByObjectNameCache by rInterop.Cached { mutableMapOf() } private val s4ClassInfosByClassNameCache by rInterop.Cached { mutableMapOf() } + private val r6ClassInfosByObjectNameCache by rInterop.Cached { mutableMapOf() } + private val r6ClassInfosByClassNameCache by rInterop.Cached { mutableMapOf() } private val loadedShortS4ClassInfosCache by rInterop.Cached { AtomicReference?>(null) } + private val loadedShortR6ClassInfosCache by rInterop.Cached { AtomicReference?>(null) } override val rMarkdownChunkOptions by lazy { rInterop.rMarkdownChunkOptions } @@ -133,6 +140,30 @@ class RConsoleRuntimeInfoImpl(override val rInterop: RInterop) : RConsoleRuntime } } + /** + * @return list of [R6ClassInfo] without information about [R6ClassInfo.fields], [R6ClassInfo.methods] and [R6ClassInfo.activeBindings] + */ + override fun loadShortR6ClassInfos(): List { + loadedShortR6ClassInfosCache.get().let { infos -> + if (infos != null) return infos + return rInterop.getLoadedShortR6ClassInfos().also { + loadedShortR6ClassInfosCache.set(it) + } ?: emptyList() + } + } + + override fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? { + return r6ClassInfosByObjectNameCache.getOrPut(objectName) { + rInterop.getR6ClassInfoByObjectName(RReference.expressionRef(objectName, rInterop)) + } + } + + override fun loadR6ClassInfoByClassName(className: String): R6ClassInfo? { + return r6ClassInfosByClassNameCache.getOrPut(className) { + rInterop.getR6ClassInfoByClassName(className) + } + } + override fun getFormalArguments(expression: String): List { return formalArgumentsCache.getOrPut(expression) { rInterop.getFormalArguments(RReference.expressionRef(expression, rInterop)) } } diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index d8c2d8462..e76be76ed 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -35,6 +35,10 @@ import io.grpc.stub.StreamObserver import org.jetbrains.annotations.TestOnly import org.jetbrains.concurrency.* import org.jetbrains.r.RBundle +import org.jetbrains.r.classes.r6.R6ClassActiveBinding +import org.jetbrains.r.classes.r6.R6ClassField +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.r6.R6ClassMethod import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassSlot import org.jetbrains.r.debugger.RDebuggerUtil @@ -871,6 +875,40 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler } } + /** + * @return list of [R6ClassInfo] without information about [R6ClassInfo.fields], [R6ClassInfo.methods] and [R6ClassInfo.activeBindings] + */ + fun getLoadedShortR6ClassInfos(): List? { + return try { + executeWithCheckCancel(asyncStub::getLoadedShortR6ClassInfos, Empty.getDefaultInstance()).shortR6ClassInfosList.map { + R6ClassInfo(it.name, it.`package`, "", emptyList(), emptyList(), emptyList()) + } + } catch (e: RInteropTerminated) { + null + } + } + + fun getR6ClassInfoByObjectName(ref: RReference): R6ClassInfo? { + return try { + val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) + R6ClassInfo(res.name, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + null + } catch (e: RInteropTerminated) { + null + } + } + + fun getR6ClassInfoByClassName(className: String): R6ClassInfo? { + return try { + val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByClassName, StringValue.of(className)) + R6ClassInfo(res.name, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + } catch (e: RInteropTerminated) { + null + } + } + fun getFormalArguments(function: RReference): List { return try { executeWithCheckCancel(asyncStub::getFormalArguments, function.proto).listList diff --git a/test/org/jetbrains/r/completion/AutoPopupTest.kt b/test/org/jetbrains/r/completion/AutoPopupTest.kt index df57a400e..7d7cc6288 100644 --- a/test/org/jetbrains/r/completion/AutoPopupTest.kt +++ b/test/org/jetbrains/r/completion/AutoPopupTest.kt @@ -10,6 +10,7 @@ import com.intellij.util.ThrowableRunnable import org.jetbrains.r.RFileType import org.jetbrains.r.RLightCodeInsightFixtureTestCase import org.jetbrains.r.classes.s4.RS4ClassInfo +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.console.RConsoleRuntimeInfo import org.jetbrains.r.console.addRuntimeInfo import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo @@ -198,6 +199,18 @@ class AutoPopupTest : RLightCodeInsightFixtureTestCase() { throw NotImplementedError() } + override fun loadShortR6ClassInfos(): List { + throw NotImplementedError() + } + + override fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? { + throw NotImplementedError() + } + + override fun loadR6ClassInfoByClassName(className: String): R6ClassInfo? { + throw NotImplementedError() + } + override fun loadExtraNamedArguments(functionName: String, functionExpression: RFunctionExpression): RExtraNamedArgumentsInfo { throw NotImplementedError() } From 36bca8b625d9439eb9ebb4e3c658c0e9981f5740 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 27 Mar 2021 20:35:08 +0300 Subject: [PATCH 10/52] inserting logic to RReferenceImpl.kt --- resources/META-INF/rplugin-common.xml | 1 + .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 10 +- .../context/R6CreateClassContextProvider.kt | 100 ++++++++++++++++++ .../r6/context/R6NewObjectContextProvider.kt | 41 +++---- .../r/psi/references/RReferenceImpl.kt | 8 +- .../r/skeleton/RSkeletonFileStubBuilder.kt | 31 +++--- 6 files changed, 149 insertions(+), 42 deletions(-) create mode 100644 src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index cd0ddd698..8500e56de 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -736,6 +736,7 @@ You can find the source code in the following repositories: + diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 9e70a8c00..9b406892f 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -19,11 +19,11 @@ object R6ClassInfoUtil { const val functionNew = "new" - private const val argumentClassName = "classname" - private const val argumentSuperClass = "inherit" - private const val argumentPublic = "public" - private const val argumentPrivate = "private" - private const val argumentActive = "active" + const val argumentClassName = "classname" + const val argumentSuperClass = "inherit" + const val argumentPublic = "public" + const val argumentPrivate = "private" + const val argumentActive = "active" private val INSTANTIATE_CLASS_DEFINITION_KEY: Key = Key.create("R6_INSTANTIATE_CLASS_DEFINITION") diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt new file mode 100644 index 000000000..ccc6bc610 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.classes.s4.context.* +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.psi.RPsiUtil +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RNamedArgument +import org.jetbrains.r.psi.api.RPsiElement +import org.jetbrains.r.psi.isFunctionFromLibrary + +sealed class R6CreateClassContext : R6Context { + override val functionName = "R6Class" +} + +// Accumulator <- R6Class("Accumulator", ...) +data class R6CreateClassNameContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6CreateClassContext() + +class R6CreateClassContextProvider : R6ContextProvider() { + override fun getR6Context(element: RPsiElement): R6CreateClassContext? { + return CachedValuesManager.getCachedValue(element) { + CachedValueProvider.Result.create(getR6ContextInner(element), element) + } + } + + private fun getR6ContextInner(element: RPsiElement): R6CreateClassContext? { + val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null + return if (parentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) { + val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null + when (element) { +// parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentClassName) -> { +// // R6Class("") +// R6CreateClassNameContext(element, parentCall, parentArgumentInfo) +// } + parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentSuperClass) -> { + // R6Class("MyClass", "") + R6CreateClassNameContext(element, parentCall, parentArgumentInfo) + } + parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) -> { + // R6Class("MyClass", , "") + R6CreateClassNameContext(element, parentCall, parentArgumentInfo) + } + parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate) -> { + // R6Class("MyClass", , , "") + R6CreateClassNameContext(element, parentCall, parentArgumentInfo) + } + parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive) -> { + // R6Class("MyClass", , , , "") + R6CreateClassNameContext(element, parentCall, parentArgumentInfo) + } + else -> null + } + } + else { + val superParentCall = PsiTreeUtil.getParentOfType(parentCall, RCallExpression::class.java) ?: return null + if (!superParentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) return null + val superParentArgumentInfo = RParameterInfoUtil.getArgumentInfo(superParentCall) ?: return null + + return when { + // R6Class("MyClass", inherit = "" + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentSuperClass), element, false) -> { + val parent = element.parent + if (parent is RNamedArgument && parent.nameIdentifier == element) null + else R6CreateClassNameContext(element, superParentCall, superParentArgumentInfo) + } + + // R6Class("MyClass", public = "" + // R6Class("MyClass", public = list("") + // R6Class("MyClass", public = list(smt = "") + + // R6Class("MyClass", private = "" + // R6Class("MyClass", private = list("") + // R6Class("MyClass", private = list(smt = "") + + // R6Class("MyClass", active = "" + // R6Class("MyClass", active = list("") + // R6Class("MyClass", active = list(smt = "") + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic), element, false) || + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate), element, false) || + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive), element, false) -> { + val parent = element.parent + if (parent !is RNamedArgument || parent.assignedValue != element) null + else R6CreateClassNameContext(element, superParentCall, superParentArgumentInfo) + } + + else -> null + } + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt index 5d25873c7..74f08a16f 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt @@ -8,6 +8,8 @@ import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.classes.s4.context.RS4NewObjectClassNameContext +import org.jetbrains.r.classes.s4.context.RS4NewObjectSlotNameContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.RPsiUtil @@ -22,8 +24,8 @@ sealed class R6NewObjectContext : R6Context { // Calculator <- Accumulator$new() data class R6NewObjectClassNameContext(override val originalElement: RPsiElement, - override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6NewObjectContext() + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6NewObjectContext() class R6NewObjectContextProvider : R6ContextProvider() { override fun getR6Context(element: RPsiElement): R6NewObjectContext? { @@ -37,22 +39,23 @@ class R6NewObjectContextProvider : R6ContextProvider() { if (!parentCall.isFunctionFromLibrary(R6ClassInfoUtil.functionNew, R6ClassInfoUtil.R6PackageName)) return null val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null - // TODO implement it normally - return if (parentArgumentInfo.getArgumentPassedToParameter("Class") == element) { - // new("") - R6NewObjectClassNameContext(element, parentCall, parentArgumentInfo) - } - else { - val currentArgument = - if (element.parent is RNamedArgument) RPsiUtil.getNamedArgumentByNameIdentifier(element) ?: return null - else element - val arguments = parentCall.argumentList.expressionList - if (!arguments.contains(currentArgument)) return null - else { - // new("ClassName", slot_name) - // new("ClassName", slot_name = slot_value) - R6NewObjectClassNameContext(currentArgument, parentCall, parentArgumentInfo) - } - } + return null + +// return if (parentArgumentInfo.getArgumentPassedToParameter() == element) { +// // MyClass$new() +// RS4NewObjectClassNameContext(element, parentCall, parentArgumentInfo) +// } +// else { +// val currentArgument = +// if (element.parent is RNamedArgument) RPsiUtil.getNamedArgumentByNameIdentifier(element) ?: return null +// else element +// val arguments = parentCall.argumentList.expressionList +// if (!arguments.contains(currentArgument)) return null +// else { +// // new("ClassName", slot_name) +// // new("ClassName", slot_name = slot_value) +// RS4NewObjectSlotNameContext(currentArgument, parentCall, parentArgumentInfo) +// } +// } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt index 2377a402b..45bbd03d5 100644 --- a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt +++ b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt @@ -31,7 +31,13 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase { val s4ClassRepresentation = symbol.s4ClassRepresentation RSkeletonCallExpressionStub(skeletonFileStub, @@ -56,26 +56,23 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { R6ClassInfo(symbol.name, r6ClassRepresentation.packageName, r6ClassRepresentation.superClass, - r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic ) }, + r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic) }, r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) } - } - - if (symbol.representationCase == RepresentationCase.S4CLASSREPRESENTATION) { - } - else { - val functionRepresentation = symbol.functionRepresentation - val extraNamedArguments = functionRepresentation.extraNamedArguments - RSkeletonAssignmentStub(skeletonFileStub, - R_SKELETON_ASSIGNMENT_STATEMENT, - symbol.name, - symbol.type, - functionRepresentation.parameters, - symbol.exported, - RExtraNamedArgumentsInfo(extraNamedArguments.argNamesList, - extraNamedArguments.funArgNamesList)) + else -> { + val functionRepresentation = symbol.functionRepresentation + val extraNamedArguments = functionRepresentation.extraNamedArguments + RSkeletonAssignmentStub(skeletonFileStub, + R_SKELETON_ASSIGNMENT_STATEMENT, + symbol.name, + symbol.type, + functionRepresentation.parameters, + symbol.exported, + RExtraNamedArgumentsInfo(extraNamedArguments.argNamesList, + extraNamedArguments.funArgNamesList)) + } } } return skeletonFileStub From 64b61dd0fd454726a081e4a389af890b4d0a7c38 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 27 Mar 2021 20:38:49 +0300 Subject: [PATCH 11/52] tests classes refactor and add r6 tests (not ready) --- .../r/completion/R6ClassCompletionTest.kt | 8 +++ .../r/findUsages/FindUsagesTestBase.kt | 29 +++++++++++ .../r/findUsages/R6FindUsagesTest.kt | 30 +++++++++++ ...UsagesTest.kt => RCommonFindUsagesTest.kt} | 51 +++++-------------- 4 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt create mode 100644 test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt rename test/org/jetbrains/r/findUsages/{RFindUsagesTest.kt => RCommonFindUsagesTest.kt} (71%) diff --git a/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt index 0b53ed27a..fc7e2c39a 100644 --- a/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt @@ -18,6 +18,14 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { addLibraries() } + fun testUserClassWithSingleSlot() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0 )) + obj <- MyClass${"$"}new() + obj$ + """.trimIndent(), "someSlot" to "ANY") + } + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { val result = doTestBase(text, withRuntimeInfo, inConsole) assertNotNull(result) diff --git a/test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt b/test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt new file mode 100644 index 000000000..1d56e43b1 --- /dev/null +++ b/test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.findUsages + +import com.intellij.testFramework.UsefulTestCase +import com.intellij.usages.PsiElementUsageTarget +import com.intellij.usages.UsageTargetUtil +import org.intellij.lang.annotations.Language +import org.jetbrains.r.run.RProcessHandlerBaseTestCase + +abstract class FindUsagesTestBase : RProcessHandlerBaseTestCase() { + override fun setUp() { + super.setUp() + addLibraries() + } + + fun doTest(@Language("R") code: String, expected: String) { + myFixture.configureByText("test.R", code.trimIndent()) + val element = myFixture.elementAtCaret + val targets = UsageTargetUtil.findUsageTargets(element) + assertNotNull(targets) + assertTrue(targets.size > 0) + val target = (targets[0] as PsiElementUsageTarget).element + val actual = myFixture.getUsageViewTreeTextRepresentation(target) + UsefulTestCase.assertSameLines(expected.trimIndent(), actual) + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt new file mode 100644 index 000000000..d3eb3efee --- /dev/null +++ b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.findUsages + +class R6FindUsagesTest : FindUsagesTestBase() { + override fun setUp() { + super.setUp() + addLibraries() + } + + fun testR6ClassField() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0 )) + obj <- MyClass${'$'}new() + obj${'$'}someField + """, """ + Usage (2 usages) + Variable + my.local.variable + Found usages (2 usages) + Unclassified (2 usages) + light_idea_test_case (2 usages) + (2 usages) + 2print(my.local.variable) + 5print(my.local.variable + 20) + """) + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/findUsages/RFindUsagesTest.kt b/test/org/jetbrains/r/findUsages/RCommonFindUsagesTest.kt similarity index 71% rename from test/org/jetbrains/r/findUsages/RFindUsagesTest.kt rename to test/org/jetbrains/r/findUsages/RCommonFindUsagesTest.kt index 5447c3733..4d6ed63e5 100644 --- a/test/org/jetbrains/r/findUsages/RFindUsagesTest.kt +++ b/test/org/jetbrains/r/findUsages/RCommonFindUsagesTest.kt @@ -10,13 +10,7 @@ import com.intellij.usages.UsageTargetUtil import org.intellij.lang.annotations.Language import org.jetbrains.r.run.RProcessHandlerBaseTestCase -class RFindUsagesTest : RProcessHandlerBaseTestCase() { - - override fun setUp() { - super.setUp() - addLibraries() - } - +class RCommonFindUsagesTest : FindUsagesTestBase() { fun testLocalVariable() { doTest(""" my.local.variable <- 10 @@ -33,9 +27,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 2print(my.local.variable) - 5print(my.local.variable + 20) + 2print(my.local.variable) + 5print(my.local.variable + 20) """) } @@ -56,9 +49,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 2print(my.local.function(2, 3)) - 5print(my.local.function(4, 5)) + 2print(my.local.function(2, 3)) + 5print(my.local.function(4, 5)) """) } @@ -74,9 +66,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 1base.package <- packageDescription("base") - 2dplyr.package <- packageDescription("dplyr") + 1base.package <- packageDescription("base") + 2dplyr.package <- packageDescription("dplyr") """) } @@ -97,9 +88,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 2x + y + z - 7func(x = p) + 2x + y + z + 7func(x = p) """) } @@ -124,13 +114,11 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 10x + y + z + 10x + y + z Usage in roxygen2 documentation (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 5#' @param x, y X and y + 5#' @param x, y X and y """) } @@ -157,24 +145,11 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 10bar(x) + y + z + 10bar(x) + y + z Usage in roxygen2 documentation (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 5#' @see [bar] + 5#' @see [bar] """) } - - private fun doTest(@Language("R") code: String, expected: String) { - myFixture.configureByText("test.R", code.trimIndent()) - val element = myFixture.elementAtCaret - val targets = UsageTargetUtil.findUsageTargets(element) - assertNotNull(targets) - assertTrue(targets.size > 0) - val target = (targets[0] as PsiElementUsageTarget).element - val actual = myFixture.getUsageViewTreeTextRepresentation(target) - UsefulTestCase.assertSameLines(expected.trimIndent(), actual) - } } \ No newline at end of file From 2532669afb7b7c00b578b879292de23435395e57 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 1 Apr 2021 22:11:57 +0300 Subject: [PATCH 12/52] trying to build references impl --- .../r/psi/references/RReferenceImpl.kt | 23 +++++++++++++++---- src/org/jetbrains/r/rinterop/RInterop.kt | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt index 45bbd03d5..86907c31c 100644 --- a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt +++ b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt @@ -9,10 +9,12 @@ import com.intellij.openapi.util.Ref import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementResolveResult import com.intellij.psi.ResolveResult +import com.intellij.psi.util.elementType import com.intellij.util.IncorrectOperationException import com.intellij.util.Processor import org.jetbrains.r.codeInsight.libraries.RLibrarySupportProvider import org.jetbrains.r.codeInsight.table.RTableContextManager +import org.jetbrains.r.parsing.RElementTypes.R_MEMBER_EXPRESSION import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement @@ -32,11 +34,7 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase { + val objectDependantIdentifierCall = element.parent + + when (objectDependantIdentifierCall.elementType) { + R_MEMBER_EXPRESSION -> { + val objectName = objectDependantIdentifierCall.firstChild + val classDeclarationStatement = objectName.reference?.resolve() + + print(classDeclarationStatement) + } + } + + return emptyArray() + } + @Throws(IncorrectOperationException::class) override fun handleElementRename(newElementName: String): PsiElement? { return element.setName(newElementName) diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index e76be76ed..22a066567 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -891,7 +891,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByObjectName(ref: RReference): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) - R6ClassInfo(res.name, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + R6ClassInfo(res.className, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) null } catch (e: RInteropTerminated) { @@ -902,7 +902,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByClassName(className: String): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByClassName, StringValue.of(className)) - R6ClassInfo(res.name, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + R6ClassInfo(res.className, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) } catch (e: RInteropTerminated) { null From 3db2904475f8863d6a5d58417090485b7544d016 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 3 Apr 2021 21:11:37 +0300 Subject: [PATCH 13/52] find usages from used class members working --- .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 86 +++++++++++++++++++ .../r/psi/references/RReferenceImpl.kt | 33 +++---- .../r/findUsages/R6FindUsagesTest.kt | 53 ++++++++++-- 3 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt new file mode 100644 index 000000000..79fc8abe6 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.psi.util.elementType +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.parsing.RElementTypes.R_MEMBER_EXPRESSION +import org.jetbrains.r.psi.api.* +import org.jetbrains.r.psi.impl.RCallExpressionImpl + +object R6ClassPsiUtil { + + /** + * @param dependantIdentifier `someMember` psi-element of expression `obj$someMember` + * @return RPsiElement with name of `someMember` + */ + fun getSearchedIdentifier(dependantIdentifier: RIdentifierExpression?) : RPsiElement? { + if (dependantIdentifier == null) return null + + val objectDeclarationStatement = getClassInstantiationExpression(dependantIdentifier) + val classDefinition = getClassDefinitionExpression(objectDeclarationStatement) + val argumentInfo = getClassDefinitionArgumentInfo(classDefinition) + + val publicMembersCall = argumentInfo?.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) as? RCallExpressionImpl + val privateMembersCall = argumentInfo?.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate) as? RCallExpressionImpl + val activeMembersCall = argumentInfo?.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive) as? RCallExpressionImpl + + val publicMembers = publicMembersCall?.argumentList?.expressionList + val privateMembers = privateMembersCall?.argumentList?.expressionList + val activeMembers = activeMembersCall?.argumentList?.expressionList + + return extractNamedArgumentByName(dependantIdentifier.name, publicMembers?.map { it as RNamedArgument }) + ?: extractNamedArgumentByName(dependantIdentifier.name, privateMembers?.map { it as RNamedArgument }) + ?: extractNamedArgumentByName(dependantIdentifier.name, activeMembers?.map { it as RNamedArgument }) + } + + /** + * @param dependantIdentifier `someMember` psi-element of expression `obj$someMember` + * @return RAssignmentStatement `obj <- MyClass$new()` + */ + private fun getClassInstantiationExpression(dependantIdentifier: RIdentifierExpression?): RAssignmentStatement? { + if (dependantIdentifier == null) return null + + val usageExpression = dependantIdentifier.parent + if (usageExpression.elementType != R_MEMBER_EXPRESSION) return null + + return usageExpression.firstChild.reference?.resolve() as RAssignmentStatement + } + + /** + * @param objectInstantiationCall RAssignmentStatement expression `obj <- MyClass$new()` + * @return class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` + */ + private fun getClassDefinitionExpression(objectInstantiationCall: RAssignmentStatement?): RAssignmentStatement? { + if (objectInstantiationCall == null) return null + + val objectCreationCall = objectInstantiationCall.lastChild // MyClass$new() + val classElement = objectCreationCall?.firstChild?.firstChild // MyClass + + return classElement?.reference?.resolve() as? RAssignmentStatement + } + + /** + * @param classDefinition class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` + * @return argument info containing all internal members of class + */ + private fun getClassDefinitionArgumentInfo(classDefinition: RAssignmentStatement?) : RArgumentInfo? { + if (classDefinition == null) return null + + val r6ClassCall = classDefinition.children?.last() as RCallExpression + return RParameterInfoUtil.getArgumentInfo(r6ClassCall) + } + + private fun extractNamedArgumentByName(elementName: String, namedArguments: List?) : RPsiElement? { + namedArguments?.forEach { + if (it != null) { + if (it.name == elementName) return it + } + } + + return null + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt index 86907c31c..340a1d881 100644 --- a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt +++ b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt @@ -9,15 +9,15 @@ import com.intellij.openapi.util.Ref import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementResolveResult import com.intellij.psi.ResolveResult -import com.intellij.psi.util.elementType import com.intellij.util.IncorrectOperationException import com.intellij.util.Processor +import org.jetbrains.r.classes.r6.R6ClassPsiUtil import org.jetbrains.r.codeInsight.libraries.RLibrarySupportProvider import org.jetbrains.r.codeInsight.table.RTableContextManager -import org.jetbrains.r.parsing.RElementTypes.R_MEMBER_EXPRESSION import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement +import java.util.ArrayList class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase(element) { @@ -34,13 +34,13 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase() val resolveProcessor = object : Processor { override fun process(it: TableColumnInfo): Boolean { @@ -67,13 +67,11 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase parameter.name == element.name}?.let { PsiElementResolveResult(it) } + assignment.getParameters().firstOrNull { parameter -> parameter.name == element.name }?.let { PsiElementResolveResult(it) } }.toTypedArray() } - private fun resolveDependantIdentifier(isIncomplete: Boolean): Array { - val objectDependantIdentifierCall = element.parent - - when (objectDependantIdentifierCall.elementType) { - R_MEMBER_EXPRESSION -> { - val objectName = objectDependantIdentifierCall.firstChild - val classDeclarationStatement = objectName.reference?.resolve() - - print(classDeclarationStatement) - } - } + private fun resolveDependantIdentifier(): Array { + val r6SearchedIdentifier = R6ClassPsiUtil.getSearchedIdentifier(element) ?: return emptyArray() - return emptyArray() + val result = ArrayList() + result.add(PsiElementResolveResult(r6SearchedIdentifier)) + return result.toTypedArray() } @Throws(IncorrectOperationException::class) diff --git a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt index d3eb3efee..3dea58cad 100644 --- a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt +++ b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt @@ -10,21 +10,60 @@ class R6FindUsagesTest : FindUsagesTestBase() { addLibraries() } - fun testR6ClassField() { + fun testR6ClassFieldFromUsage() { doTest(""" - MyClass <- R6Class("MyClass", list( someField = 0 )) - obj <- MyClass${'$'}new() - obj${'$'}someField + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someField + obj${'$'}someField """, """ Usage (2 usages) Variable - my.local.variable + someField = 0 Found usages (2 usages) Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - 2print(my.local.variable) - 5print(my.local.variable + 20) + 3obj${"$"}someField + 4obj${"$"}someField + """) + } + + fun testR6ClassFunctionFromUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someMethod() + obj${'$'}someField + """, """ + Usage (1 usage) + Variable + someMethod = function(x = 1) { print(x) } + Found usages (1 usage) + Unclassified (1 usage) + light_idea_test_case (1 usage) + (1 usage) + 3obj${"$"}someMethod() + """) + } + + /// Not working due to picked target element is `list(...)` and not `someField` + fun testR6ClassFieldFromDefinition() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someField + obj${'$'}someField + """, """ + Usage (2 usages) + Variable + someField = 0 + Found usages (2 usages) + Unclassified (2 usages) + light_idea_test_case (2 usages) + (2 usages) + 3obj${"$"}someField + 4obj${"$"}someField """) } } \ No newline at end of file From 38a12c103c11e9f2cc6a2cae42356c64bcea2836 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 3 Apr 2021 22:39:50 +0300 Subject: [PATCH 14/52] fix faster return from processing searched identifier --- src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt index 79fc8abe6..cd1e8b237 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -20,13 +20,13 @@ object R6ClassPsiUtil { fun getSearchedIdentifier(dependantIdentifier: RIdentifierExpression?) : RPsiElement? { if (dependantIdentifier == null) return null - val objectDeclarationStatement = getClassInstantiationExpression(dependantIdentifier) - val classDefinition = getClassDefinitionExpression(objectDeclarationStatement) - val argumentInfo = getClassDefinitionArgumentInfo(classDefinition) + val objectDeclarationStatement = getClassInstantiationExpression(dependantIdentifier) ?: return null + val classDefinition = getClassDefinitionExpression(objectDeclarationStatement) ?: return null + val argumentInfo = getClassDefinitionArgumentInfo(classDefinition) ?: return null - val publicMembersCall = argumentInfo?.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) as? RCallExpressionImpl - val privateMembersCall = argumentInfo?.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate) as? RCallExpressionImpl - val activeMembersCall = argumentInfo?.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive) as? RCallExpressionImpl + val publicMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) as? RCallExpressionImpl + val privateMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate) as? RCallExpressionImpl + val activeMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive) as? RCallExpressionImpl val publicMembers = publicMembersCall?.argumentList?.expressionList val privateMembers = privateMembersCall?.argumentList?.expressionList From 838d359030b80470a35b20b9e8b339da3eb75af2 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 4 Apr 2021 00:30:27 +0300 Subject: [PATCH 15/52] all other symbols placement handled --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 1 + .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 38 ++++++++++++-- .../r/findUsages/R6FindUsagesTest.kt | 50 ++++++++++++++++++- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 9b406892f..88bcc1f83 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -17,6 +17,7 @@ object R6ClassInfoUtil { const val R6PackageName = "R6" const val R6CreateClassMethod = "R6Class" + const val R6ClassThisKeyword = "self" const val functionNew = "new" const val argumentClassName = "classname" diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt index cd1e8b237..9fbb6e2bd 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -7,9 +7,11 @@ package org.jetbrains.r.classes.r6 import com.intellij.psi.util.elementType import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.parsing.RElementTypes.R_IDENTIFIER_EXPRESSION import org.jetbrains.r.parsing.RElementTypes.R_MEMBER_EXPRESSION import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl +import org.jetbrains.r.psi.isFunctionFromLibrarySoft object R6ClassPsiUtil { @@ -20,8 +22,8 @@ object R6ClassPsiUtil { fun getSearchedIdentifier(dependantIdentifier: RIdentifierExpression?) : RPsiElement? { if (dependantIdentifier == null) return null - val objectDeclarationStatement = getClassInstantiationExpression(dependantIdentifier) ?: return null - val classDefinition = getClassDefinitionExpression(objectDeclarationStatement) ?: return null + val objectDeclarationStatement = getClassInstantiationExpression(dependantIdentifier) + val classDefinition = getClassDefinitionExpression(dependantIdentifier, objectDeclarationStatement) ?: return null val argumentInfo = getClassDefinitionArgumentInfo(classDefinition) ?: return null val publicMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) as? RCallExpressionImpl @@ -47,16 +49,42 @@ object R6ClassPsiUtil { val usageExpression = dependantIdentifier.parent if (usageExpression.elementType != R_MEMBER_EXPRESSION) return null - return usageExpression.firstChild.reference?.resolve() as RAssignmentStatement + var r6Object = usageExpression.firstChild + while (r6Object != null && r6Object.elementType != R_IDENTIFIER_EXPRESSION){ + r6Object = r6Object.firstChild + } + + if (r6Object == null) return null + return r6Object.reference?.resolve() as? RAssignmentStatement } /** + * @param dependantIdentifier `someMember` psi-element of expression `obj$someMember` * @param objectInstantiationCall RAssignmentStatement expression `obj <- MyClass$new()` * @return class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` */ - private fun getClassDefinitionExpression(objectInstantiationCall: RAssignmentStatement?): RAssignmentStatement? { - if (objectInstantiationCall == null) return null + private fun getClassDefinitionExpression(dependantIdentifier: RIdentifierExpression?, objectInstantiationCall: RAssignmentStatement?): RAssignmentStatement? { + if (dependantIdentifier == null) return null + + // handling search request from inside of class usage with `self$field` + if (objectInstantiationCall == null) { + if (dependantIdentifier.parent?.firstChild?.text == R6ClassInfoUtil.R6ClassThisKeyword){ + var currentParent = dependantIdentifier.parent + var currentCall = currentParent as? RCallExpression + + while (dependantIdentifier.parent != null){ + if (currentCall?.isFunctionFromLibrarySoft(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName) == true) break + currentParent = currentParent.parent + currentCall = currentParent as? RCallExpression + } + + return currentCall?.parent as? RAssignmentStatement + } + + return null + } + // handling search request from classic out-of-class-definition usage val objectCreationCall = objectInstantiationCall.lastChild // MyClass$new() val classElement = objectCreationCall?.firstChild?.firstChild // MyClass diff --git a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt index 3dea58cad..befbc9fb7 100644 --- a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt +++ b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt @@ -10,7 +10,7 @@ class R6FindUsagesTest : FindUsagesTestBase() { addLibraries() } - fun testR6ClassFieldFromUsage() { + fun testR6ClassFieldOutOfClassUsage() { doTest(""" MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) obj <- MyClass${'$'}new() @@ -29,7 +29,7 @@ class R6FindUsagesTest : FindUsagesTestBase() { """) } - fun testR6ClassFunctionFromUsage() { + fun testR6ClassFunctionOutOfClassUsage() { doTest(""" MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) obj <- MyClass${'$'}new() @@ -47,6 +47,52 @@ class R6FindUsagesTest : FindUsagesTestBase() { """) } + fun testR6ClassFieldOutOfClassChainedUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someField + obj${'$'}someMethod()${'$'}someField + """, """ + Usage (2 usages) + Variable + someField = 0 + Found usages (2 usages) + Unclassified (2 usages) + light_idea_test_case (2 usages) + (2 usages) + 3obj${'$'}someField + 4obj${'$'}someMethod()${'$'}someField + """) + } + + fun testR6ClassInsideMethodFieldUsage(){ + doTest(""" + Accumulator <- R6Class("Accumulator", list( + sum = 0, + add = function(x = 1) { + self${"$"}sum <- self${"$"}sum + x + invisible(self) + }) + ) + + x <- Accumulator${"$"}new() + x${"$"}add(4)${"$"}sum + x${"$"}sum + """, """ + Usage (3 usages) + Variable + sum = 0 + Found usages (3 usages) + Unclassified (3 usages) + light_idea_test_case (3 usages) + (3 usages) + 4self${'$'}sum <- self${'$'}sum + x + 10x${'$'}add(4)${'$'}sum + 11x${'$'}sum + """) + } + /// Not working due to picked target element is `list(...)` and not `someField` fun testR6ClassFieldFromDefinition() { doTest(""" From ead0f81aa5b64e975c2250e211c4ed7b78d7f097 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 4 Apr 2021 16:47:57 +0300 Subject: [PATCH 16/52] implemented completion for r6ClassMembers. Tested statically (not runtime) --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 7 +- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 49 +++++++++-- .../r6/context/R6SetClassContextProvider.kt | 88 ------------------- .../r/editor/RCompletionContributor.kt | 58 +++++++++++- .../r/classes/r6/R6ClassInfoUtilTests.kt | 11 +++ .../{ => classes}/R6ClassCompletionTest.kt | 14 ++- .../{ => classes}/S4ClassCompletionTest.kt | 2 +- 7 files changed, 122 insertions(+), 107 deletions(-) delete mode 100644 src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt rename test/org/jetbrains/r/completion/{ => classes}/R6ClassCompletionTest.kt (84%) rename test/org/jetbrains/r/completion/{ => classes}/S4ClassCompletionTest.kt (99%) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index 2fb2a2fd9..c7d4dd6b0 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -11,9 +11,10 @@ import com.intellij.util.io.StringRef // no need to care about overloads because R6 lib doesn't support it: // "All items in public, private, and active must have unique names." -data class R6ClassField(val name: String, val isPublic: Boolean = true) -data class R6ClassMethod(val name: String, val isPublic: Boolean = true) -data class R6ClassActiveBinding(val name: String) +interface R6ClassMember { val name: String } +data class R6ClassField(override val name: String, val isPublic: Boolean = true) : R6ClassMember +data class R6ClassMethod(override val name: String, val isPublic: Boolean = true) : R6ClassMember +data class R6ClassActiveBinding(override val name: String) : R6ClassMember data class R6ClassInfo(val className: String, val packageName: String, diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 88bcc1f83..a99d673aa 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -11,7 +11,9 @@ import org.jetbrains.r.packages.RPackageProjectManager import org.jetbrains.r.psi.RElementFactory import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl +import org.jetbrains.r.psi.impl.RMemberExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft +import java.util.* object R6ClassInfoUtil { const val R6PackageName = "R6" @@ -34,6 +36,12 @@ object R6ClassInfoUtil { portable = TRUE, lock_class = FALSE, cloneable = TRUE, parent_env = parent.frame(), lock) {}""".trimIndent() + fun getAssociatedClassNameFromInstantiationCall(call: RCallExpression) : String? { + val callExpression = call.expression as? RMemberExpressionImpl ?: return null + if (callExpression.rightExpr?.text != functionNew) return null + return callExpression.leftExpr?.text + } + fun getAssociatedClassName(callExpression: RCallExpression, argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { argumentInfo ?: return null @@ -49,31 +57,56 @@ object R6ClassInfoUtil { return (argumentInfo.getArgumentPassedToParameter(argumentSuperClass) as? RIdentifierExpression)?.name } + fun getAllClassMembers(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false) : MutableList { + val r6ClassMembers = mutableListOf() + + val fields = getAssociatedFields(callExpression, argumentInfo, onlyPublic) + if (!fields.isNullOrEmpty()) r6ClassMembers.addAll(fields) + + val functions = getAssociatedMethods(callExpression, argumentInfo, onlyPublic) + if (!functions.isNullOrEmpty()) r6ClassMembers.addAll(functions) + + val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) + if (!activeBindings.isNullOrEmpty()) r6ClassMembers.addAll(activeBindings) + + return r6ClassMembers + } + fun getAssociatedFields(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null val r6ClassFields = mutableListOf() val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList - val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) - if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) + + if (!onlyPublic) { + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) + } + return r6ClassFields } fun getAssociatedMethods(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null val r6ClassMethods = mutableListOf() val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList - val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) - if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) + + if (!onlyPublic) { + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) + } + return r6ClassMethods } diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt deleted file mode 100644 index b916e2a6f..000000000 --- a/src/org/jetbrains/r/classes/r6/context/R6SetClassContextProvider.kt +++ /dev/null @@ -1,88 +0,0 @@ -package org.jetbrains.r.classes.r6.context - -import com.intellij.psi.util.CachedValueProvider -import com.intellij.psi.util.CachedValuesManager -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.r.hints.parameterInfo.RArgumentInfo -import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil -import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.api.RNamedArgument -import org.jetbrains.r.psi.api.RPsiElement -import org.jetbrains.r.psi.isFunctionFromLibrary - -sealed class R6SetClassContext : R6Context { - override val functionName = "setClass" -} - -// setClass("MyClass", "") -data class R6SetClassRepresentationContext(override val originalElement: RPsiElement, - override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6SetClassContext() - -// setClass("MyClass", , , "") -data class R6SetClassContainsContext(override val originalElement: RPsiElement, - override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6SetClassContext() - -// setClass("MyClass", contains = "") -// setClass("MyClass", contains = c("")) -// setClass("MyClass", contains = c(smt = "") -// setClass("MyClass", representation = representation(smt = "") -// setClass("MyClass", representation = representation("") -// setClass("MyClass", slots = c(name = "")) -data class R6SetClassDependencyClassNameContext(override val originalElement: RPsiElement, - override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6SetClassContext() - -class R6SetClassContextProvider : R6ContextProvider() { - override fun getR6Context(element: RPsiElement): R6SetClassContext? { - return CachedValuesManager.getCachedValue(element) { - CachedValueProvider.Result.create(getR6ContextInner(element), element) - } - } - - private fun getR6ContextInner(element: RPsiElement): R6SetClassContext? { - val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null - return if (parentCall.isFunctionFromLibrary("setClass", "methods")) { - val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null - when (element) { - parentArgumentInfo.getArgumentPassedToParameter("representation") -> { - // setClass("MyClass", "") - R6SetClassRepresentationContext(element, parentCall, parentArgumentInfo) - } - parentArgumentInfo.getArgumentPassedToParameter("contains") -> { - // setClass("MyClass", , , "") - R6SetClassContainsContext(element, parentCall, parentArgumentInfo) - } - else -> null - } - } - else { - val superParentCall = PsiTreeUtil.getParentOfType(parentCall, RCallExpression::class.java) ?: return null - if (!superParentCall.isFunctionFromLibrary("setClass", "methods")) return null - - val superParentArgumentInfo = RParameterInfoUtil.getArgumentInfo(superParentCall) ?: return null - return when { - // setClass("MyClass", contains = "") - // setClass("MyClass", contains = c("")) - // setClass("MyClass", contains = c(smt = "") - // setClass("MyClass", representation = representation(smt = "") - // setClass("MyClass", representation = representation("") - PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter("contains"), element, false) || - PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter("representation"), element, false) -> { - val parent = element.parent - if (parent is RNamedArgument && parent.nameIdentifier == element) null - else R6SetClassDependencyClassNameContext(element, superParentCall, superParentArgumentInfo) - } - - // setClass("MyClass", slots = c(name = "")) - PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter("slots"), element, false) -> { - val parent = element.parent - if (parent !is RNamedArgument || parent.assignedValue != element) null - else R6SetClassDependencyClassNameContext(element, superParentCall, superParentArgumentInfo) - } - else -> null - } - } - } -} diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 59d5d6580..4e1712c5e 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -20,6 +20,11 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.r.RLanguage +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.classes.r6.R6ClassMember +import org.jetbrains.r.classes.r6.context.R6ContextProvider +import org.jetbrains.r.classes.r6.context.R6CreateClassContext +import org.jetbrains.r.classes.r6.context.R6NewObjectContext import org.jetbrains.r.classes.s4.* import org.jetbrains.r.classes.s4.context.* import org.jetbrains.r.codeInsight.libraries.RLibrarySupportProvider @@ -35,6 +40,7 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.R6ClassNameIndex import org.jetbrains.r.psi.stubs.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator import org.jetbrains.r.rinterop.RValueFunction @@ -120,6 +126,46 @@ class RCompletionContributor : CompletionContributor() { for (extension in RLibrarySupportProvider.EP_NAME.extensions) { extension.completeMembers(leftExpr, rCompletionElementFactory, result) } + + addStaticRuntimeCompletionDependsOfFile(memberAccess, file, result, MemberStaticRuntimeCompletionProvider) + } + + private object MemberStaticRuntimeCompletionProvider : RStaticRuntimeCompletionProvider { + override fun addCompletionFromRuntime(psiElement: RMemberExpression, + shownNames: MutableSet, + result: CompletionResultSet, + runtimeInfo: RConsoleRuntimeInfo): Boolean { + TODO("Not yet implemented") + } + + override fun addCompletionStatically(psiElement: RMemberExpression, + shownNames: MutableSet, + result: CompletionResultSet): Boolean { + psiElement.leftExpr?.reference?.multiResolve(false)?.forEach { resolveResult -> + val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach + (definition.assignedValue as? RCallExpression)?.let { call -> + val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach + R6ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + return addSlotsCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) + } + } + } + return false + } + + private fun addSlotsCompletion(r6ClassMembers: List?, shownNames: MutableSet, result: CompletionResultSet): Boolean { + var hasNewResults = false + if (r6ClassMembers.isNullOrEmpty()) return hasNewResults + + for (r6Member in r6ClassMembers) { + if (r6Member.name in shownNames) continue + result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + shownNames.add(r6Member.name) + hasNewResults = true + } + + return hasNewResults + } } } @@ -561,15 +607,19 @@ class RCompletionContributor : CompletionContributor() { val expression = PsiTreeUtil.getParentOfType(parameters.position, RExpression::class.java, false) ?: return val file = parameters.originalFile addR6ClassNameCompletion(expression, file, result) - addR6SlotNameCompletion(expression, file, result) + addR6MemberNameCompletion(expression, file, result) } private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - // TODO fill completion + val r6Context = R6ContextProvider.getR6Context(classNameExpression, + R6NewObjectContext::class.java, + R6CreateClassContext::class.java) ?: return } - private fun addR6SlotNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - // TODO fill completion + private fun addR6MemberNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ + val r6Context = R6ContextProvider.getR6Context(classNameExpression, + R6NewObjectContext::class.java, + R6CreateClassContext::class.java) ?: return } } diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index 358ab3e47..d05dc0d45 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -7,6 +7,7 @@ package org.jetbrains.r.classes.r6 import junit.framework.TestCase import org.jetbrains.r.classes.RClassesUtilTestsBase import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RCallExpression class R6ClassInfoUtilTests : RClassesUtilTestsBase() { private val fullClassCodeDefinition = """ @@ -69,6 +70,16 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { assertEquals("Car", className) } + fun testGetAssociatedClassNameFromInstantiationCall(){ + val rAssignmentStatement = getRootElementOfPsi(""" + obj <- MyClass${'$'}new() + """.trimIndent()) as RAssignmentStatement + + val call = rAssignmentStatement.lastChild as RCallExpression + val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) + assertEquals("MyClass", className) + } + fun testGetAssociatedSuperClassName(){ val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) diff --git a/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt similarity index 84% rename from test/org/jetbrains/r/completion/R6ClassCompletionTest.kt rename to test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index fc7e2c39a..f7201d902 100644 --- a/test/org/jetbrains/r/completion/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -2,7 +2,7 @@ * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.completion +package org.jetbrains.r.completion.classes import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementPresentation @@ -18,12 +18,20 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { addLibraries() } - fun testUserClassWithSingleSlot() { + fun testUserClassWithSingleField() { doTest(""" MyClass <- R6Class("MyClass", list( someField = 0 )) obj <- MyClass${"$"}new() obj$ - """.trimIndent(), "someSlot" to "ANY") + """.trimIndent(), "someField" to "") + } + + fun testUserClassWithSeveralMembers() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) } )) + obj <- MyClass${"$"}new() + obj$ + """.trimIndent(), "someField" to "", "someMethod" to "") } private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { diff --git a/test/org/jetbrains/r/completion/S4ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt similarity index 99% rename from test/org/jetbrains/r/completion/S4ClassCompletionTest.kt rename to test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt index e8efaca00..9a7453aa3 100644 --- a/test/org/jetbrains/r/completion/S4ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt @@ -2,7 +2,7 @@ * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.completion +package org.jetbrains.r.completion.classes import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementPresentation From 51737dd64e71907d117a2548e2279505fb83b779 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Mon, 5 Apr 2021 12:58:56 +0300 Subject: [PATCH 17/52] temporary directory for sharing other configuration files (service.proto). --- temporary/service.proto | 923 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 923 insertions(+) create mode 100644 temporary/service.proto diff --git a/temporary/service.proto b/temporary/service.proto new file mode 100644 index 000000000..0897c69ea --- /dev/null +++ b/temporary/service.proto @@ -0,0 +1,923 @@ +/* + * Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +syntax = "proto3"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +option java_package = "org.jetbrains.r.rinterop"; +option java_multiple_files = true; + +package rplugininterop; + +service RPIService { + rpc getInfo(google.protobuf.Empty) returns (GetInfoResponse) {} + rpc isBusy(google.protobuf.Empty) returns (google.protobuf.BoolValue) {} + + rpc init(Init) returns (stream CommandOutput) {} + rpc quit(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc quitProceed(google.protobuf.Empty) returns (google.protobuf.Empty) {} + + rpc executeCode(ExecuteCodeRequest) returns (stream ExecuteCodeResponse) {} + rpc sendReadLn(google.protobuf.StringValue) returns (google.protobuf.Empty) {} + rpc sendEof(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc replInterrupt(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc getAsyncEvents(google.protobuf.Empty) returns (stream AsyncEvent) {} + + // Debugger + rpc debugAddOrModifyBreakpoint(DebugAddOrModifyBreakpointRequest) returns (google.protobuf.Empty) {} + rpc debugSetMasterBreakpoint(DebugSetMasterBreakpointRequest) returns (google.protobuf.Empty) {} + rpc debugRemoveBreakpoint(google.protobuf.Int32Value) returns (google.protobuf.Empty) {} + rpc debugCommandContinue(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandPause(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandStop(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandStepOver(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandStepInto(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandStepIntoMyCode(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandStepOut(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc debugCommandRunToPosition(SourcePosition) returns (google.protobuf.Empty) {} + rpc debugMuteBreakpoints(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} + rpc getFunctionSourcePosition(RRef) returns (GetFunctionSourcePositionResponse) {} + rpc getSourceFileText(google.protobuf.StringValue) returns (google.protobuf.StringValue) {} + rpc getSourceFileName(google.protobuf.StringValue) returns (google.protobuf.StringValue) {} + + // Graphics device service points + rpc graphicsInit(GraphicsInitRequest) returns (stream CommandOutput) {} + rpc graphicsDump(google.protobuf.Empty) returns (GraphicsDumpResponse) {} + rpc graphicsRescale(GraphicsRescaleRequest) returns (stream CommandOutput) {} + rpc graphicsRescaleStored(GraphicsRescaleStoredRequest) returns (stream CommandOutput) {} + rpc graphicsSetParameters(ScreenParameters) returns (google.protobuf.Empty) {} + rpc graphicsGetSnapshotPath(GraphicsGetSnapshotPathRequest) returns (GraphicsGetSnapshotPathResponse) {} + rpc graphicsFetchPlot(google.protobuf.Int32Value) returns (GraphicsFetchPlotResponse) {} + rpc graphicsCreateGroup(google.protobuf.Empty) returns (stream CommandOutput) {} + rpc graphicsRemoveGroup(google.protobuf.StringValue) returns (stream CommandOutput) {} + rpc graphicsShutdown(google.protobuf.Empty) returns (stream CommandOutput) {} + + // RMarkdown chunks + rpc beforeChunkExecution(ChunkParameters) returns (stream CommandOutput) {} + rpc afterChunkExecution(google.protobuf.Empty) returns (stream CommandOutput) {} + rpc pullChunkOutputPaths(google.protobuf.Empty) returns (StringList) {} + + // Repo utils service points + rpc repoGetPackageVersion(google.protobuf.StringValue) returns (stream CommandOutput) {} + rpc repoInstallPackage(RepoInstallPackageRequest) returns (google.protobuf.Empty) {} + rpc repoAddLibraryPath(google.protobuf.StringValue) returns (stream CommandOutput) {} + rpc repoCheckPackageInstalled(google.protobuf.StringValue) returns (stream CommandOutput) {} + rpc repoRemovePackage(RepoRemovePackageRequest) returns (google.protobuf.Empty) {} + + // Dataset import service points + rpc previewDataImport(PreviewDataImportRequest) returns (stream CommandOutput) {} + rpc commitDataImport(CommitDataImportRequest) returns (google.protobuf.Empty) {} + + // Methods for RRef and RVariableLoader + rpc copyToPersistentRef(RRef) returns (CopyToPersistentRefResponse) {} + rpc disposePersistentRefs(PersistentRefList) returns (google.protobuf.Empty) {} + rpc loaderGetParentEnvs(RRef) returns (ParentEnvsResponse) {} + rpc loaderGetVariables(GetVariablesRequest) returns (VariablesResponse) {} + rpc loaderGetLoadedNamespaces(google.protobuf.Empty) returns (StringList) {} + rpc loaderGetValueInfo(RRef) returns (ValueInfo) {} + rpc evaluateAsText(RRef) returns (StringOrError) {} + rpc evaluateAsBoolean(RRef) returns (google.protobuf.BoolValue) {} + rpc getDistinctStrings(RRef) returns (StringList) {} + rpc loadObjectNames(RRef) returns (StringList) {} + rpc findInheritorNamedArguments(RRef) returns (StringList) {} + rpc findExtraNamedArguments(RRef) returns (ExtraNamedArguments) {} + rpc getS4ClassInfoByObjectName(RRef) returns (S4ClassInfo) {} + rpc getR6ClassInfoByObjectName(RRef) returns (R6ClassInfo) {} + rpc getTableColumnsInfo(TableColumnsInfoRequest) returns (TableColumnsInfo) {} + rpc getFormalArguments(RRef) returns (StringList) {} + rpc getEqualityObject(RRef) returns (google.protobuf.Int64Value) {} + rpc setValue(SetValueRequest) returns (ValueInfo) {} + rpc getObjectSizes(RRefList) returns (Int64List) {} + + rpc getRMarkdownChunkOptions(google.protobuf.Empty) returns (StringList) {} + + // Data frame viewer + rpc dataFrameRegister(RRef) returns (google.protobuf.Int32Value) {} + rpc dataFrameGetInfo(RRef) returns (DataFrameInfoResponse) {} + rpc dataFrameGetData(DataFrameGetDataRequest) returns (DataFrameGetDataResponse) {} + rpc dataFrameSort(DataFrameSortRequest) returns (google.protobuf.Int32Value) {} + rpc dataFrameFilter(DataFrameFilterRequest) returns (google.protobuf.Int32Value) {} + rpc dataFrameRefresh(RRef) returns (google.protobuf.BoolValue) {} + + // Documentation and http + rpc convertRoxygenToHTML(ConvertRoxygenToHTMLRequest) returns (ConvertRoxygenToHTMLResponse) {} + rpc httpdRequest(google.protobuf.StringValue) returns (HttpdResponse) {} + rpc getDocumentationForPackage(google.protobuf.StringValue) returns (HttpdResponse) {} + rpc getDocumentationForSymbol(DocumentationForSymbolRequest) returns (HttpdResponse) {} + rpc startHttpd(google.protobuf.Empty) returns (google.protobuf.Int32Value) {} + + // Misc + rpc getWorkingDir(google.protobuf.Empty) returns (google.protobuf.StringValue) {} + rpc setWorkingDir(google.protobuf.StringValue) returns (google.protobuf.Empty) {} + rpc clearEnvironment(RRef) returns (google.protobuf.Empty) {} + rpc getSysEnv(GetSysEnvRequest) returns (StringList) {} + rpc loadInstalledPackages(google.protobuf.Empty) returns (RInstalledPackageList) {} + rpc loadLibPaths(google.protobuf.Empty) returns (RLibraryPathList) {} + rpc loadLibrary(google.protobuf.StringValue) returns (google.protobuf.Empty) {} + rpc unloadLibrary(UnloadLibraryRequest) returns (google.protobuf.Empty) {} + rpc saveGlobalEnvironment(google.protobuf.StringValue) returns (google.protobuf.Empty) {} + rpc loadEnvironment(LoadEnvironmentRequest) returns (google.protobuf.Empty) {} + rpc setOutputWidth(google.protobuf.Int32Value) returns (google.protobuf.Empty) {} + rpc clientRequestFinished(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc rStudioApiResponse(RObject) returns (google.protobuf.Empty) {} + rpc setSaveOnExit(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} + rpc setRStudioApiEnabled(google.protobuf.BoolValue) returns (stream CommandOutput) {} + rpc getLoadedShortS4ClassInfos(google.protobuf.Empty) returns (ShortS4ClassInfoList) {} + rpc getS4ClassInfoByClassName(google.protobuf.StringValue) returns (S4ClassInfo) {} + rpc getLoadedShortR6ClassInfos(google.protobuf.Empty) returns (ShortR6ClassInfoList) {} + rpc getR6ClassInfoByClassName(google.protobuf.StringValue) returns (R6ClassInfo) {} +} + +message Init { + string projectDir = 1; + string rScriptsPath = 2; + + string workspaceFile = 3; + bool loadWorkspace = 4; + bool saveOnExit = 5; + string httpUserAgent = 6; + bool enableRStudioApi = 7; +} + +message GetInfoResponse { + string rVersion = 1; + int32 pid = 2; +} + +message ExecuteCodeRequest { + enum DebugCommand { + CONTINUE = 0; + STOP = 1; + KEEP_PREVIOUS = 2; + } + string code = 1; + string sourceFileId = 2; + int32 sourceFileLineOffset = 3; + int32 sourceFileFirstLineOffset = 4; + bool withEcho = 5; + bool streamOutput = 6; + bool isRepl = 7; + bool isDebug = 8; + DebugCommand firstDebugCommand = 9; + bool setLastValue = 10; +} + +message CommandOutput { + enum Type { + STDOUT = 0; + STDERR = 1; + } + Type type = 1; + bytes text = 2; +} + +message ExceptionInfo { + string message = 1; + string call = 2; + oneof details { + google.protobuf.Empty simpleError = 3; + google.protobuf.Empty interrupted = 4; + string packageNotFound = 5; + } +} + +message ExecuteCodeResponse { + oneof msg { + CommandOutput output = 1; + string exception = 2; + } +} + +message StackFrame { + SourcePosition position = 1; + string functionName = 2; + int64 equalityObject = 3; + ExtendedSourcePosition extendedSourcePosition = 4; + string sourcePositionText = 5; +} + +message StackFrameList { + repeated StackFrame frames = 1; +} + +message RObject { + message RString { + repeated string strings = 1; + } + message RInt { + repeated int64 ints = 1; + } + message RDouble { + repeated double doubles = 1; + } + message List { + repeated RObject rObjects = 1; + } + message RNull {} + message RBoolean { + repeated bool booleans = 1; + } + message KeyValue { + string key = 1; + RObject value = 2; + } + message NamedList { + repeated KeyValue rObjects = 1; + } + oneof object { + RString rString = 1; + RInt rInt = 2; + RDouble rDouble = 3; + List list = 4; + RNull rNull = 5; + RBoolean rBoolean = 6; + NamedList namedList = 7; + string error = 8; + } +} + +message AsyncEvent { + message RequestReadLn { + string prompt = 1; + } + message DebugPrompt { + bool changed = 1; + StackFrameList stack = 2; + } + message ViewRequest { + int32 persistentRefIndex = 1; + string title = 2; + ValueInfo value = 3; + } + message ViewTableRequest { + int32 persistentRefIndex = 1; + string title = 2; + } + message Exception { + ExceptionInfo exception = 1; + StackFrameList stack = 2; + } + message ShowFileRequest { + string filePath = 1; + string title = 2; + } + message RStudioApiRequest { + int32 functionID = 1; + RObject args = 2; + } + oneof event { + google.protobuf.Empty busy = 1; + CommandOutput text = 2; + RequestReadLn requestReadLn = 3; + google.protobuf.Empty prompt = 4; + DebugPrompt debugPrompt = 5; + google.protobuf.Empty termination = 6; + Exception exception = 7; + ViewRequest viewRequest = 8; + ShowFileRequest showFileRequest = 9; + HttpdResponse showHelpRequest = 10; + google.protobuf.Empty subprocessInput = 11; + string browseURLRequest = 12; + RStudioApiRequest rStudioApiRequest = 13; + int32 debugRemoveBreakpointRequest = 14; + SourcePosition debugPrintSourcePositionToConsoleRequest = 15; + ViewTableRequest viewTableRequest = 16; + } +} + +message SourcePosition { + string fileId = 1; + int32 line = 2; +} + +message GetFunctionSourcePositionResponse { + SourcePosition position = 1; + string sourcePositionText = 2; +} + +message ExtendedSourcePosition { + int32 startLine = 1; + int32 startOffset = 2; + int32 endLine = 3; + int32 endOffset = 4; +} + +message DebugAddOrModifyBreakpointRequest { + int32 id = 1; + SourcePosition position = 2; + bool enabled = 3; + bool suspend = 4; + string evaluateAndLog = 5; + string condition = 6; + bool removeAfterHit = 7; + bool hitMessage = 8; + bool printStack = 9; +} + +message DebugSetMasterBreakpointRequest { + int32 breakpointId = 1; + oneof master { + google.protobuf.Empty none = 2; + int32 masterId = 3; + } + bool leaveEnabled = 4; +} + +message RRef { + message Member { + RRef env = 1; + string name = 2; + } + message ParentEnv { + RRef env = 1; + int32 index = 2; + } + message Expression { + RRef env = 1; + string code = 2; + } + message ListElement { + RRef list = 1; + int64 index = 2; + } + oneof ref { + int32 persistentIndex = 1; + google.protobuf.Empty globalEnv = 2; + google.protobuf.Empty currentEnv = 3; + int32 sysFrameIndex = 4; + Member member = 5; + ParentEnv parentEnv = 6; + Expression expression = 7; + ListElement listElement = 8; + int32 errorStackSysFrameIndex = 9; + RRef attributes = 10; + } +} + +message CopyToPersistentRefResponse { + oneof response { + int32 persistentIndex = 1; + string error = 2; + } +} + +message PersistentRefList { + repeated int32 indices = 1; +} + +message ParentEnvsResponse { + message EnvInfo { + string name = 1; + } + repeated EnvInfo envs = 1; +} + +message GetVariablesRequest { + RRef obj = 1; + int64 start = 2; + int64 end = 3; + // These parameters are for environments only + bool noHidden = 4; + bool noFunctions = 5; + bool onlyFunctions = 6; +} + +message VariablesResponse { + message Variable { + string name = 1; + ValueInfo value = 2; + } + bool isEnv = 1; + int64 totalCount = 2; + repeated Variable vars = 3; +} + +message ValueInfo { + message Unevaluated { + string code = 1; + } + message Value { + string textValue = 1; + bool isComplete = 2; + bool isVector = 3; + bool isS4 = 4; + bool isR6 = 5; + } + message List { + int64 length = 1; + } + message DataFrame { + int32 rows = 1; + int32 cols = 2; + } + message Function { + string header = 1; + } + message Environment { + string name = 1; + } + message Error { + string text = 1; + } + message Matrix { + repeated int32 dim = 1; + } + repeated string cls = 1; + oneof info { + Unevaluated unevaluated = 2; + Value value = 3; + List list = 4; + DataFrame dataFrame = 5; + Function function = 6; + Environment environment = 7; + google.protobuf.Empty graph = 8; + Error error = 9; + Matrix matrix = 10; + } +} + +message StringList { + repeated string list = 1; +} + +message Int32List { + string message = 1; + repeated int32 value = 2; +} + +message GraphicsInstallRequest { + string packagePath = 1; + string libraryPath = 2; + string packageType = 3; +} + +message ScreenParameters { + int32 width = 1; + int32 height = 2; + int32 resolution = 3; +} + +message GraphicsInitRequest { + ScreenParameters screenParameters = 1; + bool inMemory = 2; +} + +message GraphicsDumpResponse { + string message = 1; + map number2Parameters = 2; +} + +message GraphicsRescaleRequest { + int32 snapshotNumber = 1; + ScreenParameters newParameters = 2; +} + +message GraphicsRescaleStoredRequest { + string groupId = 1; + int32 snapshotNumber = 2; + int32 snapshotVersion = 3; + ScreenParameters newParameters = 4; +} + +message GraphicsGetSnapshotPathRequest { + string groupId = 1; + int32 snapshotNumber = 2; +} + +message GraphicsGetSnapshotPathResponse { + string message = 1; + string snapshotName = 2; + string directory = 3; +} + +message Font { + string name = 1; + float size = 2; + int32 style = 3; +} + +message Stroke { + float width = 1; + int32 cap = 2; + int32 join = 3; + float miterLimit = 4; + int32 pattern = 5; +} + +message Polyline { + repeated fixed64 point = 1; + int32 previewCount = 2; +} + +message RasterImage { + int32 width = 1; + int32 height = 2; + bytes data = 3; // little-endian uint32[] ARGB +} + +message FixedViewport { + float ratio = 1; + float delta = 2; + int32 parentIndex = 3; +} + +message FreeViewport { + fixed64 from = 1; + fixed64 to = 2; + int32 parentIndex = 3; +} + +message Viewport { + oneof kind { + FixedViewport fixed = 1; + FreeViewport free = 2; + } +} + +message CircleFigure { + fixed64 center = 1; + fixed32 radius = 2; + int32 strokeIndex = 3; + int32 colorIndex = 4; + int32 fillIndex = 5; +} + +message LineFigure { + fixed64 from = 1; + fixed64 to = 2; + int32 strokeIndex = 3; + int32 colorIndex = 4; +} + +message PathFigure { + repeated Polyline subPath = 1; + bool winding = 2; + int32 strokeIndex = 3; + int32 colorIndex = 4; + int32 fillIndex = 5; +} + +message PolygonFigure { + Polyline polyline = 1; + int32 strokeIndex = 2; + int32 colorIndex = 3; + int32 fillIndex = 4; +} + +message PolylineFigure { + Polyline polyline = 1; + int32 strokeIndex = 2; + int32 colorIndex = 3; +} + +message RasterFigure { + RasterImage image = 1; + fixed64 from = 2; + fixed64 to = 3; + float angle = 4; + bool interpolate = 5; +} + +message RectangleFigure { + fixed64 from = 1; + fixed64 to = 2; + int32 strokeIndex = 3; + int32 colorIndex = 4; + int32 fillIndex = 5; +} + +message TextFigure { + string text = 1; + fixed64 position = 2; + float angle = 3; + float anchor = 4; + int32 fontIndex = 5; + int32 colorIndex = 6; +} + +message Figure { + oneof kind { + CircleFigure circle = 1; + LineFigure line = 2; + PathFigure path = 3; + PolygonFigure polygon = 4; + PolylineFigure polyline = 5; + RasterFigure raster = 6; + RectangleFigure rectangle = 7; + TextFigure text = 8; + } +} + +message Layer { + int32 viewportIndex = 1; + int32 clippingAreaIndex = 2; + repeated Figure figure = 3; + bool isAxisText = 4; +} + +message Plot { + repeated Font font = 1; + repeated int32 color = 2; + repeated Stroke stroke = 3; + repeated Viewport viewport = 4; + repeated Layer layer = 5; + int32 previewComplexity = 6; + int32 totalComplexity = 7; + int32 error = 8; +} + +message GraphicsFetchPlotResponse { + string message = 1; + Plot plot = 2; +} + +message ChunkParameters { + string rmarkdownParameters = 1; + string chunkText = 2; +} + +message RepoInstallPackageRequest { + string packageName = 1; + string fallbackMethod = 2; + map arguments = 3; +} + +message RepoRemovePackageRequest { + string packageName = 1; + string libraryPath = 2; +} + +message PreviewDataImportRequest { + string path = 1; + string mode = 2; + int32 rowCount = 3; + map options = 4; +} + +message CommitDataImportRequest { + string name = 1; + string path = 2; + string mode = 3; + map options = 4; +} + +message TableColumnsInfoRequest { + RRef ref = 1; +} + +message TableColumnsInfo { + enum TableType { + UNKNOWN = 0; + DPLYR = 1; + DATA_TABLE = 2; + DATA_FRAME = 3; + } + message Column { + string name = 1; + string type = 2; + } + repeated Column columns = 1; + TableType tableType = 2; +} + +message DataFrameInfoResponse { + enum ColumnType { + INTEGER = 0; + DOUBLE = 1; + BOOLEAN = 3; + STRING = 4; + } + message Column { + string name = 1; + ColumnType type = 2; + bool sortable = 3; + bool isRowNames = 4; + } + int32 nRows = 1; + repeated Column columns = 2; + bool canRefresh = 3; +} + +message DataFrameGetDataRequest { + RRef ref = 1; + int32 start = 2; + int32 end = 3; +} + +message DataFrameGetDataResponse { + message Value { + oneof value { + google.protobuf.Empty na = 1; + int32 intValue = 2; + double doubleValue = 3; + bool booleanValue = 4; + string stringValue = 5; + } + } + message Column { + repeated Value values = 1; + } + repeated Column columns = 1; +} + +message DataFrameSortRequest { + message SortKey { + int32 columnIndex = 1; + bool descending = 2; + } + RRef ref = 1; + repeated SortKey keys = 2; +} + +message DataFrameFilterRequest { + message Filter { + message ComposedFilter { + enum Type { + AND = 0; + OR = 1; + NOT = 2; + } + Type type = 1; + repeated Filter filters = 2; + } + message Operator { + enum Type { + EQ = 0; + NEQ = 1; + LESS = 2; + GREATER = 3; + LEQ = 4; + GEQ = 5; + REGEX = 6; + } + int32 column = 1; + Type type = 2; + string value = 3; + } + message NaFilter { + int32 column = 1; + bool isNa = 2; + } + oneof filter { + google.protobuf.Empty true = 1; + ComposedFilter composed = 2; + Operator operator = 3; + NaFilter naFilter = 4; + } + } + RRef ref = 1; + Filter filter = 2; +} + +message ConvertRoxygenToHTMLRequest { + string functionName = 1; + string functionText = 2; +} + +message ConvertRoxygenToHTMLResponse { + oneof result { + string text = 1; + string error = 2; + } +} + +message UnloadLibraryRequest { + string packageName = 1; + bool withDynamicLibrary = 2; +} + +message HttpdResponse { + bool success = 1; + string content = 2; + string url = 3; +} + +message DocumentationForSymbolRequest { + string symbol = 1; + string package = 2; +} + +message SetValueRequest { + RRef ref = 1; + RRef value = 2; +} + +message LoadEnvironmentRequest { + string file = 1; + // empty variable name means load into global env + string variable = 2; +} + +message RRefList { + repeated RRef refs = 1; +} + +message Int64List { + repeated int64 list = 1; +} + +message StringOrError { + oneof result { + string value = 1; + string error = 2; + } +} + +message ExtraNamedArguments { + // See org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo for details + repeated string argNames = 1; + repeated string funArgNames = 2; +} + +message GetSysEnvRequest { + string envName = 1; + repeated string flags = 2; +} + +message RLibraryPathList { + message RLibraryPath { + string path = 1; + bool isWritable = 2; + } + + repeated RLibraryPath libPaths = 1; +} + +message RInstalledPackageList { + message RInstalledPackage { + message MapEntry { + string key = 1; + string value = 2; + } + enum RPackagePriority { + BASE = 0; + RECOMMENDED = 1; + NA = 2; + } + string packageName = 1; + string packageVersion = 2; + RPackagePriority priority = 3; + string libraryPath = 4; + string canonicalPackagePath = 5; + repeated MapEntry description = 6; + } + + repeated RInstalledPackage packages = 1; +} + +message S4ClassInfo { + message S4ClassSlot { + string name = 1; + string type = 2; + } + string className = 1; + string packageName = 2; + repeated S4ClassSlot slots = 3; + repeated string superClasses = 4; + bool isVirtual = 5; +} + +message ShortS4ClassInfoList { + message ShortS4ClassInfo { + string name = 1; + string package = 2; + bool isVirtual = 3; + } + + repeated ShortS4ClassInfo shortS4ClassInfos = 1; +} + +message R6ClassInfo { + message R6ClassField { + string name = 1; + bool isPublic = 3; + } + + message R6ClassMethod { + string name = 1; + bool isPublic = 3; + } + + message R6ClassActiveBinding { + string name = 1; + } + + string className = 1; + string packageName = 2; + string superClass = 3; + repeated R6ClassField fields = 4; + repeated R6ClassField methods = 5; + repeated R6ClassField activeBindings = 6; +} + +message ShortR6ClassInfoList { + message ShortR6ClassInfo { + string name = 1; + string package = 2; + bool isVirtual = 3; + } + + repeated ShortR6ClassInfo shortR6ClassInfos = 1; +} \ No newline at end of file From 4c05dff429d3d6364617a96288d19fd649beb01a Mon Sep 17 00:00:00 2001 From: deaglegross Date: Wed, 7 Apr 2021 17:19:56 +0300 Subject: [PATCH 18/52] `rename` and `navigation` works --- .../findUsages/RTargetElementEvaluator.kt | 10 +++-- .../rename/RenameRPsiElementProcessor.kt | 2 +- test/org/jetbrains/r/rename/RRenameTest.kt | 45 +++++++++++++------ .../classes/R6/renameR6FieldFromUsage.R | 11 +++++ .../classes/R6/renameR6FieldFromUsage.after.R | 11 +++++ .../classes/R6/renameR6MethodFromUsage.R | 11 +++++ .../R6/renameR6MethodFromUsage.after.R | 11 +++++ 7 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 testData/rename/classes/R6/renameR6FieldFromUsage.R create mode 100644 testData/rename/classes/R6/renameR6FieldFromUsage.after.R create mode 100644 testData/rename/classes/R6/renameR6MethodFromUsage.R create mode 100644 testData/rename/classes/R6/renameR6MethodFromUsage.after.R diff --git a/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt b/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt index 6c42474a9..12e0572f8 100644 --- a/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt +++ b/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt @@ -3,6 +3,7 @@ package org.jetbrains.r.codeInsight.findUsages import com.intellij.codeInsight.TargetElementEvaluatorEx2 import com.intellij.psi.PsiElement import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RParameter class RTargetElementEvaluator: TargetElementEvaluatorEx2() { @@ -12,10 +13,11 @@ class RTargetElementEvaluator: TargetElementEvaluatorEx2() { return grandParent.assignee == parent } - if (grandParent is RParameter) { - return true - } + return when (grandParent) { + is RParameter, + is RNamedArgument -> true - return false + else -> false + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt b/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt index 60bc54464..158786b91 100644 --- a/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt +++ b/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt @@ -30,7 +30,7 @@ class RenameRPsiElementProcessor : RenamePsiElementProcessor() { } } is RForStatement -> element.target - is RAssignmentStatement, is RParameter, is RFile -> element + is RAssignmentStatement, is RNamedArgument, is RParameter, is RFile -> element else -> null } } diff --git a/test/org/jetbrains/r/rename/RRenameTest.kt b/test/org/jetbrains/r/rename/RRenameTest.kt index c27eda4c8..271bc9475 100644 --- a/test/org/jetbrains/r/rename/RRenameTest.kt +++ b/test/org/jetbrains/r/rename/RRenameTest.kt @@ -91,22 +91,39 @@ class RRenameTest : RLightCodeInsightFixtureTestCase() { fun testRenameDocumentationFunctionLink() = doTestWithProject("baz") - private fun doTestWithProject(newName: String, isInlineAvailable: Boolean = true, isRmd: Boolean = false, isSourceTest: Boolean = false) { + fun testRenameR6FieldFromUsage() = doTestWithProject("summary", isInOtherDirectory = true) + + fun testRenameR6MethodFromUsage() = doTestWithProject("additiveOperator", isInOtherDirectory = true) + + private fun doTestWithProject(newName: String, isInlineAvailable: Boolean = true, isRmd: Boolean = false, isSourceTest: Boolean = false, isInOtherDirectory: Boolean = false) { val dotFileExtension = getDotExtension(isRmd) lateinit var startFiles: List lateinit var endFiles: List - if (isSourceTest) { - addLibraries() - val files = File(myFixture.testDataPath + "/rename/" + getTestName(true)) - .listFiles() - ?.map { it.absolutePath.replace(myFixture.testDataPath, "") } - ?.sortedByDescending { it.contains("main") } ?: error("Cannot find root test directory") - startFiles = files.filter { !it.contains(".after.") } - endFiles = files - startFiles - myFixture.configureByFiles(*startFiles.toTypedArray()) - } - else { - myFixture.configureByFile("rename/" + getTestName(true) + dotFileExtension) + + when { + isInOtherDirectory -> { + addLibraries() + val files = File(myFixture.testDataPath + "/rename/").walkBottomUp() + .filter { it -> it.name.contains(getTestName(true)) } + .map { it.absolutePath.replace(myFixture.testDataPath, "") } + .sortedByDescending { it.contains("main") }.toList() ?: error("Cannot find root test directory") + startFiles = files.filter { !it.contains(".after.") } + endFiles = files - startFiles + myFixture.configureByFiles(*startFiles.toTypedArray()) + } + isSourceTest -> { + addLibraries() + val files = File(myFixture.testDataPath + "/rename/" + getTestName(true)) + .listFiles() + ?.map { it.absolutePath.replace(myFixture.testDataPath, "") } + ?.sortedByDescending { it.contains("main") } ?: error("Cannot find root test directory") + startFiles = files.filter { !it.contains(".after.") } + endFiles = files - startFiles + myFixture.configureByFiles(*startFiles.toTypedArray()) + } + else -> { + myFixture.configureByFile("rename/" + getTestName(true) + dotFileExtension) + } } val variableHandler = RVariableInplaceRenameHandler() val memberHandler = RMemberInplaceRenameHandler() @@ -125,7 +142,7 @@ class RRenameTest : RLightCodeInsightFixtureTestCase() { } CodeInsightTestUtil.doInlineRename(handler, newName, myFixture) - if (isSourceTest) { + if (isSourceTest || isInOtherDirectory) { if (endFiles.size != startFiles.size) error("Different number of start and end files") for (i in startFiles.indices) { myFixture.checkResultByFile(startFiles[i], endFiles[i], false) diff --git a/testData/rename/classes/R6/renameR6FieldFromUsage.R b/testData/rename/classes/R6/renameR6FieldFromUsage.R new file mode 100644 index 000000000..1d068856e --- /dev/null +++ b/testData/rename/classes/R6/renameR6FieldFromUsage.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + sum = 0, + add = function(x = 1) { + self$sum <- self$sum + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$add(4)$sum +x$sum diff --git a/testData/rename/classes/R6/renameR6FieldFromUsage.after.R b/testData/rename/classes/R6/renameR6FieldFromUsage.after.R new file mode 100644 index 000000000..f4c876138 --- /dev/null +++ b/testData/rename/classes/R6/renameR6FieldFromUsage.after.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + summary = 0, + add = function(x = 1) { + self$summary <- self$summary + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$add(4)$summary +x$summary diff --git a/testData/rename/classes/R6/renameR6MethodFromUsage.R b/testData/rename/classes/R6/renameR6MethodFromUsage.R new file mode 100644 index 000000000..a457981fd --- /dev/null +++ b/testData/rename/classes/R6/renameR6MethodFromUsage.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + sum = 0, + add = function(x = 1) { + self$sum <- self$sum + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$add(4)$sum +x$sum diff --git a/testData/rename/classes/R6/renameR6MethodFromUsage.after.R b/testData/rename/classes/R6/renameR6MethodFromUsage.after.R new file mode 100644 index 000000000..eae41bb57 --- /dev/null +++ b/testData/rename/classes/R6/renameR6MethodFromUsage.after.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + sum = 0, + additiveOperator = function(x = 1) { + self$sum <- self$sum + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$additiveOperator(4)$sum +x$sum From b7256a0f870dd2f74f4cbc7a451fc2e43c7bf7b7 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Wed, 7 Apr 2021 18:24:06 +0300 Subject: [PATCH 19/52] refactor contexts --- .../context/LibraryClassContext.kt} | 4 +- .../common/context/LibraryContextProvider.kt | 40 +++++++++++++++++++ .../r/classes/r6/context/R6ContextProvider.kt | 34 ++-------------- .../context/R6CreateClassContextProvider.kt | 9 ++--- .../r6/context/R6NewObjectContextProvider.kt | 11 ++--- .../r/classes/s4/context/RS4Context.kt | 12 ------ .../classes/s4/context/RS4ContextProvider.kt | 33 ++------------- .../s4/context/RS4NewObjectContextProvider.kt | 7 ++-- .../s4/context/RS4SetClassContextProvider.kt | 7 ++-- .../r/editor/RCompletionContributor.kt | 22 +++++----- src/org/jetbrains/r/psi/RElementFilters.kt | 7 ++-- test/org/jetbrains/r/rename/RRenameTest.kt | 2 + .../R6/renameR6ActiveBindingFromUsage.R | 17 ++++++++ .../R6/renameR6ActiveBindingFromUsage.after.R | 17 ++++++++ 14 files changed, 116 insertions(+), 106 deletions(-) rename src/org/jetbrains/r/classes/{r6/context/R6Context.kt => common/context/LibraryClassContext.kt} (84%) create mode 100644 src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt delete mode 100644 src/org/jetbrains/r/classes/s4/context/RS4Context.kt create mode 100644 testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R create mode 100644 testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R diff --git a/src/org/jetbrains/r/classes/r6/context/R6Context.kt b/src/org/jetbrains/r/classes/common/context/LibraryClassContext.kt similarity index 84% rename from src/org/jetbrains/r/classes/r6/context/R6Context.kt rename to src/org/jetbrains/r/classes/common/context/LibraryClassContext.kt index 6dcaf2bb9..a4a900611 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6Context.kt +++ b/src/org/jetbrains/r/classes/common/context/LibraryClassContext.kt @@ -2,13 +2,13 @@ * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.classes.r6.context +package org.jetbrains.r.classes.common.context import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.api.RPsiElement -interface R6Context { +interface LibraryClassContext { val functionName: String val functionCall: RCallExpression val argumentInfo: RArgumentInfo diff --git a/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt b/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt new file mode 100644 index 000000000..9c5268e7b --- /dev/null +++ b/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.common.context + +import com.intellij.openapi.extensions.ExtensionPointName +import org.jetbrains.r.psi.api.RPsiElement +import java.lang.reflect.ParameterizedType + +abstract class LibraryContextProvider { + abstract fun getContext(element: RPsiElement): T? + + @Suppress("UNCHECKED_CAST") + private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class + + companion object { + private val EP_NAME: ExtensionPointName> = + ExtensionPointName.create("com.intellij.libraryContextProvider") + + fun getProviders(): List> = EP_NAME.extensionList + + fun getContext(element: RPsiElement): LibraryClassContext? { + return getContext(element, LibraryClassContext::class.java) + } + + fun getContext(element: RPsiElement, vararg searchedContexts: Class): T? { + for (provider in getProviders()) { + if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { + val context = provider.getContext(element) + if (context != null) { + @Suppress("UNCHECKED_CAST") + return context as T? + } + } + } + return null + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt index c1793d3be..b6387c6fe 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt @@ -5,38 +5,12 @@ package org.jetbrains.r.classes.r6.context import com.intellij.openapi.extensions.ExtensionPointName -import org.jetbrains.r.classes.s4.context.RS4Context -import org.jetbrains.r.classes.s4.context.RS4ContextProvider -import org.jetbrains.r.psi.api.RPsiElement -import java.lang.reflect.ParameterizedType - -abstract class R6ContextProvider { - abstract fun getR6Context(element: RPsiElement): T? - - @Suppress("UNCHECKED_CAST") - private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class +import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.LibraryContextProvider +abstract class R6ContextProvider : LibraryContextProvider() { companion object { - private val EP_NAME: ExtensionPointName> = + private val EP_NAME: ExtensionPointName> = ExtensionPointName.create("com.intellij.r6ContextProvider") - - fun getProviders(): List> = EP_NAME.extensionList - - fun getR6Context(element: RPsiElement): R6Context? { - return getR6Context(element, R6Context::class.java) - } - - fun getR6Context(element: RPsiElement, vararg searchedContexts: Class): T? { - for (provider in getProviders()) { - if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { - val r6Context = provider.getR6Context(element) - if (r6Context != null) { - @Suppress("UNCHECKED_CAST") - return r6Context as T? - } - } - } - return null - } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt index ccc6bc610..109ee7b1b 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -7,17 +7,16 @@ package org.jetbrains.r.classes.r6.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.LibraryClassContext import org.jetbrains.r.classes.r6.R6ClassInfoUtil -import org.jetbrains.r.classes.s4.context.* import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil -import org.jetbrains.r.psi.RPsiUtil import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class R6CreateClassContext : R6Context { +sealed class R6CreateClassContext : LibraryClassContext { override val functionName = "R6Class" } @@ -26,8 +25,8 @@ data class R6CreateClassNameContext(override val originalElement: RPsiElement, override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : R6CreateClassContext() -class R6CreateClassContextProvider : R6ContextProvider() { - override fun getR6Context(element: RPsiElement): R6CreateClassContext? { +class R6CreateClassContextProvider : R6ContextProvider() { + override fun getContext(element: RPsiElement): R6CreateClassContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getR6ContextInner(element), element) } diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt index 74f08a16f..28b3951bc 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt @@ -7,18 +7,15 @@ package org.jetbrains.r.classes.r6.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.LibraryClassContext import org.jetbrains.r.classes.r6.R6ClassInfoUtil -import org.jetbrains.r.classes.s4.context.RS4NewObjectClassNameContext -import org.jetbrains.r.classes.s4.context.RS4NewObjectSlotNameContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil -import org.jetbrains.r.psi.RPsiUtil import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class R6NewObjectContext : R6Context { +sealed class R6NewObjectContext : LibraryClassContext { override val functionName = "new" } @@ -27,8 +24,8 @@ data class R6NewObjectClassNameContext(override val originalElement: RPsiElement override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : R6NewObjectContext() -class R6NewObjectContextProvider : R6ContextProvider() { - override fun getR6Context(element: RPsiElement): R6NewObjectContext? { +class R6NewObjectContextProvider : R6ContextProvider() { + override fun getContext(element: RPsiElement): R6NewObjectContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getR6ContextInner(element), element) } diff --git a/src/org/jetbrains/r/classes/s4/context/RS4Context.kt b/src/org/jetbrains/r/classes/s4/context/RS4Context.kt deleted file mode 100644 index 6b75e4fbc..000000000 --- a/src/org/jetbrains/r/classes/s4/context/RS4Context.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.r.classes.s4.context - -import org.jetbrains.r.hints.parameterInfo.RArgumentInfo -import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.api.RPsiElement - -interface RS4Context { - val functionName: String - val functionCall: RCallExpression - val argumentInfo: RArgumentInfo - val originalElement: RPsiElement -} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt index 9f02da0f6..762e64fee 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt @@ -1,37 +1,12 @@ package org.jetbrains.r.classes.s4.context import com.intellij.openapi.extensions.ExtensionPointName -import org.jetbrains.r.psi.api.RPsiElement -import java.lang.reflect.ParameterizedType - -abstract class RS4ContextProvider { - - abstract fun getS4Context(element: RPsiElement): T? - - @Suppress("UNCHECKED_CAST") - private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class +import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.LibraryContextProvider +abstract class RS4ContextProvider: LibraryContextProvider(){ companion object { - private val EP_NAME: ExtensionPointName> = + private val EP_NAME: ExtensionPointName> = ExtensionPointName.create("com.intellij.rS4ContextProvider") - - fun getProviders(): List> = EP_NAME.extensionList - - fun getS4Context(element: RPsiElement): RS4Context? { - return getS4Context(element, RS4Context::class.java) - } - - fun getS4Context(element: RPsiElement, vararg searchedContexts: Class): T? { - for (provider in getProviders()) { - if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { - val s4Context = provider.getS4Context(element) - if (s4Context != null) { - @Suppress("UNCHECKED_CAST") - return s4Context as T? - } - } - } - return null - } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt index e13847138..4623c1c3b 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt @@ -3,6 +3,7 @@ package org.jetbrains.r.classes.s4.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.LibraryClassContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.RPsiUtil @@ -11,7 +12,7 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class RS4NewObjectContext : RS4Context { +sealed class RS4NewObjectContext : LibraryClassContext { override val functionName = "new" } @@ -26,8 +27,8 @@ data class RS4NewObjectSlotNameContext(override val originalElement: RPsiElement override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : RS4NewObjectContext() -class RS4NewObjectContextProvider : RS4ContextProvider() { - override fun getS4Context(element: RPsiElement): RS4NewObjectContext? { +class RS4NewObjectContextProvider : RS4ContextProvider() { + override fun getContext(element: RPsiElement): RS4NewObjectContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getS4ContextInner(element), element) } diff --git a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt index 188165695..a4cd6658c 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt @@ -3,6 +3,7 @@ package org.jetbrains.r.classes.s4.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.LibraryClassContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.api.RCallExpression @@ -10,7 +11,7 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class RS4SetClassContext : RS4Context { +sealed class RS4SetClassContext : LibraryClassContext { override val functionName = "setClass" } @@ -34,8 +35,8 @@ data class RS4SetClassDependencyClassNameContext(override val originalElement: R override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : RS4SetClassContext() -class RS4SetClassContextProvider : RS4ContextProvider() { - override fun getS4Context(element: RPsiElement): RS4SetClassContext? { +class RS4SetClassContextProvider : RS4ContextProvider() { + override fun getContext(element: RPsiElement): RS4SetClassContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getS4ContextInner(element), element) } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 4e1712c5e..c650ed789 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -20,9 +20,9 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.r.RLanguage +import org.jetbrains.r.classes.common.context.LibraryContextProvider import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.r6.R6ClassMember -import org.jetbrains.r.classes.r6.context.R6ContextProvider import org.jetbrains.r.classes.r6.context.R6CreateClassContext import org.jetbrains.r.classes.r6.context.R6NewObjectContext import org.jetbrains.r.classes.s4.* @@ -496,7 +496,7 @@ class RCompletionContributor : CompletionContributor() { } private fun addS4SlotNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { - val s4Context = RS4ContextProvider.getS4Context(classNameExpression, RS4NewObjectContext::class.java) ?: return + val s4Context = LibraryContextProvider.getContext(classNameExpression, RS4NewObjectContext::class.java) ?: return if (s4Context !is RS4NewObjectSlotNameContext) return val newCall = s4Context.functionCall @@ -530,9 +530,9 @@ class RCompletionContributor : CompletionContributor() { } private fun addS4ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { - val s4Context = RS4ContextProvider.getS4Context(classNameExpression, - RS4NewObjectContext::class.java, - RS4SetClassContext::class.java) ?: return + val s4Context = LibraryContextProvider.getContext(classNameExpression, + RS4NewObjectContext::class.java, + RS4SetClassContext::class.java) ?: return var omitVirtual = false var nameToOmit: String? = null when (s4Context) { @@ -611,15 +611,15 @@ class RCompletionContributor : CompletionContributor() { } private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - val r6Context = R6ContextProvider.getR6Context(classNameExpression, - R6NewObjectContext::class.java, - R6CreateClassContext::class.java) ?: return + val r6Context = LibraryContextProvider.getContext(classNameExpression, + R6NewObjectContext::class.java, + R6CreateClassContext::class.java) ?: return } private fun addR6MemberNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - val r6Context = R6ContextProvider.getR6Context(classNameExpression, - R6NewObjectContext::class.java, - R6CreateClassContext::class.java) ?: return + val r6Context = LibraryContextProvider.getContext(classNameExpression, + R6NewObjectContext::class.java, + R6CreateClassContext::class.java) ?: return } } diff --git a/src/org/jetbrains/r/psi/RElementFilters.kt b/src/org/jetbrains/r/psi/RElementFilters.kt index d2e5215a1..f0523ae20 100644 --- a/src/org/jetbrains/r/psi/RElementFilters.kt +++ b/src/org/jetbrains/r/psi/RElementFilters.kt @@ -11,8 +11,7 @@ import com.intellij.psi.filters.NotFilter import com.intellij.psi.filters.position.FilterPattern import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType -import org.jetbrains.r.classes.r6.context.R6ContextProvider -import org.jetbrains.r.classes.s4.context.RS4ContextProvider +import org.jetbrains.r.classes.common.context.LibraryContextProvider import org.jetbrains.r.parsing.RElementTypes import org.jetbrains.r.psi.api.* @@ -121,7 +120,7 @@ class StringFilter : ElementFilter { class S4ContextFilter : ElementFilter { override fun isAcceptable(element: Any?, context: PsiElement?): Boolean { val expression = PsiTreeUtil.getParentOfType(context, RExpression::class.java, false) ?: return false - return RS4ContextProvider.getS4Context(expression) != null + return LibraryContextProvider.getContext(expression) != null } override fun isClassAcceptable(hintClass: Class<*>?) = true @@ -130,7 +129,7 @@ class S4ContextFilter : ElementFilter { class R6ContextFilter : ElementFilter { override fun isAcceptable(element: Any?, context: PsiElement?): Boolean { val expression = PsiTreeUtil.getParentOfType(context, RExpression::class.java, false) ?: return false - return R6ContextProvider.getR6Context(expression) != null + return LibraryContextProvider.getContext(expression) != null } override fun isClassAcceptable(hintClass: Class<*>?) = true diff --git a/test/org/jetbrains/r/rename/RRenameTest.kt b/test/org/jetbrains/r/rename/RRenameTest.kt index 271bc9475..7ca6419b3 100644 --- a/test/org/jetbrains/r/rename/RRenameTest.kt +++ b/test/org/jetbrains/r/rename/RRenameTest.kt @@ -95,6 +95,8 @@ class RRenameTest : RLightCodeInsightFixtureTestCase() { fun testRenameR6MethodFromUsage() = doTestWithProject("additiveOperator", isInOtherDirectory = true) + fun testRenameR6ActiveBindingFromUsage() = doTestWithProject("rnd", isInOtherDirectory = true) + private fun doTestWithProject(newName: String, isInlineAvailable: Boolean = true, isRmd: Boolean = false, isSourceTest: Boolean = false, isInOtherDirectory: Boolean = false) { val dotFileExtension = getDotExtension(isRmd) lateinit var startFiles: List diff --git a/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R new file mode 100644 index 000000000..cc9cc9c09 --- /dev/null +++ b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R @@ -0,0 +1,17 @@ +Accumulator <- R6Class("Accumulator", list( + summary = 0, + add = function(x = 1) { + self$summary <- self$summary + x + invisible(self) + }, + random = function(value) { + if (missing(value)) { + runif(1) + } else { + stop("Can't set `$random`", call. = FALSE) + } + }) +) + +x <- Accumulator$new() +x$random \ No newline at end of file diff --git a/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R new file mode 100644 index 000000000..368f402c8 --- /dev/null +++ b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R @@ -0,0 +1,17 @@ +Accumulator <- R6Class("Accumulator", list( + summary = 0, + add = function(x = 1) { + self$summary <- self$summary + x + invisible(self) + }, + rnd = function(value) { + if (missing(value)) { + runif(1) + } else { + stop("Can't set `$random`", call. = FALSE) + } + }) +) + +x <- Accumulator$new() +x$rnd \ No newline at end of file From 9e708df0552efc05b94dcc7af1dc718c3398d056 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Wed, 7 Apr 2021 18:36:09 +0300 Subject: [PATCH 20/52] refactor classNameIndexes --- resources/META-INF/rplugin-common.xml | 4 +-- .../r/classes/s4/RS4ClassInfoUtil.kt | 5 +-- .../r/editor/RCompletionContributor.kt | 12 +++---- .../UnresolvedReferenceInspection.kt | 2 +- .../InstanceOfVirtualS4ClassInspection.kt | 2 +- .../r/psi/RCallExpressionElementType.kt | 9 ++++-- .../stubs/classes/LibraryClassNameIndex.kt | 31 +++++++++++++++++++ .../stubs/{ => classes}/R6ClassNameIndex.kt | 21 ++----------- .../stubs/{ => classes}/RS4ClassNameIndex.kt | 22 +++---------- .../psi/RSkeletonCallExpressionElementType.kt | 8 ++--- 10 files changed, 60 insertions(+), 56 deletions(-) create mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndex.kt rename src/org/jetbrains/r/psi/stubs/{ => classes}/R6ClassNameIndex.kt (59%) rename src/org/jetbrains/r/psi/stubs/{ => classes}/RS4ClassNameIndex.kt (57%) diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index 8500e56de..cd7b56405 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -435,8 +435,8 @@ You can find the source code in the following repositories: - - + + diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt index 6ec65f907..bb7b1de01 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt @@ -14,7 +14,8 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.isFunctionFromLibrarySoft import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.skeleton.psi.RSkeletonCallExpression object RS4ClassInfoUtil { @@ -88,7 +89,7 @@ object RS4ClassInfoUtil { return s4ClassInfo.superClasses.flatMap { superClassName -> // R keeps all superclasses together in the list, not as a tree // So, I don't see any reason to do anything else - val parentSuperClasses = RS4ClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { + val parentSuperClasses = LibraryClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { getAllAssociatedSuperClasses(it) } listOf(superClassName) + parentSuperClasses diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index c650ed789..ddd820f88 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -40,8 +40,8 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.R6ClassNameIndex -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator import org.jetbrains.r.rinterop.RValueFunction import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement @@ -145,7 +145,7 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach - R6ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + LibraryClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -204,7 +204,7 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = RS4ClassInfoUtil.getAssociatedClassName(call) ?: return@forEach - RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + LibraryClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(RS4ClassInfoUtil.getAllAssociatedSlots(it), shownNames, result) } } @@ -518,7 +518,7 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(psiElement: RCallExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> + LibraryClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> RS4ClassInfoUtil.getAllAssociatedSlots(definition).forEach { result.consume(RLookupElementFactory.createNamedArgumentLookupElement(it.name, it.type, SLOT_NAME_PRIORITY)) } @@ -550,7 +550,7 @@ class RCompletionContributor : CompletionContributor() { val runtimeInfo = file.runtimeInfo val loadedPackages = runtimeInfo?.loadedPackages?.keys val shownNames = HashSet() - RS4ClassNameIndex.processAllS4ClassInfos(project, scope, Processor { (declaration, info) -> + RS4ClassNameIndex.processAllClassInfos(project, scope, Processor { (declaration, info) -> if (omitVirtual && info.isVirtual) return@Processor true if (nameToOmit != info.className) { result.addS4ClassName(classNameExpression, declaration, info, shownNames, loadedPackages) diff --git a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt index 1856f4344..d78b73d76 100644 --- a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt +++ b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt @@ -20,7 +20,7 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RReferenceBase import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class UnresolvedReferenceInspection : RInspection() { diff --git a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt b/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt index aaaaf36e3..fb1e28f40 100644 --- a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt +++ b/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt @@ -15,7 +15,7 @@ import org.jetbrains.r.psi.api.RStringLiteralExpression import org.jetbrains.r.psi.api.RVisitor import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class InstanceOfVirtualS4ClassInspection : RInspection() { override fun getDisplayName() = RBundle.message("inspection.virtual.s4class.instance.name") diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index 7375c939a..590ce613f 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -15,7 +15,10 @@ import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfoUtil import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.impl.RCallExpressionImpl -import org.jetbrains.r.psi.stubs.* +import org.jetbrains.r.psi.stubs.RCallExpressionStub +import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl +import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex import java.io.IOException class RCallExpressionElementType(debugName: String) : RStubElementType(debugName) { @@ -47,8 +50,8 @@ class RCallExpressionElementType(debugName: String) : RStubElementType() { + override fun getKey(): StubIndexKey { + return KEY + } + + companion object { + private val KEY = StubIndexKey.createIndexKey("R.libraryClass.shortName") + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) + } + + fun sink(sink: IndexSink, name: String) { + sink.occurrence(KEY, name) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt similarity index 59% rename from src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt rename to src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt index c4b89ee36..d3f7dae69 100644 --- a/src/org/jetbrains/r/psi/stubs/R6ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt @@ -2,28 +2,21 @@ * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.psi.stubs +package org.jetbrains.r.psi.stubs.classes import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.stubs.IndexSink -import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.r6.R6ClassInfo -import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class R6ClassNameIndex : StringStubIndexExtension() { - override fun getKey(): StubIndexKey { - return KEY - } - +class R6ClassNameIndex : LibraryClassNameIndex() { companion object { private val KEY = StubIndexKey.createIndexKey("R.r6class.shortName") - fun processAllS4ClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { + fun processAllClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { val stubIndex = StubIndex.getInstance() stubIndex.processAllKeys(KEY, project) { key -> stubIndex.processElements(KEY, key, project, scope, RCallExpression::class.java) { declaration -> @@ -35,13 +28,5 @@ class R6ClassNameIndex : StringStubIndexExtension() { fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedR6ClassInfo } } - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) - } - - fun sink(sink: IndexSink, name: String) { - sink.occurrence(KEY, name) - } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt similarity index 57% rename from src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt rename to src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt index 8712903c2..12fb098f9 100644 --- a/src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt @@ -1,28 +1,22 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.psi.stubs +package org.jetbrains.r.psi.stubs.classes import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.stubs.IndexSink -import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression - class RS4ClassNameIndex : StringStubIndexExtension() { - override fun getKey(): StubIndexKey { - return KEY - } - +class RS4ClassNameIndex : LibraryClassNameIndex() { companion object { private val KEY = StubIndexKey.createIndexKey("R.s4class.shortName") - fun processAllS4ClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { + fun processAllClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { val stubIndex = StubIndex.getInstance() stubIndex.processAllKeys(KEY, project) { key -> stubIndex.processElements(KEY, key, project, scope, RCallExpression::class.java) { declaration -> @@ -34,13 +28,5 @@ import org.jetbrains.r.psi.api.RCallExpression fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedS4ClassInfo } } - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) - } - - fun sink(sink: IndexSink, name: String) { - sink.occurrence(KEY, name) - } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt index bb84727a9..73efc4c6c 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt @@ -14,10 +14,8 @@ import com.intellij.util.IncorrectOperationException import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.stubs.R6ClassNameIndex -import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex class RSkeletonCallExpressionElementType : RStubElementType("R bin s4 r6") { override fun createPsi(stub: RSkeletonCallExpressionStub): RCallExpression { @@ -50,7 +48,7 @@ class RSkeletonCallExpressionElementType : RStubElementType Date: Wed, 7 Apr 2021 19:47:30 +0300 Subject: [PATCH 21/52] fix base class library context provider --- .../r/classes/common/context/LibraryContextProvider.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt b/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt index 9c5268e7b..f42666af9 100644 --- a/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt +++ b/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt @@ -15,10 +15,7 @@ abstract class LibraryContextProvider { private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class companion object { - private val EP_NAME: ExtensionPointName> = - ExtensionPointName.create("com.intellij.libraryContextProvider") - - fun getProviders(): List> = EP_NAME.extensionList + fun getProviders(): List> = emptyList() fun getContext(element: RPsiElement): LibraryClassContext? { return getContext(element, LibraryClassContext::class.java) From ba29305a0cf853c8d592a4bb0fbf149f14271fc0 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 8 Apr 2021 19:24:49 +0300 Subject: [PATCH 22/52] refactoring moving along Psi dataStructure --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 26 ++- .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 148 +++++++++++++----- .../r/editor/RCompletionContributor.kt | 20 ++- .../r/psi/references/RReferenceImpl.kt | 4 +- .../r/classes/r6/R6ClassInfoUtilTests.kt | 2 +- .../classes/R6ClassCompletionTest.kt | 22 +++ .../r/findUsages/R6FindUsagesTest.kt | 2 +- .../classes/R6/renameR6FieldFromUsage.R | 2 +- .../classes/R6/renameR6FieldFromUsage.after.R | 2 +- 9 files changed, 174 insertions(+), 54 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index a99d673aa..ee0ed369f 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -5,6 +5,7 @@ package org.jetbrains.r.classes.r6 import com.intellij.openapi.util.Key +import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.packages.RPackageProjectManager @@ -13,7 +14,6 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.impl.RMemberExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft -import java.util.* object R6ClassInfoUtil { const val R6PackageName = "R6" @@ -36,14 +36,32 @@ object R6ClassInfoUtil { portable = TRUE, lock_class = FALSE, cloneable = TRUE, parent_env = parent.frame(), lock) {}""".trimIndent() + /** + * @param call expression `MyClass$new()` + * @return class name which type is instantiated + */ fun getAssociatedClassNameFromInstantiationCall(call: RCallExpression) : String? { val callExpression = call.expression as? RMemberExpressionImpl ?: return null if (callExpression.rightExpr?.text != functionNew) return null return callExpression.leftExpr?.text } - fun getAssociatedClassName(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { + /** + * @param rMemberExpression expression `self$someMember` or `obj$someMember` + * @return className of class where `self$...` is used or of which object is called + */ + fun getClassNameFromInternalClassMemberUsageExpression(rMemberExpression: RMemberExpression?) : String? { + if (rMemberExpression == null) return null + val classDefinitionCall = R6ClassPsiUtil.getClassDefinitionCallFromMemberUsage(rMemberExpression) ?: return null + return getAssociatedClassNameFromR6ClassCall(classDefinitionCall) + } + + /** + * @param callExpression expression `R6Class("MyClass", ...)` + * @return + */ + fun getAssociatedClassNameFromR6ClassCall(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null val arg = argumentInfo.getArgumentPassedToParameter(argumentClassName) as? RStringLiteralExpression @@ -133,7 +151,7 @@ object R6ClassInfoUtil { } val argumentInfo = RParameterInfoUtil.getArgumentInfo(callExpression, definition) ?: return null - val className = getAssociatedClassName(callExpression, argumentInfo) ?: return null + val className = getAssociatedClassNameFromR6ClassCall(callExpression, argumentInfo) ?: return null val superClassName = getAssociatedSuperClassName(callExpression, argumentInfo) ?: "" val fields = getAssociatedFields(callExpression, argumentInfo) ?: emptyList() val methods = getAssociatedMethods(callExpression, argumentInfo) ?: emptyList() diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt index 9fbb6e2bd..8c77cb1bf 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -4,11 +4,12 @@ package org.jetbrains.r.classes.r6 +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil -import org.jetbrains.r.parsing.RElementTypes.R_IDENTIFIER_EXPRESSION -import org.jetbrains.r.parsing.RElementTypes.R_MEMBER_EXPRESSION +import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft @@ -22,40 +23,16 @@ object R6ClassPsiUtil { fun getSearchedIdentifier(dependantIdentifier: RIdentifierExpression?) : RPsiElement? { if (dependantIdentifier == null) return null - val objectDeclarationStatement = getClassInstantiationExpression(dependantIdentifier) - val classDefinition = getClassDefinitionExpression(dependantIdentifier, objectDeclarationStatement) ?: return null - val argumentInfo = getClassDefinitionArgumentInfo(classDefinition) ?: return null + val classDefinitionCall = getClassDefinitionCallFromMemberUsage(dependantIdentifier) + val argumentInfo = getClassDefinitionArgumentInfo(classDefinitionCall) ?: return null - val publicMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) as? RCallExpressionImpl - val privateMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate) as? RCallExpressionImpl - val activeMembersCall = argumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive) as? RCallExpressionImpl + val publicMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPublic) + val privateMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPrivate) + val activeMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentActive) - val publicMembers = publicMembersCall?.argumentList?.expressionList - val privateMembers = privateMembersCall?.argumentList?.expressionList - val activeMembers = activeMembersCall?.argumentList?.expressionList - - return extractNamedArgumentByName(dependantIdentifier.name, publicMembers?.map { it as RNamedArgument }) - ?: extractNamedArgumentByName(dependantIdentifier.name, privateMembers?.map { it as RNamedArgument }) - ?: extractNamedArgumentByName(dependantIdentifier.name, activeMembers?.map { it as RNamedArgument }) - } - - /** - * @param dependantIdentifier `someMember` psi-element of expression `obj$someMember` - * @return RAssignmentStatement `obj <- MyClass$new()` - */ - private fun getClassInstantiationExpression(dependantIdentifier: RIdentifierExpression?): RAssignmentStatement? { - if (dependantIdentifier == null) return null - - val usageExpression = dependantIdentifier.parent - if (usageExpression.elementType != R_MEMBER_EXPRESSION) return null - - var r6Object = usageExpression.firstChild - while (r6Object != null && r6Object.elementType != R_IDENTIFIER_EXPRESSION){ - r6Object = r6Object.firstChild - } - - if (r6Object == null) return null - return r6Object.reference?.resolve() as? RAssignmentStatement + return extractNamedArgumentByName(dependantIdentifier.name, publicMembers?.mapNotNull { it as? RNamedArgument }) + ?: extractNamedArgumentByName(dependantIdentifier.name, privateMembers?.mapNotNull { it as? RNamedArgument }) + ?: extractNamedArgumentByName(dependantIdentifier.name, activeMembers?.mapNotNull { it as? RNamedArgument }) } /** @@ -92,16 +69,109 @@ object R6ClassPsiUtil { } /** - * @param classDefinition class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` + * @param rIdentifierExpression `someMember` of expression like `classObject$someMember` or `self$someMember` + * @return `R6Class` function call, which defines class containing `someMember` + */ + fun getClassDefinitionCallFromMemberUsage(rIdentifierExpression: RIdentifierExpression?) : RCallExpression? { + if (rIdentifierExpression == null) return null + val usedClassVariable = getClassIdentifierFromChainedUsages(rIdentifierExpression.parent as? RMemberExpression) + return getClassDefinitionFromClassVariableUsage(usedClassVariable) + } + + /** + * @param rMemberExpression expression like `classObject$someMember` or `self$someMember` + * @return `R6Class` function call, which defines class containing `someMember` + */ + fun getClassDefinitionCallFromMemberUsage(rMemberExpression: RMemberExpression?) : RCallExpression? { + if (rMemberExpression == null) return null + val classObject = getClassIdentifierFromChainedUsages(rMemberExpression) + return getClassDefinitionFromClassVariableUsage(classObject) + } + + private fun getClassDefinitionFromClassVariableUsage(classObject: PsiElement?) : RCallExpression? { + // `self$someMember` + if (classObject?.text == R6ClassInfoUtil.R6ClassThisKeyword) { + val parentFunction = PsiTreeUtil.getStubOrPsiParentOfType(classObject, RCallExpression::class.java) + val r6ClassDefinitionCall = PsiTreeUtil.getStubOrPsiParentOfType(parentFunction, RCallExpression::class.java) + + if (r6ClassDefinitionCall?.isFunctionFromLibrarySoft(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName) == true) { + return r6ClassDefinitionCall + } + } + // `classObject$someMember` + else { + // `classObject <- MyClass$new()` from `classObject$someMember$someMethod()$someMethod2()$someMember` + val r6ObjectCreationExpression = classObject?.reference?.resolve() as? RAssignmentStatement + // `MyClass` from `classObject <- MyClass$new()` + val usedClassVariable = r6ObjectCreationExpression?.assignedValue?.firstChild?.firstChild + // `MyClass <- R6Class(...)` from `MyClass` + val classDefinitionAssignment = usedClassVariable?.reference?.resolve() as? RAssignmentStatement + + return classDefinitionAssignment?.assignedValue as? RCallExpression + } + + return null + } + + /** + * @param classDefinitionAssignment class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` + * @return argument info containing all internal members of class + */ + fun getClassDefinitionArgumentInfo(classDefinitionAssignment: RAssignmentStatement?) : RArgumentInfo? { + if (classDefinitionAssignment == null) return null + val classDefinitionCall = classDefinitionAssignment.children.last() as? RCallExpression + return getClassDefinitionArgumentInfo(classDefinitionCall) + } + + /** + * @param classDefinitionCall class definition expression `R6Class("MyClass", list( someField = 0))` * @return argument info containing all internal members of class */ - private fun getClassDefinitionArgumentInfo(classDefinition: RAssignmentStatement?) : RArgumentInfo? { - if (classDefinition == null) return null + fun getClassDefinitionArgumentInfo(classDefinitionCall: RCallExpression?) : RArgumentInfo? { + if (classDefinitionCall == null) return null + return RParameterInfoUtil.getArgumentInfo(classDefinitionCall) + } + + /** + * @param argumentInfo information about arguments of R6 class definition call + * @param argumentName name of argument (i.e. `public`, `private`, `active`) from where to pick class members + */ + private fun getClassMemberExpressionsOfArgument(argumentInfo: RArgumentInfo, argumentName: String) : List? { + val members = argumentInfo.getArgumentPassedToParameter(argumentName) as? RCallExpressionImpl + return members?.argumentList?.expressionList + } + + /** + * @param dependantIdentifier `someMember` psi-element of expression `classObject$someMember$someMethod()$someActive$someMethod2()` + * @return RIdentifier of R6-class object + */ + private fun getR6ObjectIdentifierFromChainedUsage(dependantIdentifier: RIdentifierExpression?): RIdentifierExpression? { + if (dependantIdentifier == null) return null + + val usageExpression = dependantIdentifier.parent + if (usageExpression.elementType != R_MEMBER_EXPRESSION) return null - val r6ClassCall = classDefinition.children?.last() as RCallExpression - return RParameterInfoUtil.getArgumentInfo(r6ClassCall) + var r6Object = usageExpression.firstChild + while (r6Object != null && r6Object.elementType != R_IDENTIFIER_EXPRESSION){ + r6Object = r6Object.firstChild + } + + return r6Object as RIdentifierExpression } + /** + * @param rMemberExpression `classObject$someMember$someMethod()$someActive()` + * @return `classObject` identifier as the most left psi-element in chained usage expression + */ + private fun getClassIdentifierFromChainedUsages(rMemberExpression: RMemberExpression?) : RIdentifierExpression? { + if (rMemberExpression == null) return null + val classIdentifier = PsiTreeUtil.firstChild(rMemberExpression).parent as? RIdentifierExpression + if (classIdentifier?.parent.elementType != R_MEMBER_EXPRESSION) return null + return classIdentifier + } + + + private fun extractNamedArgumentByName(elementName: String, namedArguments: List?) : RPsiElement? { namedArguments?.forEach { if (it != null) { diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index ddd820f88..9ceb0b8bc 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -138,22 +138,32 @@ class RCompletionContributor : CompletionContributor() { TODO("Not yet implemented") } - override fun addCompletionStatically(psiElement: RMemberExpression, + override fun addCompletionStatically(rMemberExpression: RMemberExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - psiElement.leftExpr?.reference?.multiResolve(false)?.forEach { resolveResult -> + // self$member expression + val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(rMemberExpression) + if (className != null){ + LibraryClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) + } + } + + // classObject$member expression + rMemberExpression.leftExpr?.reference?.multiResolve(false)?.forEach { resolveResult -> val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach - LibraryClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { - return addSlotsCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) + LibraryClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } } + return false } - private fun addSlotsCompletion(r6ClassMembers: List?, shownNames: MutableSet, result: CompletionResultSet): Boolean { + private fun addMembersCompletion(r6ClassMembers: List?, shownNames: MutableSet, result: CompletionResultSet): Boolean { var hasNewResults = false if (r6ClassMembers.isNullOrEmpty()) return hasNewResults diff --git a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt index 340a1d881..44aa56fb1 100644 --- a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt +++ b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt @@ -89,10 +89,10 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase { - val r6SearchedIdentifier = R6ClassPsiUtil.getSearchedIdentifier(element) ?: return emptyArray() + val r6SearchedIdentifierDefinition = R6ClassPsiUtil.getSearchedIdentifier(element) ?: return emptyArray() val result = ArrayList() - result.add(PsiElementResolveResult(r6SearchedIdentifier)) + result.add(PsiElementResolveResult(r6SearchedIdentifierDefinition)) return result.toTypedArray() } diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index d05dc0d45..ab9869bfa 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -66,7 +66,7 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { fun testGetAssociatedClassName(){ val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val className = R6ClassInfoUtil.getAssociatedClassName(rCallExpression!!) + val className = R6ClassInfoUtil.getAssociatedClassNameFromR6ClassCall(rCallExpression!!) assertEquals("Car", className) } diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index f7201d902..93064a3e3 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -26,6 +26,20 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "someField" to "") } + fun testUserClassWithSingleFieldChainedUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0 )) + obj <- MyClass${"$"}new() + obj${'$'}someField${'$'} + """.trimIndent(), "someField" to "") + } + + fun testUserClassForInternalUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, add = function(x = 1) { self$ } )) + """.trimIndent(), "add" to "", "someField" to "") + } + fun testUserClassWithSeveralMembers() { doTest(""" MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) } )) @@ -34,6 +48,14 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "someField" to "", "someMethod" to "") } + fun testUserClassWithFieldMethodActiveBinding() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } )) + obj <- MyClass${"$"}new() + obj$ + """.trimIndent(), "random" to "", "someField" to "", "someMethod" to "") + } + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { val result = doTestBase(text, withRuntimeInfo, inConsole) assertNotNull(result) diff --git a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt index befbc9fb7..319fea063 100644 --- a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt +++ b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt @@ -103,7 +103,7 @@ class R6FindUsagesTest : FindUsagesTestBase() { """, """ Usage (2 usages) Variable - someField = 0 + someField Found usages (2 usages) Unclassified (2 usages) light_idea_test_case (2 usages) diff --git a/testData/rename/classes/R6/renameR6FieldFromUsage.R b/testData/rename/classes/R6/renameR6FieldFromUsage.R index 1d068856e..5ed7f7f47 100644 --- a/testData/rename/classes/R6/renameR6FieldFromUsage.R +++ b/testData/rename/classes/R6/renameR6FieldFromUsage.R @@ -7,5 +7,5 @@ Accumulator <- R6Class("Accumulator", list( ) x <- Accumulator$new() -x$add(4)$sum +x$sum$add(4)$sum$add(4)$sum x$sum diff --git a/testData/rename/classes/R6/renameR6FieldFromUsage.after.R b/testData/rename/classes/R6/renameR6FieldFromUsage.after.R index f4c876138..a480bb3f3 100644 --- a/testData/rename/classes/R6/renameR6FieldFromUsage.after.R +++ b/testData/rename/classes/R6/renameR6FieldFromUsage.after.R @@ -7,5 +7,5 @@ Accumulator <- R6Class("Accumulator", list( ) x <- Accumulator$new() -x$add(4)$summary +x$summary$add(4)$summary$add(4)$summary x$summary From 2d40380cbb9c174c87b31a3506ab28b2dc1a286f Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 8 Apr 2021 19:25:09 +0300 Subject: [PATCH 23/52] removed unused provider --- resources/META-INF/rplugin-common.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index cd7b56405..fd6223413 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -738,7 +738,6 @@ You can find the source code in the following repositories: - From 67f18b0ade94ef16853c5bda3c80bf72900c29a1 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 11 Apr 2021 16:57:20 +0300 Subject: [PATCH 24/52] rollback refactoring changes for fixing --- .../common/context/LibraryContextProvider.kt | 37 ------------------- .../r/classes/r6/context/R6ContextProvider.kt | 32 ++++++++++++++-- .../context/R6CreateClassContextProvider.kt | 2 +- .../r6/context/R6NewObjectContextProvider.kt | 2 +- .../r/classes/s4/RS4ClassInfoUtil.kt | 3 +- .../classes/s4/context/RS4ContextProvider.kt | 32 ++++++++++++++-- .../s4/context/RS4NewObjectContextProvider.kt | 4 +- .../s4/context/RS4SetClassContextProvider.kt | 4 +- .../r/editor/RCompletionContributor.kt | 20 +++++----- .../r/psi/RCallExpressionElementType.kt | 7 ++-- src/org/jetbrains/r/psi/RElementFilters.kt | 9 +++-- .../stubs/classes/LibraryClassNameIndex.kt | 31 ---------------- .../r/psi/stubs/classes/R6ClassNameIndex.kt | 16 +++++++- .../r/psi/stubs/classes/RS4ClassNameIndex.kt | 16 +++++++- .../psi/RSkeletonCallExpressionElementType.kt | 7 ++-- 15 files changed, 118 insertions(+), 104 deletions(-) delete mode 100644 src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt delete mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndex.kt diff --git a/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt b/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt deleted file mode 100644 index f42666af9..000000000 --- a/src/org/jetbrains/r/classes/common/context/LibraryContextProvider.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -package org.jetbrains.r.classes.common.context - -import com.intellij.openapi.extensions.ExtensionPointName -import org.jetbrains.r.psi.api.RPsiElement -import java.lang.reflect.ParameterizedType - -abstract class LibraryContextProvider { - abstract fun getContext(element: RPsiElement): T? - - @Suppress("UNCHECKED_CAST") - private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class - - companion object { - fun getProviders(): List> = emptyList() - - fun getContext(element: RPsiElement): LibraryClassContext? { - return getContext(element, LibraryClassContext::class.java) - } - - fun getContext(element: RPsiElement, vararg searchedContexts: Class): T? { - for (provider in getProviders()) { - if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { - val context = provider.getContext(element) - if (context != null) { - @Suppress("UNCHECKED_CAST") - return context as T? - } - } - } - return null - } - } -} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt index b6387c6fe..3f8fdeb1d 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt @@ -6,11 +6,37 @@ package org.jetbrains.r.classes.r6.context import com.intellij.openapi.extensions.ExtensionPointName import org.jetbrains.r.classes.common.context.LibraryClassContext -import org.jetbrains.r.classes.common.context.LibraryContextProvider +import org.jetbrains.r.psi.api.RPsiElement +import java.lang.reflect.ParameterizedType + +abstract class R6ContextProvider { + + abstract fun getContext(element: RPsiElement): T? + + @Suppress("UNCHECKED_CAST") + private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class -abstract class R6ContextProvider : LibraryContextProvider() { companion object { - private val EP_NAME: ExtensionPointName> = + private val EP_NAME: ExtensionPointName> = ExtensionPointName.create("com.intellij.r6ContextProvider") + + fun getProviders(): List> = EP_NAME.extensionList + + fun getR6Context(element: RPsiElement): LibraryClassContext? { + return getR6Context(element, LibraryClassContext::class.java) + } + + fun getR6Context(element: RPsiElement, vararg searchedContexts: Class): T? { + for (provider in getProviders()) { + if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { + val s4Context = provider.getContext(element) + if (s4Context != null) { + @Suppress("UNCHECKED_CAST") + return s4Context as T? + } + } + } + return null + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt index 109ee7b1b..1eb23e61a 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -25,7 +25,7 @@ data class R6CreateClassNameContext(override val originalElement: RPsiElement, override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : R6CreateClassContext() -class R6CreateClassContextProvider : R6ContextProvider() { +class R6CreateClassContextProvider : R6ContextProvider() { override fun getContext(element: RPsiElement): R6CreateClassContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getR6ContextInner(element), element) diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt index 28b3951bc..b884de40b 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt @@ -24,7 +24,7 @@ data class R6NewObjectClassNameContext(override val originalElement: RPsiElement override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : R6NewObjectContext() -class R6NewObjectContextProvider : R6ContextProvider() { +class R6NewObjectContextProvider : R6ContextProvider() { override fun getContext(element: RPsiElement): R6NewObjectContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getR6ContextInner(element), element) diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt index bb7b1de01..23f8c25ef 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt @@ -14,7 +14,6 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.isFunctionFromLibrarySoft import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.skeleton.psi.RSkeletonCallExpression @@ -89,7 +88,7 @@ object RS4ClassInfoUtil { return s4ClassInfo.superClasses.flatMap { superClassName -> // R keeps all superclasses together in the list, not as a tree // So, I don't see any reason to do anything else - val parentSuperClasses = LibraryClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { + val parentSuperClasses = RS4ClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { getAllAssociatedSuperClasses(it) } listOf(superClassName) + parentSuperClasses diff --git a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt index 762e64fee..f8b426bd9 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt @@ -2,11 +2,37 @@ package org.jetbrains.r.classes.s4.context import com.intellij.openapi.extensions.ExtensionPointName import org.jetbrains.r.classes.common.context.LibraryClassContext -import org.jetbrains.r.classes.common.context.LibraryContextProvider +import org.jetbrains.r.psi.api.RPsiElement +import java.lang.reflect.ParameterizedType + +abstract class RS4ContextProvider { + + abstract fun getContext(element: RPsiElement): T? + + @Suppress("UNCHECKED_CAST") + private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class -abstract class RS4ContextProvider: LibraryContextProvider(){ companion object { - private val EP_NAME: ExtensionPointName> = + private val EP_NAME: ExtensionPointName> = ExtensionPointName.create("com.intellij.rS4ContextProvider") + + fun getProviders(): List> = EP_NAME.extensionList + + fun getS4Context(element: RPsiElement): LibraryClassContext? { + return getS4Context(element, LibraryClassContext::class.java) + } + + fun getS4Context(element: RPsiElement, vararg searchedContexts: Class): T? { + for (provider in getProviders()) { + if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { + val s4Context = provider.getContext(element) + if (s4Context != null) { + @Suppress("UNCHECKED_CAST") + return s4Context as T? + } + } + } + return null + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt index 4623c1c3b..b80ca05d2 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt @@ -27,7 +27,7 @@ data class RS4NewObjectSlotNameContext(override val originalElement: RPsiElement override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : RS4NewObjectContext() -class RS4NewObjectContextProvider : RS4ContextProvider() { +class RS4NewObjectContextProvider : RS4ContextProvider() { override fun getContext(element: RPsiElement): RS4NewObjectContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getS4ContextInner(element), element) @@ -55,4 +55,4 @@ class RS4NewObjectContextProvider : RS4ContextProvider() { } } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt index a4cd6658c..f25394314 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt @@ -35,7 +35,7 @@ data class RS4SetClassDependencyClassNameContext(override val originalElement: R override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : RS4SetClassContext() -class RS4SetClassContextProvider : RS4ContextProvider() { +class RS4SetClassContextProvider : RS4ContextProvider() { override fun getContext(element: RPsiElement): RS4SetClassContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getS4ContextInner(element), element) @@ -86,4 +86,4 @@ class RS4SetClassContextProvider : RS4ContextProvider() { } } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 9ceb0b8bc..109dcdd2b 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -20,9 +20,9 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.r.RLanguage -import org.jetbrains.r.classes.common.context.LibraryContextProvider import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.r6.R6ClassMember +import org.jetbrains.r.classes.r6.context.R6ContextProvider import org.jetbrains.r.classes.r6.context.R6CreateClassContext import org.jetbrains.r.classes.r6.context.R6NewObjectContext import org.jetbrains.r.classes.s4.* @@ -40,7 +40,7 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator import org.jetbrains.r.rinterop.RValueFunction @@ -144,7 +144,7 @@ class RCompletionContributor : CompletionContributor() { // self$member expression val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(rMemberExpression) if (className != null){ - LibraryClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -154,7 +154,7 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach - LibraryClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -214,7 +214,7 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = RS4ClassInfoUtil.getAssociatedClassName(call) ?: return@forEach - LibraryClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(RS4ClassInfoUtil.getAllAssociatedSlots(it), shownNames, result) } } @@ -506,7 +506,7 @@ class RCompletionContributor : CompletionContributor() { } private fun addS4SlotNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { - val s4Context = LibraryContextProvider.getContext(classNameExpression, RS4NewObjectContext::class.java) ?: return + val s4Context = RS4ContextProvider.getS4Context(classNameExpression, RS4NewObjectContext::class.java) ?: return if (s4Context !is RS4NewObjectSlotNameContext) return val newCall = s4Context.functionCall @@ -528,7 +528,7 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(psiElement: RCallExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - LibraryClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> + RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> RS4ClassInfoUtil.getAllAssociatedSlots(definition).forEach { result.consume(RLookupElementFactory.createNamedArgumentLookupElement(it.name, it.type, SLOT_NAME_PRIORITY)) } @@ -540,7 +540,7 @@ class RCompletionContributor : CompletionContributor() { } private fun addS4ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { - val s4Context = LibraryContextProvider.getContext(classNameExpression, + val s4Context = RS4ContextProvider.getS4Context(classNameExpression, RS4NewObjectContext::class.java, RS4SetClassContext::class.java) ?: return var omitVirtual = false @@ -621,13 +621,13 @@ class RCompletionContributor : CompletionContributor() { } private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - val r6Context = LibraryContextProvider.getContext(classNameExpression, + val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6NewObjectContext::class.java, R6CreateClassContext::class.java) ?: return } private fun addR6MemberNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - val r6Context = LibraryContextProvider.getContext(classNameExpression, + val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6NewObjectContext::class.java, R6CreateClassContext::class.java) ?: return } diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index 590ce613f..34ac169d7 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -18,7 +18,8 @@ import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.stubs.RCallExpressionStub import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl import org.jetbrains.r.psi.stubs.RStubElementType -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import java.io.IOException class RCallExpressionElementType(debugName: String) : RStubElementType(debugName) { @@ -50,8 +51,8 @@ class RCallExpressionElementType(debugName: String) : RStubElementType?) = true @@ -129,7 +130,7 @@ class S4ContextFilter : ElementFilter { class R6ContextFilter : ElementFilter { override fun isAcceptable(element: Any?, context: PsiElement?): Boolean { val expression = PsiTreeUtil.getParentOfType(context, RExpression::class.java, false) ?: return false - return LibraryContextProvider.getContext(expression) != null + return R6ContextProvider.getR6Context(expression) != null } override fun isClassAcceptable(hintClass: Class<*>?) = true diff --git a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndex.kt deleted file mode 100644 index d51a5d96c..000000000 --- a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndex.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -package org.jetbrains.r.psi.stubs.classes - -import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.stubs.IndexSink -import com.intellij.psi.stubs.StringStubIndexExtension -import com.intellij.psi.stubs.StubIndex -import com.intellij.psi.stubs.StubIndexKey -import org.jetbrains.r.psi.api.RCallExpression - -abstract class LibraryClassNameIndex : StringStubIndexExtension() { - override fun getKey(): StubIndexKey { - return KEY - } - - companion object { - private val KEY = StubIndexKey.createIndexKey("R.libraryClass.shortName") - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) - } - - fun sink(sink: IndexSink, name: String) { - sink.occurrence(KEY, name) - } - } -} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt index d3f7dae69..27e9ba13a 100644 --- a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt @@ -6,13 +6,19 @@ package org.jetbrains.r.psi.stubs.classes import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class R6ClassNameIndex : LibraryClassNameIndex() { +class R6ClassNameIndex : StringStubIndexExtension() { + override fun getKey(): StubIndexKey { + return KEY + } + companion object { private val KEY = StubIndexKey.createIndexKey("R.r6class.shortName") @@ -28,5 +34,13 @@ class R6ClassNameIndex : LibraryClassNameIndex() { fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedR6ClassInfo } } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) + } + + fun sink(sink: IndexSink, name: String) { + sink.occurrence(KEY, name) + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt index 12fb098f9..ccae631ce 100644 --- a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt @@ -6,13 +6,19 @@ package org.jetbrains.r.psi.stubs.classes import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class RS4ClassNameIndex : LibraryClassNameIndex() { +class RS4ClassNameIndex : StringStubIndexExtension() { + override fun getKey(): StubIndexKey { + return KEY + } + companion object { private val KEY = StubIndexKey.createIndexKey("R.s4class.shortName") @@ -28,5 +34,13 @@ class RS4ClassNameIndex : LibraryClassNameIndex() { fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedS4ClassInfo } } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) + } + + fun sink(sink: IndexSink, name: String) { + sink.occurrence(KEY, name) + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt index 73efc4c6c..4cc91419c 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt @@ -15,7 +15,8 @@ import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.stubs.RStubElementType -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndex +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class RSkeletonCallExpressionElementType : RStubElementType("R bin s4 r6") { override fun createPsi(stub: RSkeletonCallExpressionStub): RCallExpression { @@ -48,7 +49,7 @@ class RSkeletonCallExpressionElementType : RStubElementType Date: Sun, 11 Apr 2021 17:08:40 +0300 Subject: [PATCH 25/52] clearing null asserting in RS4ClassInfoUtil --- src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt index 23f8c25ef..e74b5f658 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt @@ -35,7 +35,7 @@ object RS4ClassInfoUtil { if (callExpression == null) return emptyList() if (callExpression is RSkeletonCallExpression) { // S4 classes from packages and so contains all slots - return callExpression.associatedS4ClassInfo!!.slots + return callExpression.associatedS4ClassInfo?.slots ?: emptyList() } if (!callExpression.isFunctionFromLibrary("setClass", "methods")) return emptyList() return CachedValuesManager.getProjectPsiDependentCache(callExpression) { @@ -53,7 +53,7 @@ object RS4ClassInfoUtil { if (callExpression == null) return emptyList() if (callExpression is RSkeletonCallExpression) { // S4 classes from packages and so contains all super classes - return callExpression.associatedS4ClassInfo!!.superClasses + return callExpression.associatedS4ClassInfo?.superClasses ?: emptyList() } if (!callExpression.isFunctionFromLibrary("setClass", "methods")) return emptyList() return CachedValuesManager.getProjectPsiDependentCache(callExpression) { From 34becb71cd9161213de323385eb156d83230d139 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 11 Apr 2021 17:54:54 +0300 Subject: [PATCH 26/52] minor changes --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 6 ++++++ .../r/codeInsight/findUsages/RTargetElementEvaluator.kt | 4 +--- src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt | 2 +- .../jetbrains/r/completion/classes/S4ClassCompletionTest.kt | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index c7d4dd6b0..d3e020518 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -69,4 +69,10 @@ data class R6ClassInfo(val className: String, return R6ClassInfo(className, packageName, superClass, fields, methods, activeBindings) } } + + override fun toString() : String { + return buildString { + // TODO build string from R6ClassInfo + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt b/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt index 12e0572f8..67f1f76f4 100644 --- a/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt +++ b/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt @@ -14,9 +14,7 @@ class RTargetElementEvaluator: TargetElementEvaluatorEx2() { } return when (grandParent) { - is RParameter, - is RNamedArgument -> true - + is RParameter, is RNamedArgument -> true else -> false } } diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt index 579373dfe..fdb47c42d 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt @@ -28,7 +28,7 @@ class RSkeletonCallExpression(private val myStub: RSkeletonCallExpressionStub) : override fun getElementType(): IStubElementType, *> = stub.stubType - override fun getName(): String = myStub.s4ClassInfo!!.className + override fun getName(): String = myStub.s4ClassInfo?.className ?: "" override fun canNavigate(): Boolean = false diff --git a/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt index 9a7453aa3..cd78df20c 100644 --- a/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.r.completion.classes From e1b270782984d67dbb4c738ec5a3e2ac5f01f5fc Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 11 Apr 2021 18:49:05 +0300 Subject: [PATCH 27/52] create provider and instances of indexes --- .../r/classes/s4/RS4ClassInfoUtil.kt | 5 ++- .../r/editor/RCompletionContributor.kt | 11 ++--- .../UnresolvedReferenceInspection.kt | 3 +- .../InstanceOfVirtualS4ClassInspection.kt | 3 +- .../r/psi/RCallExpressionElementType.kt | 5 ++- .../classes/LibraryClassNameIndexBase.kt | 42 +++++++++++++++++++ .../classes/LibraryClassNameIndexProvider.kt | 12 ++++++ .../r/psi/stubs/classes/R6ClassNameIndex.kt | 39 +++++------------ .../r/psi/stubs/classes/RS4ClassNameIndex.kt | 39 +++++------------ .../psi/RSkeletonCallExpressionElementType.kt | 5 ++- 10 files changed, 93 insertions(+), 71 deletions(-) create mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexBase.kt create mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt index e74b5f658..882019f4c 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt @@ -14,6 +14,7 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.isFunctionFromLibrarySoft import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.skeleton.psi.RSkeletonCallExpression @@ -69,7 +70,7 @@ object RS4ClassInfoUtil { val project = callExpression.project return (s4ClassInfo.slots + allSuperClasses.flatMap { superClassName -> // Collect slots from super classes and data slot if needed - RS4ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { + LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { val superClassSlots = it.slots when { // methods:::.InhSlotNames @@ -88,7 +89,7 @@ object RS4ClassInfoUtil { return s4ClassInfo.superClasses.flatMap { superClassName -> // R keeps all superclasses together in the list, not as a tree // So, I don't see any reason to do anything else - val parentSuperClasses = RS4ClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { + val parentSuperClasses = LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { getAllAssociatedSuperClasses(it) } listOf(superClassName) + parentSuperClasses diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 109dcdd2b..60a2cfbdd 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -40,6 +40,7 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator @@ -144,7 +145,7 @@ class RCompletionContributor : CompletionContributor() { // self$member expression val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(rMemberExpression) if (className != null){ - R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -154,7 +155,7 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach - R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -214,7 +215,7 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = RS4ClassInfoUtil.getAssociatedClassName(call) ?: return@forEach - RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(RS4ClassInfoUtil.getAllAssociatedSlots(it), shownNames, result) } } @@ -528,7 +529,7 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(psiElement: RCallExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> + LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> RS4ClassInfoUtil.getAllAssociatedSlots(definition).forEach { result.consume(RLookupElementFactory.createNamedArgumentLookupElement(it.name, it.type, SLOT_NAME_PRIORITY)) } @@ -560,7 +561,7 @@ class RCompletionContributor : CompletionContributor() { val runtimeInfo = file.runtimeInfo val loadedPackages = runtimeInfo?.loadedPackages?.keys val shownNames = HashSet() - RS4ClassNameIndex.processAllClassInfos(project, scope, Processor { (declaration, info) -> + LibraryClassNameIndexProvider.RS4ClassNameIndex.processAllClassInfos(project, scope, Processor { (declaration, info) -> if (omitVirtual && info.isVirtual) return@Processor true if (nameToOmit != info.className) { result.addS4ClassName(classNameExpression, declaration, info, shownNames, loadedPackages) diff --git a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt index d78b73d76..29e83e4b2 100644 --- a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt +++ b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt @@ -20,6 +20,7 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RReferenceBase import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class UnresolvedReferenceInspection : RInspection() { @@ -65,7 +66,7 @@ class UnresolvedReferenceInspection : RInspection() { val classNameExpr = element.argumentList.expressionList.firstOrNull() as? RStringLiteralExpression ?: return val className = classNameExpr.name val packageNames = className?.let { - RS4ClassNameIndex.findClassInfos(it, element.project, RSearchScopeUtil.getScope(element)) + LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassInfos(it, element.project, RSearchScopeUtil.getScope(element)) }?.map { it.packageName }?.filter { it.isNotBlank() } ?: return if (packageNames.isEmpty() || packageNames.any { it in loadedPackages }) return registerMissingPackages(classNameExpr, className, packageNames, runtimeInfo) diff --git a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt b/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt index fb1e28f40..1e43eb293 100644 --- a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt +++ b/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt @@ -15,6 +15,7 @@ import org.jetbrains.r.psi.api.RStringLiteralExpression import org.jetbrains.r.psi.api.RVisitor import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class InstanceOfVirtualS4ClassInspection : RInspection() { @@ -29,7 +30,7 @@ class InstanceOfVirtualS4ClassInspection : RInspection() { if (!call.isFunctionFromLibrary("new", "methods")) return val classNameExpression = call.argumentList.expressionList.firstOrNull() as? RStringLiteralExpression ?: return val className = classNameExpression.name ?: return - val infos = RS4ClassNameIndex.findClassInfos(className, call.project, RSearchScopeUtil.getScope(call)) + val infos = LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassInfos(className, call.project, RSearchScopeUtil.getScope(call)) if (infos.isNotEmpty() && infos.all { it.isVirtual }) { myProblemHolder.registerProblem(classNameExpression, RBundle.message("inspection.virtual.s4class.instance.description", className), diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index 34ac169d7..138c244ca 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -18,6 +18,7 @@ import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.stubs.RCallExpressionStub import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import java.io.IOException @@ -51,8 +52,8 @@ class RCallExpressionElementType(debugName: String) : RStubElementType : StringStubIndexExtension() { + abstract val classKey: StubIndexKey + + abstract fun callProcessingForDeclaration(rCallExpression: RCallExpression, processor: Processor>) : Boolean + abstract fun getClassInfoFromRCallExpression(rCallExpression: RCallExpression) : TClassInfo? + + fun processAllClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { + val stubIndex = StubIndex.getInstance() + stubIndex.processAllKeys(classKey, project) { key -> + stubIndex.processElements(classKey, key, project, scope, RCallExpression::class.java) { + declaration -> callProcessingForDeclaration(declaration, processor) + } + } + } + + fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { + return StubIndex.getElements(classKey, name, project, scope, RCallExpression::class.java).mapNotNull { getClassInfoFromRCallExpression(it) } + } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(classKey, name, project, scope, RCallExpression::class.java) + } + + fun sink(sink: IndexSink, name: String) { + sink.occurrence(classKey, name) + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt new file mode 100644 index 000000000..badb3e005 --- /dev/null +++ b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.psi.stubs.classes + +class LibraryClassNameIndexProvider { + companion object { + val RS4ClassNameIndex = RS4ClassNameIndex() + val R6ClassNameIndex = R6ClassNameIndex() + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt index 27e9ba13a..65c156163 100644 --- a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt @@ -4,43 +4,24 @@ package org.jetbrains.r.psi.stubs.classes -import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.stubs.IndexSink -import com.intellij.psi.stubs.StringStubIndexExtension -import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class R6ClassNameIndex : StringStubIndexExtension() { +class R6ClassNameIndex : LibraryClassNameIndexBase() { + override val classKey = StubIndexKey.createIndexKey("R.r6class.shortName") + override fun getKey(): StubIndexKey { - return KEY + return classKey } - companion object { - private val KEY = StubIndexKey.createIndexKey("R.r6class.shortName") - - fun processAllClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { - val stubIndex = StubIndex.getInstance() - stubIndex.processAllKeys(KEY, project) { key -> - stubIndex.processElements(KEY, key, project, scope, RCallExpression::class.java) { declaration -> - declaration.associatedR6ClassInfo?.let { processor.process(declaration to it) } ?: true - } - } - } - - fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedR6ClassInfo } - } - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) - } + override fun callProcessingForDeclaration(rCallExpression: RCallExpression, + processor: Processor>): Boolean { + return rCallExpression.associatedR6ClassInfo?.let { processor.process(rCallExpression to it) } ?: true + } - fun sink(sink: IndexSink, name: String) { - sink.occurrence(KEY, name) - } + override fun getClassInfoFromRCallExpression(rCallExpression: RCallExpression): R6ClassInfo? { + return rCallExpression.associatedR6ClassInfo } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt index ccae631ce..6b3559d9a 100644 --- a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt @@ -4,43 +4,24 @@ package org.jetbrains.r.psi.stubs.classes -import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.stubs.IndexSink -import com.intellij.psi.stubs.StringStubIndexExtension -import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class RS4ClassNameIndex : StringStubIndexExtension() { +class RS4ClassNameIndex : LibraryClassNameIndexBase() { + override val classKey = StubIndexKey.createIndexKey("R.s4class.shortName") + override fun getKey(): StubIndexKey { - return KEY + return classKey } - companion object { - private val KEY = StubIndexKey.createIndexKey("R.s4class.shortName") - - fun processAllClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { - val stubIndex = StubIndex.getInstance() - stubIndex.processAllKeys(KEY, project) { key -> - stubIndex.processElements(KEY, key, project, scope, RCallExpression::class.java) { declaration -> - declaration.associatedS4ClassInfo?.let { processor.process(declaration to it) } ?: true - } - } - } - - fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedS4ClassInfo } - } - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) - } + override fun callProcessingForDeclaration(rCallExpression: RCallExpression, + processor: Processor>): Boolean { + return rCallExpression.associatedS4ClassInfo?.let { processor.process(rCallExpression to it) } ?: true + } - fun sink(sink: IndexSink, name: String) { - sink.occurrence(KEY, name) - } + override fun getClassInfoFromRCallExpression(rCallExpression: RCallExpression): RS4ClassInfo? { + return rCallExpression.associatedS4ClassInfo } } \ No newline at end of file diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt index 4cc91419c..09a086097 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt @@ -15,6 +15,7 @@ import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex @@ -49,7 +50,7 @@ class RSkeletonCallExpressionElementType : RStubElementType Date: Sun, 11 Apr 2021 23:19:15 +0300 Subject: [PATCH 28/52] completion refactor + class name suggestion --- .../jetbrains/r/psi/api/RCallExpression.java | 6 ++ gen/org/jetbrains/r/psi/api/RVisitor.java | 2 +- .../r/psi/impl/RAndOperatorImpl.java | 4 +- .../r/psi/impl/RArgumentListImpl.java | 4 +- .../r/psi/impl/RAssignOperatorImpl.java | 4 +- .../r/psi/impl/RAssignmentStatementImpl.java | 2 +- .../r/psi/impl/RAtExpressionImpl.java | 6 +- .../jetbrains/r/psi/impl/RAtOperatorImpl.java | 4 +- .../r/psi/impl/RBlockExpressionImpl.java | 4 +- .../r/psi/impl/RBooleanLiteralImpl.java | 4 +- .../r/psi/impl/RBoundaryLiteralImpl.java | 4 +- .../r/psi/impl/RBreakStatementImpl.java | 4 +- .../r/psi/impl/RCallExpressionImpl.java | 12 +++ .../r/psi/impl/RColonOperatorImpl.java | 4 +- .../r/psi/impl/RCompareOperatorImpl.java | 4 +- .../r/psi/impl/REmptyExpressionImpl.java | 4 +- .../r/psi/impl/RExpOperatorImpl.java | 4 +- .../jetbrains/r/psi/impl/RExpressionImpl.java | 4 +- .../r/psi/impl/RForStatementImpl.java | 4 +- .../r/psi/impl/RHelpExpressionImpl.java | 4 +- .../r/psi/impl/RIdentifierExpressionImpl.java | 4 +- .../r/psi/impl/RIfStatementImpl.java | 4 +- .../r/psi/impl/RInfixOperatorImpl.java | 4 +- .../r/psi/impl/RInvalidLiteralImpl.java | 4 +- .../r/psi/impl/RListSubsetOperatorImpl.java | 4 +- .../r/psi/impl/RMemberExpressionImpl.java | 4 +- .../r/psi/impl/RMuldivOperatorImpl.java | 4 +- .../jetbrains/r/psi/impl/RNaLiteralImpl.java | 4 +- .../r/psi/impl/RNamedArgumentImpl.java | 4 +- .../impl/RNamespaceAccessExpressionImpl.java | 4 +- .../r/psi/impl/RNextStatementImpl.java | 4 +- .../r/psi/impl/RNoCommaTailImpl.java | 4 +- .../r/psi/impl/RNotOperatorImpl.java | 4 +- .../r/psi/impl/RNullLiteralImpl.java | 4 +- .../impl/RNumericLiteralExpressionImpl.java | 4 +- .../r/psi/impl/ROperatorExpressionImpl.java | 4 +- .../jetbrains/r/psi/impl/ROperatorImpl.java | 4 +- .../jetbrains/r/psi/impl/ROrOperatorImpl.java | 4 +- .../r/psi/impl/RParameterListImpl.java | 4 +- .../impl/RParenthesizedExpressionImpl.java | 4 +- .../r/psi/impl/RPlusminusOperatorImpl.java | 4 +- .../r/psi/impl/RRepeatStatementImpl.java | 4 +- .../psi/impl/RSubscriptionExpressionImpl.java | 4 +- .../r/psi/impl/RTildeExpressionImpl.java | 4 +- .../r/psi/impl/RTildeOperatorImpl.java | 4 +- .../r/psi/impl/RUnaryNotExpressionImpl.java | 4 +- .../impl/RUnaryPlusminusExpressionImpl.java | 4 +- .../r/psi/impl/RUnaryTildeExpressionImpl.java | 4 +- .../r/psi/impl/RWhileStatementImpl.java | 4 +- ...lassContext.kt => ILibraryClassContext.kt} | 2 +- .../r/classes/r6/context/R6ContextProvider.kt | 14 +-- .../context/R6CreateClassContextProvider.kt | 55 ++++++----- .../r6/context/R6NewObjectContextProvider.kt | 4 +- .../classes/s4/context/RS4ContextProvider.kt | 14 +-- .../s4/context/RS4NewObjectContextProvider.kt | 4 +- .../s4/context/RS4SetClassContextProvider.kt | 4 +- .../r/editor/RCompletionContributor.kt | 99 ++++++++++++------- .../completion/RLookupElementFactory.kt | 1 + .../classes/R6ClassCompletionTest.kt | 25 ++++- 59 files changed, 243 insertions(+), 183 deletions(-) rename src/org/jetbrains/r/classes/common/context/{LibraryClassContext.kt => ILibraryClassContext.kt} (93%) diff --git a/gen/org/jetbrains/r/psi/api/RCallExpression.java b/gen/org/jetbrains/r/psi/api/RCallExpression.java index 8490555a1..34f842f78 100644 --- a/gen/org/jetbrains/r/psi/api/RCallExpression.java +++ b/gen/org/jetbrains/r/psi/api/RCallExpression.java @@ -1,9 +1,12 @@ // This is a generated file. Not intended for manual editing. package org.jetbrains.r.psi.api; +import java.util.List; import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; import com.intellij.psi.StubBasedPsiElement; import org.jetbrains.r.psi.stubs.RCallExpressionStub; +import org.jetbrains.r.classes.r6.R6ClassInfo; import org.jetbrains.r.classes.s4.RS4ClassInfo; public interface RCallExpression extends RExpression, StubBasedPsiElement { @@ -17,4 +20,7 @@ public interface RCallExpression extends RExpression, StubBasedPsiElement { +abstract class R6ContextProvider { abstract fun getContext(element: RPsiElement): T? @@ -17,16 +17,16 @@ abstract class R6ContextProvider { private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class companion object { - private val EP_NAME: ExtensionPointName> = + private val EP_NAME: ExtensionPointName> = ExtensionPointName.create("com.intellij.r6ContextProvider") - fun getProviders(): List> = EP_NAME.extensionList + fun getProviders(): List> = EP_NAME.extensionList - fun getR6Context(element: RPsiElement): LibraryClassContext? { - return getR6Context(element, LibraryClassContext::class.java) + fun getR6Context(element: RPsiElement): ILibraryClassContext? { + return getR6Context(element, ILibraryClassContext::class.java) } - fun getR6Context(element: RPsiElement, vararg searchedContexts: Class): T? { + fun getR6Context(element: RPsiElement, vararg searchedContexts: Class): T? { for (provider in getProviders()) { if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { val s4Context = provider.getContext(element) diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt index 1eb23e61a..74d72c01b 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -7,7 +7,7 @@ package org.jetbrains.r.classes.r6.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil @@ -16,15 +16,31 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class R6CreateClassContext : LibraryClassContext { +sealed class R6CreateClassContext : ILibraryClassContext { override val functionName = "R6Class" } -// Accumulator <- R6Class("Accumulator", ...) +// R6Class(, ) +// R6Class("", ) data class R6CreateClassNameContext(override val originalElement: RPsiElement, override val functionCall: RCallExpression, override val argumentInfo: RArgumentInfo) : R6CreateClassContext() +// R6Class("MyClass", inherit = ) +data class R6CreateClassInheritContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6CreateClassContext() + +// R6Class("MyClass", , public = ) +// R6Class("MyClass", , public = list()) +// R6Class("MyClass", , private = ) +// R6Class("MyClass", , private = list()) +// R6Class("MyClass", , active = ) +// R6Class("MyClass", , active = list()) +data class R6CreateClassMembersContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6CreateClassContext() + class R6CreateClassContextProvider : R6ContextProvider() { override fun getContext(element: RPsiElement): R6CreateClassContext? { return CachedValuesManager.getCachedValue(element) { @@ -37,40 +53,25 @@ class R6CreateClassContextProvider : R6ContextProvider() { return if (parentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) { val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null when (element) { -// parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentClassName) -> { -// // R6Class("") -// R6CreateClassNameContext(element, parentCall, parentArgumentInfo) -// } - parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentSuperClass) -> { - // R6Class("MyClass", "") - R6CreateClassNameContext(element, parentCall, parentArgumentInfo) - } - parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic) -> { - // R6Class("MyClass", , "") - R6CreateClassNameContext(element, parentCall, parentArgumentInfo) - } - parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate) -> { - // R6Class("MyClass", , , "") - R6CreateClassNameContext(element, parentCall, parentArgumentInfo) - } - parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive) -> { - // R6Class("MyClass", , , , "") + parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentClassName) -> { + // R6Class("") R6CreateClassNameContext(element, parentCall, parentArgumentInfo) } + else -> null } - } - else { + } else { val superParentCall = PsiTreeUtil.getParentOfType(parentCall, RCallExpression::class.java) ?: return null if (!superParentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) return null val superParentArgumentInfo = RParameterInfoUtil.getArgumentInfo(superParentCall) ?: return null return when { - // R6Class("MyClass", inherit = "" - PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentSuperClass), element, false) -> { + // R6Class("MyClass", inherit = "") + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentSuperClass), element, + false) -> { val parent = element.parent if (parent is RNamedArgument && parent.nameIdentifier == element) null - else R6CreateClassNameContext(element, superParentCall, superParentArgumentInfo) + else R6CreateClassInheritContext(element, superParentCall, superParentArgumentInfo) } // R6Class("MyClass", public = "" @@ -89,7 +90,7 @@ class R6CreateClassContextProvider : R6ContextProvider() { PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive), element, false) -> { val parent = element.parent if (parent !is RNamedArgument || parent.assignedValue != element) null - else R6CreateClassNameContext(element, superParentCall, superParentArgumentInfo) + else R6CreateClassMembersContext(element, superParentCall, superParentArgumentInfo) } else -> null diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt index b884de40b..98ce4e2f8 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt @@ -7,7 +7,7 @@ package org.jetbrains.r.classes.r6.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil @@ -15,7 +15,7 @@ import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class R6NewObjectContext : LibraryClassContext { +sealed class R6NewObjectContext : ILibraryClassContext { override val functionName = "new" } diff --git a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt index f8b426bd9..6b7e65303 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt @@ -1,11 +1,11 @@ package org.jetbrains.r.classes.s4.context import com.intellij.openapi.extensions.ExtensionPointName -import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.psi.api.RPsiElement import java.lang.reflect.ParameterizedType -abstract class RS4ContextProvider { +abstract class RS4ContextProvider { abstract fun getContext(element: RPsiElement): T? @@ -13,16 +13,16 @@ abstract class RS4ContextProvider { private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class companion object { - private val EP_NAME: ExtensionPointName> = + private val EP_NAME: ExtensionPointName> = ExtensionPointName.create("com.intellij.rS4ContextProvider") - fun getProviders(): List> = EP_NAME.extensionList + fun getProviders(): List> = EP_NAME.extensionList - fun getS4Context(element: RPsiElement): LibraryClassContext? { - return getS4Context(element, LibraryClassContext::class.java) + fun getS4Context(element: RPsiElement): ILibraryClassContext? { + return getS4Context(element, ILibraryClassContext::class.java) } - fun getS4Context(element: RPsiElement, vararg searchedContexts: Class): T? { + fun getS4Context(element: RPsiElement, vararg searchedContexts: Class): T? { for (provider in getProviders()) { if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { val s4Context = provider.getContext(element) diff --git a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt index b80ca05d2..6019a43bf 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt @@ -3,7 +3,7 @@ package org.jetbrains.r.classes.s4.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.RPsiUtil @@ -12,7 +12,7 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class RS4NewObjectContext : LibraryClassContext { +sealed class RS4NewObjectContext : ILibraryClassContext { override val functionName = "new" } diff --git a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt index f25394314..6f1910201 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt @@ -3,7 +3,7 @@ package org.jetbrains.r.classes.s4.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.r.classes.common.context.LibraryClassContext +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.api.RCallExpression @@ -11,7 +11,7 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class RS4SetClassContext : LibraryClassContext { +sealed class RS4SetClassContext : ILibraryClassContext { override val functionName = "setClass" } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 60a2cfbdd..2b0460de9 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -22,9 +22,7 @@ import com.intellij.util.Processor import org.jetbrains.r.RLanguage import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.r6.R6ClassMember -import org.jetbrains.r.classes.r6.context.R6ContextProvider -import org.jetbrains.r.classes.r6.context.R6CreateClassContext -import org.jetbrains.r.classes.r6.context.R6NewObjectContext +import org.jetbrains.r.classes.r6.context.* import org.jetbrains.r.classes.s4.* import org.jetbrains.r.classes.s4.context.* import org.jetbrains.r.codeInsight.libraries.RLibrarySupportProvider @@ -41,8 +39,6 @@ import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider -import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex -import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator import org.jetbrains.r.rinterop.RValueFunction import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement @@ -144,8 +140,9 @@ class RCompletionContributor : CompletionContributor() { result: CompletionResultSet): Boolean { // self$member expression val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(rMemberExpression) - if (className != null){ - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + if (className != null) { + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, + RSearchScopeUtil.getScope(rMemberExpression)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -155,7 +152,8 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, + RSearchScopeUtil.getScope(rMemberExpression)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } @@ -164,7 +162,9 @@ class RCompletionContributor : CompletionContributor() { return false } - private fun addMembersCompletion(r6ClassMembers: List?, shownNames: MutableSet, result: CompletionResultSet): Boolean { + private fun addMembersCompletion(r6ClassMembers: List?, + shownNames: MutableSet, + result: CompletionResultSet): Boolean { var hasNewResults = false if (r6ClassMembers.isNullOrEmpty()) return hasNewResults @@ -215,7 +215,8 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = RS4ClassInfoUtil.getAssociatedClassName(call) ?: return@forEach - LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, + RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(RS4ClassInfoUtil.getAllAssociatedSlots(it), shownNames, result) } } @@ -264,8 +265,7 @@ class RCompletionContributor : CompletionContributor() { val position = if (probableIdentifier != null) { // operator surrounded by % or identifier PsiTreeUtil.findChildOfType(probableIdentifier, RInfixOperator::class.java) ?: probableIdentifier - } - else { + } else { // operator with parser error PsiTreeUtil.getParentOfType(parameters.position, RPsiElement::class.java, false) ?: return } @@ -318,8 +318,7 @@ class RCompletionContributor : CompletionContributor() { val element = RElementFactory.createRPsiElementFromTextOrNull(originFile.project, code) as? RAssignmentStatement ?: return@forEach result.consume(elementFactory.createFunctionLookupElement(element, isLocal = true)) - } - else { + } else { result.consume(elementFactory.createLocalVariableLookupElement(name, false)) } } @@ -364,8 +363,7 @@ class RCompletionContributor : CompletionContributor() { val parent = it.variableDescription.firstDefinition.parent if (parent is RAssignmentStatement && parent.isFunctionDeclaration) { result.consume(elementFactory.createFunctionLookupElement(parent, true)) - } - else { + } else { result.consume(elementFactory.createLocalVariableLookupElement(name, parent is RParameter)) } } @@ -425,8 +423,7 @@ class RCompletionContributor : CompletionContributor() { arg.parameterList?.parameterList?.map { it.name }?.forEach { consumeParameter(it, shownNames, result) } - } - else { + } else { arg.reference?.multiResolve(false)?.forEach { resolveResult -> (resolveResult.element as? RAssignmentStatement)?.let { assignment -> val inhNamedArgs = info?.loadInheritorNamedArguments(assignment.name) ?: emptyList() @@ -486,9 +483,9 @@ class RCompletionContributor : CompletionContributor() { result.addAllElements(lookupElements.map { val column = it.column if (column.quoteNeeded) { - rCompletionElementFactory.createQuotedLookupElement(column.name, TABLE_MANIPULATION_PRIORITY, true, AllIcons.Nodes.Field, column.type) - } - else { + rCompletionElementFactory.createQuotedLookupElement(column.name, TABLE_MANIPULATION_PRIORITY, true, AllIcons.Nodes.Field, + column.type) + } else { PrioritizedLookupElement.withPriority( RLookupElement(column.name, true, AllIcons.Nodes.Field, packageName = column.type), TABLE_MANIPULATION_PRIORITY @@ -529,7 +526,8 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(psiElement: RCallExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> + LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope( + psiElement)).singleOrNull()?.let { definition -> RS4ClassInfoUtil.getAllAssociatedSlots(definition).forEach { result.consume(RLookupElementFactory.createNamedArgumentLookupElement(it.name, it.type, SLOT_NAME_PRIORITY)) } @@ -542,8 +540,8 @@ class RCompletionContributor : CompletionContributor() { private fun addS4ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { val s4Context = RS4ContextProvider.getS4Context(classNameExpression, - RS4NewObjectContext::class.java, - RS4SetClassContext::class.java) ?: return + RS4NewObjectContext::class.java, + RS4SetClassContext::class.java) ?: return var omitVirtual = false var nameToOmit: String? = null when (s4Context) { @@ -600,14 +598,12 @@ class RCompletionContributor : CompletionContributor() { val projectDir = classDeclaration.project.guessProjectDir() if (virtualFile == null || projectDir == null) "" else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" - } - else packageName + } else packageName if (classNameExpression is RStringLiteralExpression) { addElement(RLookupElementFactory.createLookupElementWithPriority( RLookupElement(escape(className), true, AllIcons.Nodes.Field, packageName = location), STRING_LITERAL_INSERT_HANDLER, priority)) - } - else { + } else { addElement(rCompletionElementFactory.createQuotedLookupElement(className, priority, true, AllIcons.Nodes.Field, location)) } } @@ -621,16 +617,44 @@ class RCompletionContributor : CompletionContributor() { addR6MemberNameCompletion(expression, file, result) } - private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ - val r6Context = R6ContextProvider.getR6Context(classNameExpression, - R6NewObjectContext::class.java, - R6CreateClassContext::class.java) ?: return + private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { + val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6CreateClassContext::class.java) ?: return + val shownNames = HashSet() + + when (r6Context) { + is R6CreateClassNameContext -> { // suggestion of name + result.addR6ClassName(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) + } + + is R6CreateClassInheritContext -> TODO() + is R6CreateClassMembersContext -> TODO() + + else -> return + } } - private fun addR6MemberNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet){ + private fun addR6MemberNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { val r6Context = R6ContextProvider.getR6Context(classNameExpression, - R6NewObjectContext::class.java, - R6CreateClassContext::class.java) ?: return + R6NewObjectContext::class.java, + R6CreateClassContext::class.java) ?: return + } + + private fun CompletionResultSet.addR6ClassName(classNameExpression: RExpression, + shownNames: MutableSet, + loadedPackages: Set?) { + val classAssignmentExpression = PsiTreeUtil.getParentOfType(classNameExpression, + RAssignmentStatement::class.java) as RAssignmentStatement + ?: return + val classNameToSuggest = classAssignmentExpression.assignee?.text ?: return + if (classNameToSuggest in shownNames) return + shownNames.add(classNameToSuggest) + + val virtualFile = classNameExpression.containingFile.virtualFile + val projectDir = classNameExpression.project.guessProjectDir() + val location = if (virtualFile == null || projectDir == null) "" + else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" + + addElement(rCompletionElementFactory.createQuotedLookupElement(classNameToSuggest, LANGUAGE_R6_CLASS_NAME, true, AllIcons.Nodes.Field, location)) } } @@ -693,7 +717,7 @@ class RCompletionContributor : CompletionContributor() { private fun findParentheses(text: String, offset: Int): Int? { var whitespaceNo = 0 while (offset + whitespaceNo < text.length && text[offset + whitespaceNo] == ' ') whitespaceNo += 1 - return whitespaceNo.takeIf { (offset + whitespaceNo< text.length && text[offset + whitespaceNo ] == '(') } + return whitespaceNo.takeIf { (offset + whitespaceNo < text.length && text[offset + whitespaceNo] == '(') } } private object RFunctionCompletionInsertHandler : RLookupElementInsertHandler { @@ -761,8 +785,7 @@ class RCompletionContributor : CompletionContributor() { if (runtimeInfo == null || !provider.addCompletionFromRuntime(psiElement, shownNames, result, runtimeInfo)) { provider.addCompletionStatically(psiElement, shownNames, result) } - } - else { + } else { if (!provider.addCompletionStatically(psiElement, shownNames, result)) { runtimeInfo?.let { provider.addCompletionFromRuntime(psiElement, shownNames, result, it) } } diff --git a/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt b/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt index ab165c48b..c42ebee71 100644 --- a/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt +++ b/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt @@ -29,6 +29,7 @@ const val LOADED_S4_CLASS_NAME = 100.0 const val VARIABLE_GROUPING = 90 const val NOT_LOADED_S4_CLASS_NAME = 50.0 const val LANGUAGE_S4_CLASS_NAME = 25.0 +const val LANGUAGE_R6_CLASS_NAME = 25.0 const val PACKAGE_PRIORITY = -1.0 const val GLOBAL_GROUPING = 0 const val NAMESPACE_NAME_GROUPING = -1 diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index 93064a3e3..c5d7f86a7 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -56,6 +56,12 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "random" to "", "someField" to "", "someMethod" to "") } + fun testUserClassNameSuggestion() { + doTest(""" + MyClass <- R6Class() + """.trimIndent(), "MyClass" to "string", containedInSuggestions = true) + } + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { val result = doTestBase(text, withRuntimeInfo, inConsole) assertNotNull(result) @@ -65,6 +71,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { private fun doTest(text: String, vararg variants: Pair, // + containedInSuggestions: Boolean = false, strict: Boolean = true, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { @@ -75,11 +82,19 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { it.renderElement(elementPresentation) elementPresentation.itemText to elementPresentation.typeText } - if (strict) { - assertOrderedEquals(lookupStrings, *variants) - } - else { - assertContainsOrdered(lookupStrings, *variants) + + when { + containedInSuggestions -> { + variants.forEach { expectedSuggestion -> + assert(lookupStrings.any { it.first == expectedSuggestion.first }) + } + } + strict -> { + assertOrderedEquals(lookupStrings, *variants) + } + else -> { + assertContainsOrdered(lookupStrings, *variants) + } } } From 793fb07ef3bf1f1ba653782b1136c2dc1d05d658 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 13 Apr 2021 01:52:47 +0300 Subject: [PATCH 29/52] R6Class contains whole superclass hierarchy --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 8 +- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 77 +++++++++++++++---- .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 2 - .../r/editor/RCompletionContributor.kt | 13 +--- src/org/jetbrains/r/rinterop/RInterop.kt | 6 +- .../r/skeleton/RSkeletonFileStubBuilder.kt | 2 +- .../r/classes/r6/R6ClassInfoUtilTests.kt | 22 ++++-- .../classes/R6ClassCompletionTest.kt | 11 +++ 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index d3e020518..ab431d3d1 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -18,7 +18,7 @@ data class R6ClassActiveBinding(override val name: String) : R6ClassMember data class R6ClassInfo(val className: String, val packageName: String, - val superClass: String, + val superClasses: List, val fields: List, val methods: List, val activeBindings: List) { @@ -26,7 +26,7 @@ data class R6ClassInfo(val className: String, fun serialize(dataStream: StubOutputStream) { dataStream.writeName(className) dataStream.writeName(packageName) - dataStream.writeName(superClass) + DataInputOutputUtilRt.writeSeq(dataStream, superClasses) { dataStream.writeName(it) } DataInputOutputUtilRt.writeSeq(dataStream, fields) { dataStream.writeName(it.name); @@ -47,7 +47,7 @@ data class R6ClassInfo(val className: String, fun deserialize(dataStream: StubInputStream): R6ClassInfo { val className = StringRef.toString(dataStream.readName()) val packageName = StringRef.toString(dataStream.readName()) - val superClass = StringRef.toString(dataStream.readName()) + val superClasses = DataInputOutputUtilRt.readSeq(dataStream) { StringRef.toString(dataStream.readName()) } val fields = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) @@ -66,7 +66,7 @@ data class R6ClassInfo(val className: String, R6ClassActiveBinding(name) } - return R6ClassInfo(className, packageName, superClass, fields, methods, activeBindings) + return R6ClassInfo(className, packageName, superClasses, fields, methods, activeBindings) } } diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index ee0ed369f..1cce25f8c 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -40,7 +40,7 @@ object R6ClassInfoUtil { * @param call expression `MyClass$new()` * @return class name which type is instantiated */ - fun getAssociatedClassNameFromInstantiationCall(call: RCallExpression) : String? { + fun getAssociatedClassNameFromInstantiationCall(call: RCallExpression): String? { val callExpression = call.expression as? RMemberExpressionImpl ?: return null if (callExpression.rightExpr?.text != functionNew) return null return callExpression.leftExpr?.text @@ -50,7 +50,7 @@ object R6ClassInfoUtil { * @param rMemberExpression expression `self$someMember` or `obj$someMember` * @return className of class where `self$...` is used or of which object is called */ - fun getClassNameFromInternalClassMemberUsageExpression(rMemberExpression: RMemberExpression?) : String? { + fun getClassNameFromInternalClassMemberUsageExpression(rMemberExpression: RMemberExpression?): String? { if (rMemberExpression == null) return null val classDefinitionCall = R6ClassPsiUtil.getClassDefinitionCallFromMemberUsage(rMemberExpression) ?: return null return getAssociatedClassNameFromR6ClassCall(classDefinitionCall) @@ -68,16 +68,47 @@ object R6ClassInfoUtil { return arg?.name } - fun getAssociatedSuperClassName(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { + /** + * @param callExpression expression `R6Class("MyClass", ...)` + * @return names of all inherited chain of parents + */ + fun getAssociatedSuperClassesHierarchy(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo ?: return null + + var classDeclarationExpression = callExpression + val classNamesHierarchy = mutableListOf() + + while (true) { + val directInherit = getAssociatedSuperClassName(classDeclarationExpression) ?: break + classNamesHierarchy.add(directInherit) + classDeclarationExpression = getSuperClassDefinitionCallExpression(classDeclarationExpression) ?: break + } + + return classNamesHierarchy + } + + /** + * @param callExpression expression `R6Class("MyClass", ...)` + * @return direct parent classname + */ + private fun getAssociatedSuperClassName(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null return (argumentInfo.getArgumentPassedToParameter(argumentSuperClass) as? RIdentifierExpression)?.name } + private fun getSuperClassDefinitionCallExpression(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): RCallExpression? { + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val inheritClassDefinition = argumentInfo?.getArgumentPassedToParameter(argumentSuperClass) as? RIdentifierExpression + return (inheritClassDefinition?.reference?.resolve() as? RAssignmentStatement)?.assignedValue as? RCallExpression + } + fun getAllClassMembers(callExpression: RCallExpression, argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), - onlyPublic: Boolean = false) : MutableList { + onlyPublic: Boolean = false): List { val r6ClassMembers = mutableListOf() val fields = getAssociatedFields(callExpression, argumentInfo, onlyPublic) @@ -103,7 +134,8 @@ object R6ClassInfoUtil { if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) if (!onlyPublic) { - val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + val privateContents = (argumentInfo.getArgumentPassedToParameter( + argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) } @@ -121,7 +153,8 @@ object R6ClassInfoUtil { if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) if (!onlyPublic) { - val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + val privateContents = (argumentInfo.getArgumentPassedToParameter( + argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) } @@ -129,7 +162,8 @@ object R6ClassInfoUtil { } fun getAssociatedActiveBindings(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo( + callExpression)): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null val r6ClassActiveBindings = mutableListOf() @@ -152,30 +186,41 @@ object R6ClassInfoUtil { val argumentInfo = RParameterInfoUtil.getArgumentInfo(callExpression, definition) ?: return null val className = getAssociatedClassNameFromR6ClassCall(callExpression, argumentInfo) ?: return null - val superClassName = getAssociatedSuperClassName(callExpression, argumentInfo) ?: "" + val superClassesHierarchy = getAssociatedSuperClassesHierarchy(callExpression, argumentInfo) ?: emptyList() val fields = getAssociatedFields(callExpression, argumentInfo) ?: emptyList() val methods = getAssociatedMethods(callExpression, argumentInfo) ?: emptyList() val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) ?: emptyList() val packageName = RPackageProjectManager.getInstance(project).getProjectPackageDescriptionInfo()?.packageName ?: "" - return R6ClassInfo(className, packageName, superClassName, fields, methods, activeBindings) + return R6ClassInfo(className, packageName, superClassesHierarchy, fields, methods, activeBindings) } - private fun getFieldsFromExpressionList(r6ClassFields: MutableList, callExpressions: List, isFromPublicScope: Boolean){ + private fun getFieldsFromExpressionList(r6ClassFields: MutableList, + callExpressions: List, + isFromPublicScope: Boolean) { callExpressions.forEach { - if (it.lastChild !is RFunctionExpression && !it.name.isNullOrEmpty()) { r6ClassFields.add(R6ClassField(it.name!!, isFromPublicScope)) } + if (it.lastChild !is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassFields.add(R6ClassField(it.name!!, isFromPublicScope)) + } } } - private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, callExpressions: List, isFromPublicScope: Boolean){ + private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, + callExpressions: List, + isFromPublicScope: Boolean) { callExpressions.forEach { - if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { r6ClassMethods.add(R6ClassMethod(it.name!!, isFromPublicScope)) } + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassMethods.add(R6ClassMethod(it.name!!, isFromPublicScope)) + } } } - private fun getActiveBindingsFromExpressionList(r6ClassActiveBindings: MutableList, callExpressions: List){ + private fun getActiveBindingsFromExpressionList(r6ClassActiveBindings: MutableList, + callExpressions: List) { callExpressions.forEach { - if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { r6ClassActiveBindings.add(R6ClassActiveBinding(it.name!!)) } + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassActiveBindings.add(R6ClassActiveBinding(it.name!!)) + } } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt index 8c77cb1bf..963d11f64 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -170,8 +170,6 @@ object R6ClassPsiUtil { return classIdentifier } - - private fun extractNamedArgumentByName(elementName: String, namedArguments: List?) : RPsiElement? { namedArguments?.forEach { if (it != null) { diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 2b0460de9..c99bb7602 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -143,19 +143,8 @@ class RCompletionContributor : CompletionContributor() { if (className != null) { LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { - return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) - } - } - // classObject$member expression - rMemberExpression.leftExpr?.reference?.multiResolve(false)?.forEach { resolveResult -> - val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach - (definition.assignedValue as? RCallExpression)?.let { call -> - val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) ?: return@forEach - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, - RSearchScopeUtil.getScope(rMemberExpression)).forEach { - return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) - } + return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index 22a066567..fd6253be9 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -881,7 +881,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getLoadedShortR6ClassInfos(): List? { return try { executeWithCheckCancel(asyncStub::getLoadedShortR6ClassInfos, Empty.getDefaultInstance()).shortR6ClassInfosList.map { - R6ClassInfo(it.name, it.`package`, "", emptyList(), emptyList(), emptyList()) + R6ClassInfo(it.name, it.`package`, emptyList(), emptyList(), emptyList(), emptyList()) } } catch (e: RInteropTerminated) { null @@ -891,7 +891,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByObjectName(ref: RReference): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) - R6ClassInfo(res.className, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + R6ClassInfo(res.className, res.packageName, res.superClassesList, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) null } catch (e: RInteropTerminated) { @@ -902,7 +902,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByClassName(className: String): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByClassName, StringValue.of(className)) - R6ClassInfo(res.className, res.packageName, res.superClass, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + R6ClassInfo(res.className, res.packageName, res.superClassesList, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) } catch (e: RInteropTerminated) { null diff --git a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt index 136e0ad74..573dc241a 100644 --- a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt +++ b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt @@ -55,7 +55,7 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { null, R6ClassInfo(symbol.name, r6ClassRepresentation.packageName, - r6ClassRepresentation.superClass, + r6ClassRepresentation.superClassesList, r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic) }, r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index ab9869bfa..de912b9c6 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -4,6 +4,7 @@ package org.jetbrains.r.classes.r6 +import com.intellij.psi.util.PsiTreeUtil import junit.framework.TestCase import org.jetbrains.r.classes.RClassesUtilTestsBase import org.jetbrains.r.psi.api.RAssignmentStatement @@ -80,11 +81,20 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { assertEquals("MyClass", className) } - fun testGetAssociatedSuperClassName(){ - val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement - val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val superClassName = R6ClassInfoUtil.getAssociatedSuperClassName(rCallExpression!!) - assertEquals("Vehicle", superClassName) + fun testGetAssociatedSuperClasses(){ + val psiTree = getRootElementOfPsi(""" + SuperParentClass <- R6Class("SuperParentClass") + ParentClass <- R6Class("ParentClass", inherit = SuperParentClass) + ChildClass <- R6Class("ChildClass", inherit = ParentClass) + """.trimIndent()).parent + + val lastClassAssignment = PsiTreeUtil.getChildrenOfType(psiTree, RAssignmentStatement::class.java).last() as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(lastClassAssignment) + val superClassNames = R6ClassInfoUtil.getAssociatedSuperClassesHierarchy(rCallExpression!!) + + assertNotNull(superClassNames) + assert(superClassNames!!.contains("ParentClass")) + assert(superClassNames.contains("SuperParentClass")) } fun testGetAssociatedFields(){ @@ -92,7 +102,7 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) - TestCase.assertNotNull(classFields) + assertNotNull(classFields) assertEquals(classFields!!.size, 3) val classFieldsNames = classFields.map { it.name } diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index c5d7f86a7..4922afa51 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -56,6 +56,17 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "random" to "", "someField" to "", "someMethod" to "") } + fun testInheritedUserClass() { + // TODO finish + + doTest(""" + ParentClass <- R6Class("ParentClass", list( someField = 0 )) + ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) + obj <- ChildClass${"$"}new() + obj${'$'}someField${'$'} + """.trimIndent(), "add" to "", "someField" to "") + } + fun testUserClassNameSuggestion() { doTest(""" MyClass <- R6Class() From 0f8e7d7ee130858cf287535e6f6d3f51e21aef1f Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 13 Apr 2021 02:19:32 +0300 Subject: [PATCH 30/52] superclass members analysis completed --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 27 ++++++++++--------- .../r/editor/RCompletionContributor.kt | 1 - .../classes/R6ClassCompletionTest.kt | 2 -- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 1cce25f8c..f64c79b4f 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -5,7 +5,7 @@ package org.jetbrains.r.classes.r6 import com.intellij.openapi.util.Key -import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.CachedValuesManager import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.packages.RPackageProjectManager @@ -14,6 +14,8 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.impl.RMemberExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft +import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider object R6ClassInfoUtil { const val R6PackageName = "R6" @@ -106,21 +108,20 @@ object R6ClassInfoUtil { return (inheritClassDefinition?.reference?.resolve() as? RAssignmentStatement)?.assignedValue as? RCallExpression } - fun getAllClassMembers(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), - onlyPublic: Boolean = false): List { - val r6ClassMembers = mutableListOf() + fun getAllClassMembers(callExpression: RCallExpression): List { + val r6ClassInfo = CachedValuesManager.getProjectPsiDependentCache(callExpression) { callExpression.associatedR6ClassInfo } ?: return emptyList() + val allSuperClasses = getAssociatedSuperClassesHierarchy(callExpression) - val fields = getAssociatedFields(callExpression, argumentInfo, onlyPublic) - if (!fields.isNullOrEmpty()) r6ClassMembers.addAll(fields) - - val functions = getAssociatedMethods(callExpression, argumentInfo, onlyPublic) - if (!functions.isNullOrEmpty()) r6ClassMembers.addAll(functions) + val callSearchScope = RSearchScopeUtil.getScope(callExpression) + val project = callExpression.project - val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) - if (!activeBindings.isNullOrEmpty()) r6ClassMembers.addAll(activeBindings) + if (allSuperClasses != null) { + return (r6ClassInfo.fields + r6ClassInfo.methods + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.fields + it.methods + it.activeBindings } + }).distinctBy { it.name } + } - return r6ClassMembers + return emptyList() } fun getAssociatedFields(callExpression: RCallExpression, diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index c99bb7602..6458de4c8 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -143,7 +143,6 @@ class RCompletionContributor : CompletionContributor() { if (className != null) { LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { - return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) } } diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index 4922afa51..c04b783af 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -57,8 +57,6 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { } fun testInheritedUserClass() { - // TODO finish - doTest(""" ParentClass <- R6Class("ParentClass", list( someField = 0 )) ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) From 8820b0b19bbcff3a33efc8b3c39fc1cefd9d3ef8 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 13 Apr 2021 02:20:17 +0300 Subject: [PATCH 31/52] library_summary - configure R6ClassInfoRepresentation with list of superClasses --- grammars/library_summary.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grammars/library_summary.proto b/grammars/library_summary.proto index 6be4f3c99..a1fe4ded3 100644 --- a/grammars/library_summary.proto +++ b/grammars/library_summary.proto @@ -50,7 +50,7 @@ message RLibrarySymbol { string name = 1; } string packageName = 1; - string superClass = 2; + repeated string superClasses = 2; repeated R6ClassField fields = 3; repeated R6ClassField methods = 4; repeated R6ClassField activeBindings = 5; From 57039336cb25438db752a36526c870fe142b37f0 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 13 Apr 2021 03:23:51 +0300 Subject: [PATCH 32/52] support resolving dependant identifier for super classes --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 6 +++++ .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 2 +- .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 25 +++++++++++++++++-- .../classes/R6ClassCompletionTest.kt | 2 +- .../r/findUsages/R6FindUsagesTest.kt | 19 +++++++++++++- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index ab431d3d1..9aff5edb4 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -23,6 +23,12 @@ data class R6ClassInfo(val className: String, val methods: List, val activeBindings: List) { + fun containsMember(memberName: String) : Boolean { + return (fields.map { it.name }.contains(memberName) || + methods.map { it.name }.contains(memberName) || + activeBindings.map { it.name }.contains(memberName)) + } + fun serialize(dataStream: StubOutputStream) { dataStream.writeName(className) dataStream.writeName(packageName) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index f64c79b4f..919f8c43e 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -75,7 +75,7 @@ object R6ClassInfoUtil { * @return names of all inherited chain of parents */ fun getAssociatedSuperClassesHierarchy(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): List? { + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): MutableList? { argumentInfo ?: return null var classDeclarationExpression = callExpression diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt index 963d11f64..bbcbef581 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -13,6 +13,8 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft +import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider object R6ClassPsiUtil { @@ -23,8 +25,27 @@ object R6ClassPsiUtil { fun getSearchedIdentifier(dependantIdentifier: RIdentifierExpression?) : RPsiElement? { if (dependantIdentifier == null) return null - val classDefinitionCall = getClassDefinitionCallFromMemberUsage(dependantIdentifier) - val argumentInfo = getClassDefinitionArgumentInfo(classDefinitionCall) ?: return null + val classDefinitionCall = getClassDefinitionCallFromMemberUsage(dependantIdentifier) ?: return null + val className = R6ClassInfoUtil.getAssociatedClassNameFromR6ClassCall(classDefinitionCall) ?: return null + val classNamesHierarchy = R6ClassInfoUtil.getAssociatedSuperClassesHierarchy(classDefinitionCall) + classNamesHierarchy?.add(0, className) + + val callSearchScope = RSearchScopeUtil.getScope(classDefinitionCall) + val project = classDefinitionCall.project + + val r6ClassInfo = run findMemberDefinition@ { + (classNamesHierarchy)?.reversed()?.forEach { + val r6ClassInfo = LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(it, project, callSearchScope).firstOrNull() + + if (r6ClassInfo != null) { + if (r6ClassInfo.containsMember(dependantIdentifier.name)) return@findMemberDefinition r6ClassInfo + } + } + } as R6ClassInfo? + + r6ClassInfo ?: return null + val r6ClassDefinitionCall = LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(r6ClassInfo.className, project, callSearchScope).firstOrNull() + val argumentInfo = getClassDefinitionArgumentInfo(r6ClassDefinitionCall) ?: return null val publicMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPublic) val privateMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPrivate) diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index c04b783af..6db42680d 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -56,7 +56,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "random" to "", "someField" to "", "someMethod" to "") } - fun testInheritedUserClass() { + fun testInheritedUserClassFieldsVisibility() { doTest(""" ParentClass <- R6Class("ParentClass", list( someField = 0 )) ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) diff --git a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt index 319fea063..d5a457d02 100644 --- a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt +++ b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt @@ -93,7 +93,6 @@ class R6FindUsagesTest : FindUsagesTestBase() { """) } - /// Not working due to picked target element is `list(...)` and not `someField` fun testR6ClassFieldFromDefinition() { doTest(""" MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) @@ -112,4 +111,22 @@ class R6FindUsagesTest : FindUsagesTestBase() { 4obj${"$"}someField """) } + + fun testR6ClassFieldWithSuperClass() { + doTest(""" + ParentClass <- R6Class("ParentClass", list( someField = 0 )) + ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) + obj <- ChildClass${'$'}new() + obj${'$'}someField + """, """ + Usage (1 usage) + Variable + someField = 0 + Found usages (1 usage) + Unclassified (1 usage) + light_idea_test_case (1 usage) + (1 usage) + 4obj${'$'}someField + """) + } } \ No newline at end of file From 74114d2fdb318501729e48ada8b32f73c1b995bb Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 15 Apr 2021 02:19:52 +0300 Subject: [PATCH 33/52] more codeCompletion suggestions --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 9 +++ .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 1 + .../r/classes/r6/context/R6ContextProvider.kt | 2 +- .../context/R6CreateClassContextProvider.kt | 4 +- .../r6/context/R6NewObjectContextProvider.kt | 2 +- .../R6SetClassMembersContextProvider.kt | 62 +++++++++++++++++++ .../r/editor/RCompletionContributor.kt | 42 +++++++++++-- .../classes/R6ClassCompletionTest.kt | 19 ++++-- 8 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index 9aff5edb4..b5f9a9f78 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -81,4 +81,13 @@ data class R6ClassInfo(val className: String, // TODO build string from R6ClassInfo } } +} + +class R6ClassMemberProvider { + companion object { + val KeyMembers = listOf( + R6ClassMethod("clone", true), + R6ClassMethod("set", true) + ) + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 919f8c43e..f7f04afd2 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -23,6 +23,7 @@ object R6ClassInfoUtil { const val R6ClassThisKeyword = "self" const val functionNew = "new" + const val functionSet = "set" const val argumentClassName = "classname" const val argumentSuperClass = "inherit" diff --git a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt index 9de10ce6a..bb0d00fba 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt @@ -10,7 +10,7 @@ import org.jetbrains.r.psi.api.RPsiElement import java.lang.reflect.ParameterizedType abstract class R6ContextProvider { - + abstract fun getR6ContextInner(element: RPsiElement): T? abstract fun getContext(element: RPsiElement): T? @Suppress("UNCHECKED_CAST") diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt index 74d72c01b..6881e1451 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -17,7 +17,7 @@ import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary sealed class R6CreateClassContext : ILibraryClassContext { - override val functionName = "R6Class" + override val functionName = R6ClassInfoUtil.R6CreateClassMethod } // R6Class(, ) @@ -48,7 +48,7 @@ class R6CreateClassContextProvider : R6ContextProvider() { } } - private fun getR6ContextInner(element: RPsiElement): R6CreateClassContext? { + override fun getR6ContextInner(element: RPsiElement): R6CreateClassContext? { val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null return if (parentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) { val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt index 98ce4e2f8..0507d5132 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt @@ -31,7 +31,7 @@ class R6NewObjectContextProvider : R6ContextProvider() { } } - private fun getR6ContextInner(element: RPsiElement): R6NewObjectContext? { + override fun getR6ContextInner(element: RPsiElement): R6NewObjectContext? { val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null if (!parentCall.isFunctionFromLibrary(R6ClassInfoUtil.functionNew, R6ClassInfoUtil.R6PackageName)) return null val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt new file mode 100644 index 000000000..a3505d630 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.ILibraryClassContext +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RMemberExpression +import org.jetbrains.r.psi.api.RPsiElement +import org.jetbrains.r.psi.isFunctionFromLibrary +import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider + +sealed class R6SetClassMembersContext : ILibraryClassContext { + override val functionName = R6ClassInfoUtil.functionSet +} + +// MyClass$set() +// MyClass$set("") +data class R6SetClassMembersContextVisibility(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassMembersContext() + +// MyClass$set(visibility, ) +// MyClass$set("visibility", ) +data class R6SetClassMembersContextName(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassMembersContext() + +class R6SetClassMembersContextProvider : R6ContextProvider() { + override fun getContext(element: RPsiElement): R6SetClassMembersContext? { + return CachedValuesManager.getCachedValue(element) { + CachedValueProvider.Result.create(getR6ContextInner(element), element) + } + } + + override fun getR6ContextInner(element: RPsiElement): R6SetClassMembersContext? { + val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null + if (!isFromR6Library(parentCall)) return null + + // todo finish with suggesting `MyClass$set(...)` + val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null + + return null + } + + private fun isFromR6Library(rCallExpression: RCallExpression) : Boolean { + val memberExpression = rCallExpression.expression as? RMemberExpression ?: return false + if (!memberExpression.lastChild.textMatches(R6ClassInfoUtil.functionSet)) return false + + val r6ClassIdentifier = memberExpression.firstChild + val cachedClasses = LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(r6ClassIdentifier.text, memberExpression.project, RSearchScopeUtil.getScope(rCallExpression)) + return (!cachedClasses.isNullOrEmpty()) + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 6458de4c8..0f1483343 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -22,6 +22,7 @@ import com.intellij.util.Processor import org.jetbrains.r.RLanguage import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.r6.R6ClassMember +import org.jetbrains.r.classes.r6.R6ClassMemberProvider import org.jetbrains.r.classes.r6.context.* import org.jetbrains.r.classes.s4.* import org.jetbrains.r.classes.s4.context.* @@ -138,12 +139,12 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(rMemberExpression: RMemberExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - // self$member expression val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(rMemberExpression) if (className != null) { LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, RSearchScopeUtil.getScope(rMemberExpression)).forEach { - return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) + addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it), shownNames, result) + return addMembersCompletion(R6ClassMemberProvider.KeyMembers, shownNames, result) } } @@ -603,6 +604,7 @@ class RCompletionContributor : CompletionContributor() { val file = parameters.originalFile addR6ClassNameCompletion(expression, file, result) addR6MemberNameCompletion(expression, file, result) + addR6AdditionalMembersAfterCreation(expression, file, result) } private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { @@ -610,8 +612,8 @@ class RCompletionContributor : CompletionContributor() { val shownNames = HashSet() when (r6Context) { - is R6CreateClassNameContext -> { // suggestion of name - result.addR6ClassName(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) + is R6CreateClassNameContext -> { // suggestion of name of `<- R6Class("")` + result.addR6ClassNameCompletion(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) } is R6CreateClassInheritContext -> TODO() @@ -627,7 +629,37 @@ class RCompletionContributor : CompletionContributor() { R6CreateClassContext::class.java) ?: return } - private fun CompletionResultSet.addR6ClassName(classNameExpression: RExpression, + private fun addR6AdditionalMembersAfterCreation(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { + val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6SetClassMembersContext::class.java) ?: return + val shownNames = HashSet() + + when (r6Context) { + is R6SetClassMembersContextVisibility -> { // suggestion of name of `classObject$set("")` + result.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) + } + + else -> return + } + } + + private fun CompletionResultSet.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression: RExpression, + shownNames: MutableSet, + loadedPackages: Set?) { + val classAssignmentExpression = PsiTreeUtil.getParentOfType(classNameExpression, RAssignmentStatement::class.java) as RAssignmentStatement + + val classNameToSuggest = classAssignmentExpression.assignee?.text ?: return + if (classNameToSuggest in shownNames) return + shownNames.add(classNameToSuggest) + + val virtualFile = classNameExpression.containingFile.virtualFile + val projectDir = classNameExpression.project.guessProjectDir() + val location = if (virtualFile == null || projectDir == null) "" + else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" + + addElement(rCompletionElementFactory.createQuotedLookupElement(classNameToSuggest, LANGUAGE_R6_CLASS_NAME, true, AllIcons.Nodes.Field, location)) + } + + private fun CompletionResultSet.addR6ClassNameCompletion(classNameExpression: RExpression, shownNames: MutableSet, loadedPackages: Set?) { val classAssignmentExpression = PsiTreeUtil.getParentOfType(classNameExpression, diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index 6db42680d..f471fad48 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -23,7 +23,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { MyClass <- R6Class("MyClass", list( someField = 0 )) obj <- MyClass${"$"}new() obj$ - """.trimIndent(), "someField" to "") + """.trimIndent(), "clone" to "", "someField" to "") } fun testUserClassWithSingleFieldChainedUsage() { @@ -31,13 +31,13 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { MyClass <- R6Class("MyClass", list( someField = 0 )) obj <- MyClass${"$"}new() obj${'$'}someField${'$'} - """.trimIndent(), "someField" to "") + """.trimIndent(), "clone" to "", "someField" to "") } fun testUserClassForInternalUsage() { doTest(""" MyClass <- R6Class("MyClass", list( someField = 0, add = function(x = 1) { self$ } )) - """.trimIndent(), "add" to "", "someField" to "") + """.trimIndent(), "add" to "", "clone" to "", "someField" to "") } fun testUserClassWithSeveralMembers() { @@ -45,7 +45,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) } )) obj <- MyClass${"$"}new() obj$ - """.trimIndent(), "someField" to "", "someMethod" to "") + """.trimIndent(), "clone" to "", "someField" to "", "someMethod" to "") } fun testUserClassWithFieldMethodActiveBinding() { @@ -53,7 +53,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } )) obj <- MyClass${"$"}new() obj$ - """.trimIndent(), "random" to "", "someField" to "", "someMethod" to "") + """.trimIndent(), "clone" to "", "random" to "", "someField" to "", "someMethod" to "") } fun testInheritedUserClassFieldsVisibility() { @@ -62,7 +62,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) obj <- ChildClass${"$"}new() obj${'$'}someField${'$'} - """.trimIndent(), "add" to "", "someField" to "") + """.trimIndent(), "add" to "", "clone" to "", "someField" to "") } fun testUserClassNameSuggestion() { @@ -71,6 +71,13 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "MyClass" to "string", containedInSuggestions = true) } + fun testSetMemberVisibilityModifierCompletionToUserClass() { + doTest(""" + MyClass <- R6Class("MyClass", list(someField = 0)) + MyClass${'$'}set() + """.trimIndent(), "active" to "string", "public" to "string", "private" to "string", containedInSuggestions = true) + } + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { val result = doTestBase(text, withRuntimeInfo, inConsole) assertNotNull(result) From d9aea501cc7ed42f22edc5479d3c7d0cb378ca12 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 15 Apr 2021 22:30:14 +0300 Subject: [PATCH 34/52] $set() completion suggestions ready --- .../common/context/ILibraryClassContext.kt | 2 +- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 13 +++++++--- .../R6SetClassMembersContextProvider.kt | 26 ++++++++++++------- .../r/editor/RCompletionContributor.kt | 23 +++++++--------- .../classes/R6ClassCompletionTest.kt | 2 +- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt b/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt index 3692577eb..19efef86c 100644 --- a/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt +++ b/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt @@ -11,6 +11,6 @@ import org.jetbrains.r.psi.api.RPsiElement interface ILibraryClassContext { val functionName: String val functionCall: RCallExpression - val argumentInfo: RArgumentInfo + val argumentInfo: RArgumentInfo? val originalElement: RPsiElement } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index b5f9a9f78..30a8b93ca 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -83,11 +83,16 @@ data class R6ClassInfo(val className: String, } } -class R6ClassMemberProvider { +class R6ClassKeywordsProvider { companion object { - val KeyMembers = listOf( - R6ClassMethod("clone", true), - R6ClassMethod("set", true) + val predefinedClassMethods = listOf( + R6ClassMethod("clone", true) + ) + + val visibilityModifiers = listOf( + R6ClassInfoUtil.argumentPrivate, + R6ClassInfoUtil.argumentPublic, + R6ClassInfoUtil.argumentActive, ) } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt index a3505d630..48ed88167 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt @@ -10,11 +10,9 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.hints.parameterInfo.RArgumentInfo -import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.api.RMemberExpression import org.jetbrains.r.psi.api.RPsiElement -import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RSearchScopeUtil import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider @@ -26,13 +24,13 @@ sealed class R6SetClassMembersContext : ILibraryClassContext { // MyClass$set("") data class R6SetClassMembersContextVisibility(override val originalElement: RPsiElement, override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6SetClassMembersContext() + override val argumentInfo: RArgumentInfo?) : R6SetClassMembersContext() // MyClass$set(visibility, ) // MyClass$set("visibility", ) data class R6SetClassMembersContextName(override val originalElement: RPsiElement, - override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6SetClassMembersContext() + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassMembersContext() class R6SetClassMembersContextProvider : R6ContextProvider() { override fun getContext(element: RPsiElement): R6SetClassMembersContext? { @@ -44,19 +42,27 @@ class R6SetClassMembersContextProvider : R6ContextProvider { // suggestion of name of `classObject$set("")` - result.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) + result.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression, shownNames) } else -> return } } - private fun CompletionResultSet.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression: RExpression, - shownNames: MutableSet, - loadedPackages: Set?) { - val classAssignmentExpression = PsiTreeUtil.getParentOfType(classNameExpression, RAssignmentStatement::class.java) as RAssignmentStatement - - val classNameToSuggest = classAssignmentExpression.assignee?.text ?: return - if (classNameToSuggest in shownNames) return - shownNames.add(classNameToSuggest) - + private fun CompletionResultSet.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression: RExpression, shownNames: MutableSet) { val virtualFile = classNameExpression.containingFile.virtualFile val projectDir = classNameExpression.project.guessProjectDir() val location = if (virtualFile == null || projectDir == null) "" else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" - addElement(rCompletionElementFactory.createQuotedLookupElement(classNameToSuggest, LANGUAGE_R6_CLASS_NAME, true, AllIcons.Nodes.Field, location)) + R6ClassKeywordsProvider.visibilityModifiers.forEach { visibilityModifier -> + if (visibilityModifier in shownNames) return + shownNames.add(visibilityModifier) + addElement(rCompletionElementFactory.createQuotedLookupElement(visibilityModifier, LANGUAGE_R6_CLASS_NAME, true, AllIcons.Nodes.Field, location)) + } } private fun CompletionResultSet.addR6ClassNameCompletion(classNameExpression: RExpression, diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index f471fad48..dd3c4f738 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -75,7 +75,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { doTest(""" MyClass <- R6Class("MyClass", list(someField = 0)) MyClass${'$'}set() - """.trimIndent(), "active" to "string", "public" to "string", "private" to "string", containedInSuggestions = true) + """.trimIndent(), "\"active\"" to "string", "\"public\"" to "string", "\"private\"" to "string", containedInSuggestions = true) } private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { From b68ad7132bd301e530fd22dd1317dd55c33f63d3 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 15 Apr 2021 23:31:54 +0300 Subject: [PATCH 35/52] removed unused --- .../r6/context/R6NewObjectContextProvider.kt | 58 ------------------- .../r/editor/RCompletionContributor.kt | 10 ---- 2 files changed, 68 deletions(-) delete mode 100644 src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt diff --git a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt deleted file mode 100644 index 0507d5132..000000000 --- a/src/org/jetbrains/r/classes/r6/context/R6NewObjectContextProvider.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -package org.jetbrains.r.classes.r6.context - -import com.intellij.psi.util.CachedValueProvider -import com.intellij.psi.util.CachedValuesManager -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.r.classes.common.context.ILibraryClassContext -import org.jetbrains.r.classes.r6.R6ClassInfoUtil -import org.jetbrains.r.hints.parameterInfo.RArgumentInfo -import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil -import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.api.RPsiElement -import org.jetbrains.r.psi.isFunctionFromLibrary - -sealed class R6NewObjectContext : ILibraryClassContext { - override val functionName = "new" -} - -// Calculator <- Accumulator$new() -data class R6NewObjectClassNameContext(override val originalElement: RPsiElement, - override val functionCall: RCallExpression, - override val argumentInfo: RArgumentInfo) : R6NewObjectContext() - -class R6NewObjectContextProvider : R6ContextProvider() { - override fun getContext(element: RPsiElement): R6NewObjectContext? { - return CachedValuesManager.getCachedValue(element) { - CachedValueProvider.Result.create(getR6ContextInner(element), element) - } - } - - override fun getR6ContextInner(element: RPsiElement): R6NewObjectContext? { - val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null - if (!parentCall.isFunctionFromLibrary(R6ClassInfoUtil.functionNew, R6ClassInfoUtil.R6PackageName)) return null - val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null - - return null - -// return if (parentArgumentInfo.getArgumentPassedToParameter() == element) { -// // MyClass$new() -// RS4NewObjectClassNameContext(element, parentCall, parentArgumentInfo) -// } -// else { -// val currentArgument = -// if (element.parent is RNamedArgument) RPsiUtil.getNamedArgumentByNameIdentifier(element) ?: return null -// else element -// val arguments = parentCall.argumentList.expressionList -// if (!arguments.contains(currentArgument)) return null -// else { -// // new("ClassName", slot_name) -// // new("ClassName", slot_name = slot_value) -// RS4NewObjectSlotNameContext(currentArgument, parentCall, parentArgumentInfo) -// } -// } - } -} \ No newline at end of file diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 4a2484535..17e49e75a 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -602,7 +602,6 @@ class RCompletionContributor : CompletionContributor() { val expression = PsiTreeUtil.getParentOfType(parameters.position, RExpression::class.java, false) ?: return val file = parameters.originalFile addR6ClassNameCompletion(expression, file, result) - addR6MemberNameCompletion(expression, file, result) addR6AdditionalMembersAfterCreation(expression, file, result) } @@ -615,19 +614,10 @@ class RCompletionContributor : CompletionContributor() { result.addR6ClassNameCompletion(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) } - is R6CreateClassInheritContext -> TODO() - is R6CreateClassMembersContext -> TODO() - else -> return } } - private fun addR6MemberNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { - val r6Context = R6ContextProvider.getR6Context(classNameExpression, - R6NewObjectContext::class.java, - R6CreateClassContext::class.java) ?: return - } - private fun addR6AdditionalMembersAfterCreation(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6SetClassMembersContext::class.java) ?: return val shownNames = HashSet() From c6df69978eec0fa72f624218ac7524301e7adf80 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 15 Apr 2021 23:52:54 +0300 Subject: [PATCH 36/52] removed unused --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index 30a8b93ca..0132712e0 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -75,12 +75,6 @@ data class R6ClassInfo(val className: String, return R6ClassInfo(className, packageName, superClasses, fields, methods, activeBindings) } } - - override fun toString() : String { - return buildString { - // TODO build string from R6ClassInfo - } - } } class R6ClassKeywordsProvider { From fa048014f2ff634ef78b866d912c6250d015da4f Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 15 Apr 2021 23:53:14 +0300 Subject: [PATCH 37/52] removed unused --- resources/META-INF/rplugin-common.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index fd6223413..0d5070a63 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -737,7 +737,7 @@ You can find the source code in the following repositories: - + From 9852453211dd42149e79ccdb8cce42bcbadff32d Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 18 Apr 2021 14:20:48 +0300 Subject: [PATCH 38/52] remove unused bytes in proto and merge `field` + `method` to `r6Member` --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 37 ++++-------- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 57 +++++-------------- .../r/editor/RCompletionContributor.kt | 4 +- src/org/jetbrains/r/rinterop/RInterop.kt | 12 ++-- .../r/skeleton/RSkeletonFileStubBuilder.kt | 9 +-- temporary/service.proto | 19 ++----- .../r/classes/r6/R6ClassInfoUtilTests.kt | 24 ++++---- 7 files changed, 46 insertions(+), 116 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index 0132712e0..4016c413d 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -11,39 +11,29 @@ import com.intellij.util.io.StringRef // no need to care about overloads because R6 lib doesn't support it: // "All items in public, private, and active must have unique names." -interface R6ClassMember { val name: String } -data class R6ClassField(override val name: String, val isPublic: Boolean = true) : R6ClassMember -data class R6ClassMethod(override val name: String, val isPublic: Boolean = true) : R6ClassMember -data class R6ClassActiveBinding(override val name: String) : R6ClassMember +interface IR6ClassMember { val name: String } +data class R6ClassMember(override val name: String, val isPublic: Boolean = true) : IR6ClassMember +data class R6ClassActiveBinding(override val name: String) : IR6ClassMember data class R6ClassInfo(val className: String, - val packageName: String, val superClasses: List, - val fields: List, - val methods: List, + val members: List, val activeBindings: List) { fun containsMember(memberName: String) : Boolean { - return (fields.map { it.name }.contains(memberName) || - methods.map { it.name }.contains(memberName) || + return (members.map { it.name }.contains(memberName) || activeBindings.map { it.name }.contains(memberName)) } fun serialize(dataStream: StubOutputStream) { dataStream.writeName(className) - dataStream.writeName(packageName) DataInputOutputUtilRt.writeSeq(dataStream, superClasses) { dataStream.writeName(it) } - DataInputOutputUtilRt.writeSeq(dataStream, fields) { + DataInputOutputUtilRt.writeSeq(dataStream, members) { dataStream.writeName(it.name); dataStream.writeBoolean(it.isPublic) } - DataInputOutputUtilRt.writeSeq(dataStream, methods) { - dataStream.writeName(it.name) - dataStream.writeBoolean(it.isPublic); - } - DataInputOutputUtilRt.writeSeq(dataStream, activeBindings) { dataStream.writeName(it.name) } @@ -52,19 +42,12 @@ data class R6ClassInfo(val className: String, companion object { fun deserialize(dataStream: StubInputStream): R6ClassInfo { val className = StringRef.toString(dataStream.readName()) - val packageName = StringRef.toString(dataStream.readName()) val superClasses = DataInputOutputUtilRt.readSeq(dataStream) { StringRef.toString(dataStream.readName()) } - val fields = DataInputOutputUtilRt.readSeq(dataStream) { - val name = StringRef.toString(dataStream.readName()) - val isPublic = dataStream.readBoolean() - R6ClassField(name, isPublic) - } - - val methods = DataInputOutputUtilRt.readSeq(dataStream) { + val members = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassMethod(name, isPublic) + R6ClassMember(name, isPublic) } val activeBindings = DataInputOutputUtilRt.readSeq(dataStream) { @@ -72,7 +55,7 @@ data class R6ClassInfo(val className: String, R6ClassActiveBinding(name) } - return R6ClassInfo(className, packageName, superClasses, fields, methods, activeBindings) + return R6ClassInfo(className, superClasses, members, activeBindings) } } } @@ -80,7 +63,7 @@ data class R6ClassInfo(val className: String, class R6ClassKeywordsProvider { companion object { val predefinedClassMethods = listOf( - R6ClassMethod("clone", true) + R6ClassMember("clone", true) ) val visibilityModifiers = listOf( diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index f7f04afd2..ca1481dba 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -109,7 +109,7 @@ object R6ClassInfoUtil { return (inheritClassDefinition?.reference?.resolve() as? RAssignmentStatement)?.assignedValue as? RCallExpression } - fun getAllClassMembers(callExpression: RCallExpression): List { + fun getAllClassMembers(callExpression: RCallExpression): List { val r6ClassInfo = CachedValuesManager.getProjectPsiDependentCache(callExpression) { callExpression.associatedR6ClassInfo } ?: return emptyList() val allSuperClasses = getAssociatedSuperClassesHierarchy(callExpression) @@ -117,47 +117,28 @@ object R6ClassInfoUtil { val project = callExpression.project if (allSuperClasses != null) { - return (r6ClassInfo.fields + r6ClassInfo.methods + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.fields + it.methods + it.activeBindings } + return (r6ClassInfo.members + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.members + it.activeBindings } }).distinctBy { it.name } } return emptyList() } - fun getAssociatedFields(callExpression: RCallExpression, - argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), - onlyPublic: Boolean = false): List? { - argumentInfo ?: return null - if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - - val r6ClassFields = mutableListOf() - val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) - - if (!onlyPublic) { - val privateContents = (argumentInfo.getArgumentPassedToParameter( - argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) - } - - return r6ClassFields - } - - fun getAssociatedMethods(callExpression: RCallExpression, + fun getAssociatedMembers(callExpression: RCallExpression, argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), - onlyPublic: Boolean = false): List? { + onlyPublic: Boolean = false): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - val r6ClassMethods = mutableListOf() + val r6ClassMethods = mutableListOf() val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) + if (!publicContents.isNullOrEmpty()) getMembersFromExpressionList(r6ClassMethods, publicContents, true) if (!onlyPublic) { val privateContents = (argumentInfo.getArgumentPassedToParameter( argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) + if (!privateContents.isNullOrEmpty()) getMembersFromExpressionList(r6ClassMethods, privateContents, false) } return r6ClassMethods @@ -189,30 +170,18 @@ object R6ClassInfoUtil { val argumentInfo = RParameterInfoUtil.getArgumentInfo(callExpression, definition) ?: return null val className = getAssociatedClassNameFromR6ClassCall(callExpression, argumentInfo) ?: return null val superClassesHierarchy = getAssociatedSuperClassesHierarchy(callExpression, argumentInfo) ?: emptyList() - val fields = getAssociatedFields(callExpression, argumentInfo) ?: emptyList() - val methods = getAssociatedMethods(callExpression, argumentInfo) ?: emptyList() + val members = getAssociatedMembers(callExpression, argumentInfo) ?: emptyList() val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) ?: emptyList() - val packageName = RPackageProjectManager.getInstance(project).getProjectPackageDescriptionInfo()?.packageName ?: "" - return R6ClassInfo(className, packageName, superClassesHierarchy, fields, methods, activeBindings) + return R6ClassInfo(className, superClassesHierarchy, members, activeBindings) } - private fun getFieldsFromExpressionList(r6ClassFields: MutableList, + private fun getMembersFromExpressionList(r6ClassFields: MutableList, callExpressions: List, isFromPublicScope: Boolean) { callExpressions.forEach { - if (it.lastChild !is RFunctionExpression && !it.name.isNullOrEmpty()) { - r6ClassFields.add(R6ClassField(it.name!!, isFromPublicScope)) - } - } - } - - private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, - callExpressions: List, - isFromPublicScope: Boolean) { - callExpressions.forEach { - if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { - r6ClassMethods.add(R6ClassMethod(it.name!!, isFromPublicScope)) + if (it.lastChild != null && !it.name.isNullOrEmpty()) { + r6ClassFields.add(R6ClassMember(it.name!!, isFromPublicScope)) } } } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 17e49e75a..187d89834 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -21,7 +21,7 @@ import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.r.RLanguage import org.jetbrains.r.classes.r6.R6ClassInfoUtil -import org.jetbrains.r.classes.r6.R6ClassMember +import org.jetbrains.r.classes.r6.IR6ClassMember import org.jetbrains.r.classes.r6.R6ClassKeywordsProvider import org.jetbrains.r.classes.r6.context.* import org.jetbrains.r.classes.s4.* @@ -150,7 +150,7 @@ class RCompletionContributor : CompletionContributor() { return false } - private fun addMembersCompletion(r6ClassMembers: List?, + private fun addMembersCompletion(r6ClassMembers: List?, shownNames: MutableSet, result: CompletionResultSet): Boolean { var hasNewResults = false diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index fd6253be9..1a3126c3e 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -35,10 +35,8 @@ import io.grpc.stub.StreamObserver import org.jetbrains.annotations.TestOnly import org.jetbrains.concurrency.* import org.jetbrains.r.RBundle -import org.jetbrains.r.classes.r6.R6ClassActiveBinding -import org.jetbrains.r.classes.r6.R6ClassField +import org.jetbrains.r.classes.r6.* import org.jetbrains.r.classes.r6.R6ClassInfo -import org.jetbrains.r.classes.r6.R6ClassMethod import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassSlot import org.jetbrains.r.debugger.RDebuggerUtil @@ -881,7 +879,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getLoadedShortR6ClassInfos(): List? { return try { executeWithCheckCancel(asyncStub::getLoadedShortR6ClassInfos, Empty.getDefaultInstance()).shortR6ClassInfosList.map { - R6ClassInfo(it.name, it.`package`, emptyList(), emptyList(), emptyList(), emptyList()) + R6ClassInfo(it.name, emptyList(), emptyList(), emptyList()) } } catch (e: RInteropTerminated) { null @@ -891,8 +889,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByObjectName(ref: RReference): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) - R6ClassInfo(res.className, res.packageName, res.superClassesList, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, - res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + R6ClassInfo(res.className, res.superClassesList, res.membersList.map { R6ClassMember(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) null } catch (e: RInteropTerminated) { null @@ -902,8 +899,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByClassName(className: String): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByClassName, StringValue.of(className)) - R6ClassInfo(res.className, res.packageName, res.superClassesList, res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, - res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + R6ClassInfo(res.className, res.superClassesList, res.membersList.map { R6ClassMember(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) } catch (e: RInteropTerminated) { null } diff --git a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt index 573dc241a..1c5315041 100644 --- a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt +++ b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt @@ -8,10 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.stubs.BinaryFileStubBuilder import com.intellij.psi.stubs.Stub import com.intellij.util.indexing.FileContent -import org.jetbrains.r.classes.r6.R6ClassActiveBinding -import org.jetbrains.r.classes.r6.R6ClassField -import org.jetbrains.r.classes.r6.R6ClassInfo -import org.jetbrains.r.classes.r6.R6ClassMethod +import org.jetbrains.r.classes.r6.* import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassSlot import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo @@ -54,10 +51,8 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { R_SKELETON_CALL_EXPRESSION, null, R6ClassInfo(symbol.name, - r6ClassRepresentation.packageName, r6ClassRepresentation.superClassesList, - r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic) }, - r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, + r6ClassRepresentation.membersList.map { R6ClassMember(it.name, it.isPublic) }, r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) } diff --git a/temporary/service.proto b/temporary/service.proto index 0897c69ea..eeaebc1d6 100644 --- a/temporary/service.proto +++ b/temporary/service.proto @@ -890,14 +890,9 @@ message ShortS4ClassInfoList { } message R6ClassInfo { - message R6ClassField { + message R6ClassMember { string name = 1; - bool isPublic = 3; - } - - message R6ClassMethod { - string name = 1; - bool isPublic = 3; + bool isPublic = 2; } message R6ClassActiveBinding { @@ -905,18 +900,14 @@ message R6ClassInfo { } string className = 1; - string packageName = 2; - string superClass = 3; - repeated R6ClassField fields = 4; - repeated R6ClassField methods = 5; - repeated R6ClassField activeBindings = 6; + repeated string superClasses = 2; + repeated R6ClassMember members = 3; + repeated R6ClassActiveBinding activeBindings = 4; } message ShortR6ClassInfoList { message ShortR6ClassInfo { string name = 1; - string package = 2; - bool isVirtual = 3; } repeated ShortR6ClassInfo shortR6ClassInfos = 1; diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index de912b9c6..f97edddd8 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -97,29 +97,27 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { assert(superClassNames.contains("SuperParentClass")) } - fun testGetAssociatedFields(){ + fun testClassContainsFields(){ val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) + val classFields = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) assertNotNull(classFields) - assertEquals(classFields!!.size, 3) - val classFieldsNames = classFields.map { it.name } + val classFieldsNames = classFields!!.map { it.name } assertContainsElements(classFieldsNames, "weight") assertContainsElements(classFieldsNames, "speed") assertContainsElements(classFieldsNames, "engine_rpm") } - fun testGetAssociatedMethods(){ + fun testClassContainsMethods(){ val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) + val classMethods = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) TestCase.assertNotNull(classMethods) - assertEquals(classMethods!!.size, 3) - val classMethodsNames = classMethods.map { it.name } + val classMethodsNames = classMethods!!.map { it.name } assertContainsElements(classMethodsNames, "accelerate") assertContainsElements(classMethodsNames, "slowDown") assertContainsElements(classMethodsNames, "maximize") @@ -140,12 +138,11 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { fun testGetShortenedClassAssociatedFields(){ val rAssignmentStatement = getRootElementOfPsi(shortedClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) + val classFields = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) TestCase.assertNotNull(classFields) - assertEquals(classFields!!.size, 2) - val classFieldsNames = classFields.map { it.name } + val classFieldsNames = classFields!!.map { it.name } assertContainsElements(classFieldsNames, "weight") assertContainsElements(classFieldsNames, "speed") classFields.forEach { assertEquals(true, it.isPublic) } @@ -154,12 +151,11 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { fun testGetShortenedClassAssociatedMethods(){ val rAssignmentStatement = getRootElementOfPsi(shortedClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) + val classMethods = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) TestCase.assertNotNull(classMethods) - assertEquals(classMethods!!.size, 2) - val classMethodsNames = classMethods.map { it.name } + val classMethodsNames = classMethods!!.map { it.name } assertContainsElements(classMethodsNames, "accelerate") assertContainsElements(classMethodsNames, "slowDown") classMethods.forEach { assertEquals(true, it.isPublic) } From ae85d29e7ad6dd6363e117346704984cb3a397bf Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 18 Apr 2021 15:46:45 +0300 Subject: [PATCH 39/52] correct classname retrieval --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 6 ++--- .../r/editor/RCompletionContributor.kt | 22 ++++++++++++++----- .../r/psi/RCallExpressionElementType.kt | 2 -- src/org/jetbrains/r/rinterop/RInterop.kt | 1 - 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index ca1481dba..145a64f0d 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -46,7 +46,7 @@ object R6ClassInfoUtil { fun getAssociatedClassNameFromInstantiationCall(call: RCallExpression): String? { val callExpression = call.expression as? RMemberExpressionImpl ?: return null if (callExpression.rightExpr?.text != functionNew) return null - return callExpression.leftExpr?.text + return callExpression.leftExpr?.name } /** @@ -67,8 +67,8 @@ object R6ClassInfoUtil { argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - val arg = argumentInfo.getArgumentPassedToParameter(argumentClassName) as? RStringLiteralExpression - return arg?.name + val rAssignmentStatement = callExpression.parent as? RAssignmentStatement ?: return null + return rAssignmentStatement.assignee?.name } /** diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 187d89834..2ddd9a9b7 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -133,16 +133,28 @@ class RCompletionContributor : CompletionContributor() { shownNames: MutableSet, result: CompletionResultSet, runtimeInfo: RConsoleRuntimeInfo): Boolean { - TODO("Not yet implemented") + val obj = psiElement.leftExpr ?: return false + // obj$ + // env$obj$ + if (obj !is RIdentifierExpression && + (obj !is RMemberExpression || obj.rightExpr !is RIdentifierExpression)) { + return false + } + + val text = obj.text + runtimeInfo.loadR6ClassInfoByObjectName(text)?.let { info -> + return addMembersCompletion(info.members + info.activeBindings, shownNames, result) + } + return false } - override fun addCompletionStatically(rMemberExpression: RMemberExpression, + override fun addCompletionStatically(psiElement: RMemberExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(rMemberExpression) + val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(psiElement) if (className != null) { - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, rMemberExpression.project, - RSearchScopeUtil.getScope(rMemberExpression)).forEach { + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, psiElement.project, + RSearchScopeUtil.getScope(psiElement)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it) + R6ClassKeywordsProvider.predefinedClassMethods, shownNames, result) } } diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index 138c244ca..f9feda4c6 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -19,8 +19,6 @@ import org.jetbrains.r.psi.stubs.RCallExpressionStub import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl import org.jetbrains.r.psi.stubs.RStubElementType import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider -import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex -import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import java.io.IOException class RCallExpressionElementType(debugName: String) : RStubElementType(debugName) { diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index 1a3126c3e..b6f36125c 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -890,7 +890,6 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) R6ClassInfo(res.className, res.superClassesList, res.membersList.map { R6ClassMember(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) - null } catch (e: RInteropTerminated) { null } From 63449b3d43250402ca5b7020fd26e15b12bb0fe8 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 18 Apr 2021 17:17:30 +0300 Subject: [PATCH 40/52] r6 classname inspection added --- .../r6/UnmatchingR6ClassNameInspection.kt | 40 +++++++++++++++++++ .../s4}/DeprecatedSetClassArgsInspection.kt | 4 +- .../s4}/InstanceOfVirtualS4ClassInspection.kt | 3 +- .../r6/UnmatchingR6ClassNameInspectionTest.kt | 36 +++++++++++++++++ .../DeprecatedSetClassArgsInspectionTest.kt | 6 +-- .../InstanceOfVirtualS4ClassInspectionTest.kt | 6 +-- 6 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt rename src/org/jetbrains/r/inspections/{s4class => classes/s4}/DeprecatedSetClassArgsInspection.kt (94%) rename src/org/jetbrains/r/inspections/{s4class => classes/s4}/InstanceOfVirtualS4ClassInspection.kt (94%) create mode 100644 test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt rename test/org/jetbrains/r/inspections/{ => classes/s4}/DeprecatedSetClassArgsInspectionTest.kt (91%) rename test/org/jetbrains/r/inspections/{ => classes/s4}/InstanceOfVirtualS4ClassInspectionTest.kt (87%) diff --git a/src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt b/src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt new file mode 100644 index 000000000..cfd150483 --- /dev/null +++ b/src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.inspections.classes.r6 + +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.r.RBundle +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.inspections.RInspection +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RStringLiteralExpression +import org.jetbrains.r.psi.api.RVisitor +import org.jetbrains.r.psi.isFunctionFromLibrary + +class UnmatchingR6ClassNameInspection : RInspection() { + override fun getDisplayName() = RBundle.message("inspection.r6class.naming.convention.classname") + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + return Visitor(holder) + } + + private class Visitor(private val myProblemHolder: ProblemsHolder) : RVisitor() { + override fun visitCallExpression(call: RCallExpression) { + if (!call.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) return + val classNameExpression = call.argumentList.expressionList.firstOrNull() as? RStringLiteralExpression ?: return + val className = classNameExpression.name ?: return + val userClassVariableName = R6ClassInfoUtil.getAssociatedClassNameFromR6ClassCall(call) ?: return + + if (className != userClassVariableName) { + myProblemHolder.registerProblem(classNameExpression, + RBundle.message("inspection.r6class.naming.convention.classname", className), + ProblemHighlightType.GENERIC_ERROR) + } + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/inspections/s4class/DeprecatedSetClassArgsInspection.kt b/src/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspection.kt similarity index 94% rename from src/org/jetbrains/r/inspections/s4class/DeprecatedSetClassArgsInspection.kt rename to src/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspection.kt index 964344a8f..0736b64ec 100644 --- a/src/org/jetbrains/r/inspections/s4class/DeprecatedSetClassArgsInspection.kt +++ b/src/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspection.kt @@ -1,8 +1,8 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections.s4class +package org.jetbrains.r.inspections.classes.s4 import com.intellij.codeInspection.LocalInspectionToolSession import com.intellij.codeInspection.ProblemHighlightType diff --git a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt b/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt similarity index 94% rename from src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt rename to src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt index 1e43eb293..d4588e649 100644 --- a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt +++ b/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt @@ -2,7 +2,7 @@ * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections.s4class +package org.jetbrains.r.inspections.classes.s4 import com.intellij.codeInspection.LocalInspectionToolSession import com.intellij.codeInspection.ProblemHighlightType @@ -16,7 +16,6 @@ import org.jetbrains.r.psi.api.RVisitor import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RSearchScopeUtil import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider -import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class InstanceOfVirtualS4ClassInspection : RInspection() { override fun getDisplayName() = RBundle.message("inspection.virtual.s4class.instance.name") diff --git a/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt new file mode 100644 index 000000000..1c6f0ae55 --- /dev/null +++ b/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.inspections.classes.r6 + +import org.jetbrains.r.RBundle +import org.jetbrains.r.inspections.RInspectionTest +import org.jetbrains.r.inspections.classes.s4.InstanceOfVirtualS4ClassInspection + +class UnmatchingR6ClassNameInspectionTest : RInspectionTest() { + override fun setUp() { + super.setUp() + addLibraries() + } + + fun testClassNameInspection() { + doExprTest(""" + UserClass <- R6Class("UserClass") + """.trimIndent()) + + doExprTest(""" + UserClass <- R6Class(${makeError("MyClass")}) + """.trimIndent()) + } + + override val inspection = UnmatchingR6ClassNameInspection::class.java + + companion object { + private fun msg(argumentName: String) = RBundle.message("inspection.r6class.naming.convention.classname", argumentName) + + private fun makeError(className: String): String { + return "'$className'" + } + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/inspections/DeprecatedSetClassArgsInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspectionTest.kt similarity index 91% rename from test/org/jetbrains/r/inspections/DeprecatedSetClassArgsInspectionTest.kt rename to test/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspectionTest.kt index 40100f427..9bd2875fa 100644 --- a/test/org/jetbrains/r/inspections/DeprecatedSetClassArgsInspectionTest.kt +++ b/test/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspectionTest.kt @@ -1,11 +1,11 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections +package org.jetbrains.r.inspections.classes.s4 import org.jetbrains.r.RBundle -import org.jetbrains.r.inspections.s4class.DeprecatedSetClassArgsInspection +import org.jetbrains.r.inspections.RInspectionTest class DeprecatedSetClassArgsInspectionTest : RInspectionTest() { diff --git a/test/org/jetbrains/r/inspections/InstanceOfVirtualS4ClassInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspectionTest.kt similarity index 87% rename from test/org/jetbrains/r/inspections/InstanceOfVirtualS4ClassInspectionTest.kt rename to test/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspectionTest.kt index a84dc8884..e56e07dab 100644 --- a/test/org/jetbrains/r/inspections/InstanceOfVirtualS4ClassInspectionTest.kt +++ b/test/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspectionTest.kt @@ -1,11 +1,11 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections +package org.jetbrains.r.inspections.classes.s4 import org.jetbrains.r.RBundle -import org.jetbrains.r.inspections.s4class.InstanceOfVirtualS4ClassInspection +import org.jetbrains.r.inspections.RInspectionTest class InstanceOfVirtualS4ClassInspectionTest : RInspectionTest() { From d961db565a17c40ffcfb01112c3646b375d98131 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 18 Apr 2021 17:21:02 +0300 Subject: [PATCH 41/52] resources for inspections --- resources/META-INF/rplugin-common.xml | 10 ++++++++-- .../UnmatchingR6ClassNameInspection.html | 6 ++++++ resources/messages/RPluginBundle.properties | 2 ++ .../classes/r6/UnmatchingR6ClassNameInspectionTest.kt | 1 - 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index 0d5070a63..0391a3068 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -683,13 +683,19 @@ You can find the source code in the following repositories: bundle="messages.RPluginBundle" key="inspection.deprecated.double.starts.name" implementationClass="org.jetbrains.r.inspections.DeprecatedDoubleStarts"/> + + implementationClass="org.jetbrains.r.inspections.classes.s4.DeprecatedSetClassArgsInspection"/> + implementationClass="org.jetbrains.r.inspections.classes.s4.InstanceOfVirtualS4ClassInspection"/> + + + R diff --git a/resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html b/resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html new file mode 100644 index 000000000..e0656c7ec --- /dev/null +++ b/resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html @@ -0,0 +1,6 @@ + + +Variable and argument class name in expression 'UserClass <- R6Class("UserClass")' should match. +It’s not strictly needed, but it improves error messages and makes it possible to use R6 objects with S3 generics + + \ No newline at end of file diff --git a/resources/messages/RPluginBundle.properties b/resources/messages/RPluginBundle.properties index ba7bc5088..ffd38803e 100644 --- a/resources/messages/RPluginBundle.properties +++ b/resources/messages/RPluginBundle.properties @@ -381,6 +381,8 @@ inspection.deprecated.setClass.args.description=Argument ''{0}'' is deprecated f inspection.virtual.s4class.instance.name=Trying to generate an object from a virtual class inspection.virtual.s4class.instance.description=Class ''{0}'' is virtual and object of this class cannot be created +inspection.r6class.naming.convention.classname=Classname should match the variable assignee name + install.libraries.fix.name=Install {0} install.libraries.fix.family.name=Install packages diff --git a/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt index 1c6f0ae55..2d2fc3701 100644 --- a/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt +++ b/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt @@ -6,7 +6,6 @@ package org.jetbrains.r.inspections.classes.r6 import org.jetbrains.r.RBundle import org.jetbrains.r.inspections.RInspectionTest -import org.jetbrains.r.inspections.classes.s4.InstanceOfVirtualS4ClassInspection class UnmatchingR6ClassNameInspectionTest : RInspectionTest() { override fun setUp() { From bde4c7c018773b03626e15d4cfd6a423dccb661d Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 20 Apr 2021 01:44:04 +0300 Subject: [PATCH 42/52] add test for console member suggestion --- .../r/completion/classes/R6ClassCompletionTest.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index dd3c4f738..1d0836076 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -78,6 +78,15 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { """.trimIndent(), "\"active\"" to "string", "\"public\"" to "string", "\"private\"" to "string", containedInSuggestions = true) } + fun testConsoleMembersSuggestion() { + rInterop.executeCode(""" + library(R6) + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } )) + obj <- MyClass${'$'}new() + """.trimIndent()) + doTest("obj${'$'}", "clone" to "", "random" to "", "someField" to "", "someMethod" to "", withRuntimeInfo = true, inConsole = true, containedInSuggestions = true) + } + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { val result = doTestBase(text, withRuntimeInfo, inConsole) assertNotNull(result) From d1d206d7ad4dcdcd4d47a704b43f9025fe25231e Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 20 Apr 2021 01:54:06 +0300 Subject: [PATCH 43/52] remove unused console interop API --- .../r/console/RConsoleRuntimeInfo.kt | 20 ------------------- .../r/editor/RCompletionContributor.kt | 6 ++---- .../jetbrains/r/completion/AutoPopupTest.kt | 8 -------- 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt b/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt index a8fa53411..878a74730 100644 --- a/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt +++ b/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt @@ -32,11 +32,9 @@ interface RConsoleRuntimeInfo { fun loadExtraNamedArguments(functionName: String): RExtraNamedArgumentsInfo fun loadExtraNamedArguments(functionName: String, functionExpression: RFunctionExpression): RExtraNamedArgumentsInfo fun loadShortS4ClassInfos(): List - fun loadShortR6ClassInfos(): List fun loadS4ClassInfoByObjectName(objectName: String): RS4ClassInfo? fun loadS4ClassInfoByClassName(className: String): RS4ClassInfo? fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? - fun loadR6ClassInfoByClassName(className: String): R6ClassInfo? fun getFormalArguments(expression: String) : List fun loadTableColumns(expression: String): TableInfo val rInterop: RInterop @@ -140,30 +138,12 @@ class RConsoleRuntimeInfoImpl(override val rInterop: RInterop) : RConsoleRuntime } } - /** - * @return list of [R6ClassInfo] without information about [R6ClassInfo.fields], [R6ClassInfo.methods] and [R6ClassInfo.activeBindings] - */ - override fun loadShortR6ClassInfos(): List { - loadedShortR6ClassInfosCache.get().let { infos -> - if (infos != null) return infos - return rInterop.getLoadedShortR6ClassInfos().also { - loadedShortR6ClassInfosCache.set(it) - } ?: emptyList() - } - } - override fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? { return r6ClassInfosByObjectNameCache.getOrPut(objectName) { rInterop.getR6ClassInfoByObjectName(RReference.expressionRef(objectName, rInterop)) } } - override fun loadR6ClassInfoByClassName(className: String): R6ClassInfo? { - return r6ClassInfosByClassNameCache.getOrPut(className) { - rInterop.getR6ClassInfoByClassName(className) - } - } - override fun getFormalArguments(expression: String): List { return formalArgumentsCache.getOrPut(expression) { rInterop.getFormalArguments(RReference.expressionRef(expression, rInterop)) } } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 2ddd9a9b7..4950f3faf 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -623,7 +623,7 @@ class RCompletionContributor : CompletionContributor() { when (r6Context) { is R6CreateClassNameContext -> { // suggestion of name of `<- R6Class("")` - result.addR6ClassNameCompletion(classNameExpression, shownNames, file.runtimeInfo?.loadedPackages?.keys) + result.addR6ClassNameCompletion(classNameExpression, shownNames) } else -> return @@ -657,11 +657,9 @@ class RCompletionContributor : CompletionContributor() { } private fun CompletionResultSet.addR6ClassNameCompletion(classNameExpression: RExpression, - shownNames: MutableSet, - loadedPackages: Set?) { + shownNames: MutableSet) { val classAssignmentExpression = PsiTreeUtil.getParentOfType(classNameExpression, RAssignmentStatement::class.java) as RAssignmentStatement - ?: return val classNameToSuggest = classAssignmentExpression.assignee?.text ?: return if (classNameToSuggest in shownNames) return shownNames.add(classNameToSuggest) diff --git a/test/org/jetbrains/r/completion/AutoPopupTest.kt b/test/org/jetbrains/r/completion/AutoPopupTest.kt index 7d7cc6288..8d8fe0f3d 100644 --- a/test/org/jetbrains/r/completion/AutoPopupTest.kt +++ b/test/org/jetbrains/r/completion/AutoPopupTest.kt @@ -199,18 +199,10 @@ class AutoPopupTest : RLightCodeInsightFixtureTestCase() { throw NotImplementedError() } - override fun loadShortR6ClassInfos(): List { - throw NotImplementedError() - } - override fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? { throw NotImplementedError() } - override fun loadR6ClassInfoByClassName(className: String): R6ClassInfo? { - throw NotImplementedError() - } - override fun loadExtraNamedArguments(functionName: String, functionExpression: RFunctionExpression): RExtraNamedArgumentsInfo { throw NotImplementedError() } From 53ec95178c9cf307defaa207ca6a3626ea5acf1e Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 20 Apr 2021 01:56:10 +0300 Subject: [PATCH 44/52] Rkernel and proto saved to temporary --- temporary/{ => RKernel-Proto}/service.proto | 0 temporary/RKernel/RPIServiceImpl.h | 212 ++++++++ temporary/RKernel/RRefs.cpp | 410 ++++++++++++++++ temporary/RKernel/init.R | 504 ++++++++++++++++++++ 4 files changed, 1126 insertions(+) rename temporary/{ => RKernel-Proto}/service.proto (100%) create mode 100644 temporary/RKernel/RPIServiceImpl.h create mode 100644 temporary/RKernel/RRefs.cpp create mode 100644 temporary/RKernel/init.R diff --git a/temporary/service.proto b/temporary/RKernel-Proto/service.proto similarity index 100% rename from temporary/service.proto rename to temporary/RKernel-Proto/service.proto diff --git a/temporary/RKernel/RPIServiceImpl.h b/temporary/RKernel/RPIServiceImpl.h new file mode 100644 index 000000000..726a57617 --- /dev/null +++ b/temporary/RKernel/RPIServiceImpl.h @@ -0,0 +1,212 @@ +// Rkernel is an execution kernel for R interpreter +// Copyright (C) 2019 JetBrains s.r.o. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef RWRAPPER_RPI_SERVICE_IMPL_H +#define RWRAPPER_RPI_SERVICE_IMPL_H + +#include "protos/service.grpc.pb.h" +#include +#include +#include "util/BlockingQueue.h" +#include "util/IndexedStorage.h" +#include "IO.h" +#include "Options.h" +#include "debugger/RDebugger.h" +#include "RStuff/MySEXP.h" + +using grpc::Status; +using grpc::ServerContext; +using grpc::ServerWriter; +using namespace rplugininterop; +using namespace google::protobuf; + +class RPIServiceImpl : public RPIService::Service { +public: + RPIServiceImpl(); + ~RPIServiceImpl() override; + + Status getInfo(ServerContext* context, const Empty*, GetInfoResponse* response) override; + Status isBusy(ServerContext* context, const Empty*, BoolValue* response) override; + Status init(ServerContext* context, const Init* request, ServerWriter* response) override; + Status quit(ServerContext* context, const Empty*, Empty*) override; + Status quitProceed(ServerContext* context, const Empty*, Empty*) override; + + Status executeCode(ServerContext* context, const ExecuteCodeRequest* request, ServerWriter* writer) override; + Status sendReadLn(ServerContext* context, const StringValue* request, Empty*) override; + Status sendEof(ServerContext* context, const Empty*, Empty*) override; + Status replInterrupt(ServerContext* context, const Empty*, Empty*) override; + Status getAsyncEvents(ServerContext* context, const Empty*, ServerWriter* writer) override; + + Status debugAddOrModifyBreakpoint(ServerContext* context, const DebugAddOrModifyBreakpointRequest* request, Empty*) override; + Status debugSetMasterBreakpoint(ServerContext* context, const DebugSetMasterBreakpointRequest* request, Empty*) override; + Status debugRemoveBreakpoint(ServerContext* context, const Int32Value* request, Empty*) override; + Status debugCommandContinue(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandPause(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandStop(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandStepOver(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandStepInto(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandStepIntoMyCode(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandStepOut(ServerContext* context, const Empty*, Empty*) override; + Status debugCommandRunToPosition(ServerContext* context, const SourcePosition* request, Empty*) override; + Status debugMuteBreakpoints(ServerContext* context, const BoolValue* request, Empty*) override; + + Status copyToPersistentRef(ServerContext* context, const RRef* request, CopyToPersistentRefResponse* response) override; + Status disposePersistentRefs(ServerContext* context, const PersistentRefList* request, Empty*) override; + + Status loaderGetParentEnvs(ServerContext* context, const RRef* request, ParentEnvsResponse* response) override; + Status loaderGetVariables(ServerContext* context, const GetVariablesRequest* request, VariablesResponse* response) override; + Status loaderGetLoadedNamespaces(ServerContext* context, const Empty*, StringList* response) override; + Status loaderGetValueInfo(ServerContext* context, const RRef* request, ValueInfo* response) override; + Status evaluateAsText(ServerContext* context, const RRef* request, StringOrError* response) override; + Status evaluateAsBoolean(ServerContext* context, const RRef* request, BoolValue* response) override; + Status getDistinctStrings(ServerContext* context, const RRef* request, StringList* response) override; + Status getFunctionSourcePosition(ServerContext* context, const RRef* request, GetFunctionSourcePositionResponse* response) override; + Status getSourceFileText(ServerContext* context, const StringValue* request, StringValue* response) override; + Status getSourceFileName(ServerContext* context, const StringValue* request, StringValue* response) override; + Status loadObjectNames(ServerContext* context, const RRef* request, StringList* response) override; + Status findInheritorNamedArguments(ServerContext* context, const RRef* request, StringList* response) override; + Status findExtraNamedArguments(ServerContext* context, const RRef* request, ExtraNamedArguments* response) override; + Status getS4ClassInfoByObjectName(ServerContext* context, const RRef* request, S4ClassInfo* response) override; + Status getS4ClassInfoByClassName(ServerContext* context, const StringValue* request, S4ClassInfo* response) override; + Status getR6ClassInfoByObjectName(ServerContext* context, const RRef* request, R6ClassInfo* response) override; + Status getLoadedShortS4ClassInfos(ServerContext* context, const Empty* request, ShortS4ClassInfoList* response) override; + Status getTableColumnsInfo(ServerContext* context, const TableColumnsInfoRequest* request, TableColumnsInfo* response) override; + Status getFormalArguments(ServerContext* context, const RRef* request, StringList* response) override; + Status getEqualityObject(ServerContext* context, const RRef* request, Int64Value* response) override; + Status setValue(ServerContext* context, const SetValueRequest* request, ValueInfo* response) override; + Status getObjectSizes(ServerContext* context, const RRefList* request, Int64List* response) override; + + Status getRMarkdownChunkOptions(ServerContext* context, const Empty*, StringList* response) override; + + Status graphicsInit(ServerContext* context, const GraphicsInitRequest* request, ServerWriter* writer) override; + Status graphicsDump(ServerContext* context, const Empty*, GraphicsDumpResponse* response) override; + Status graphicsRescale(ServerContext* context, const GraphicsRescaleRequest* request, ServerWriter* writer) override; + Status graphicsRescaleStored(ServerContext* context, const GraphicsRescaleStoredRequest* request, ServerWriter* writer) override; + Status graphicsSetParameters(ServerContext* context, const ScreenParameters* request, Empty*) override; + Status graphicsGetSnapshotPath(ServerContext* context, const GraphicsGetSnapshotPathRequest* request, GraphicsGetSnapshotPathResponse* response) override; + Status graphicsFetchPlot(ServerContext* context, const Int32Value* request, GraphicsFetchPlotResponse* response) override; + Status graphicsCreateGroup(ServerContext* context, const google::protobuf::Empty* request, ServerWriter* writer) override; + Status graphicsRemoveGroup(ServerContext* context, const google::protobuf::StringValue* request, ServerWriter* writer) override; + Status graphicsShutdown(ServerContext* context, const Empty*, ServerWriter* writer) override; + + Status beforeChunkExecution(ServerContext *context, const ChunkParameters *request, ServerWriter *writer) override; + Status afterChunkExecution(ServerContext *context, const Empty*, ServerWriter *writer) override; + Status pullChunkOutputPaths(ServerContext *context, const Empty*, StringList* response) override; + + Status repoGetPackageVersion(ServerContext* context, const StringValue* request, ServerWriter* writer) override; + Status repoInstallPackage(ServerContext* context, const RepoInstallPackageRequest* request, Empty*) override; + Status repoAddLibraryPath(ServerContext* context, const StringValue* request, ServerWriter* writer) override; + Status repoCheckPackageInstalled(ServerContext* context, const StringValue* request, ServerWriter* writer) override; + Status repoRemovePackage(ServerContext* context, const RepoRemovePackageRequest* request, Empty*) override; + + Status previewDataImport(ServerContext* context, const PreviewDataImportRequest* request, ServerWriter* writer) override; + Status commitDataImport(ServerContext* context, const CommitDataImportRequest* request, Empty*) override; + + Status dataFrameRegister(ServerContext* context, const RRef* request, Int32Value* response) override; + Status dataFrameGetInfo(ServerContext* context, const RRef* request, DataFrameInfoResponse* response) override; + Status dataFrameGetData(ServerContext* context, const DataFrameGetDataRequest* request, DataFrameGetDataResponse* response) override; + Status dataFrameSort(ServerContext* context, const DataFrameSortRequest* request, Int32Value* response) override; + Status dataFrameFilter(ServerContext* context, const DataFrameFilterRequest* request, Int32Value* response) override; + Status dataFrameRefresh(ServerContext* context, const RRef* request, BoolValue* response) override; + + Status getWorkingDir(ServerContext* context, const Empty*, StringValue* response) override; + Status setWorkingDir(ServerContext* context, const StringValue* request, Empty*) override; + Status clearEnvironment(ServerContext* context, const RRef* request, Empty*) override; + Status getSysEnv(ServerContext* context, const GetSysEnvRequest* request, StringList* response) override; + Status loadInstalledPackages(ServerContext* context, const Empty*, RInstalledPackageList* response) override; + Status loadLibPaths(ServerContext* context, const Empty*, RLibraryPathList* response) override; + Status loadLibrary(ServerContext* context, const StringValue* request, Empty*) override; + Status unloadLibrary(ServerContext* context, const UnloadLibraryRequest* request, Empty*) override; + Status saveGlobalEnvironment(ServerContext *context, const StringValue *request, Empty*) override; + Status loadEnvironment(ServerContext *context, const LoadEnvironmentRequest *request, Empty*) override; + Status setOutputWidth(ServerContext* context, const Int32Value* request, Empty*) override; + Status clientRequestFinished(ServerContext* context, const Empty*, Empty*) override; + Status rStudioApiResponse(ServerContext* context, const RObject* request, Empty* response) override; + + Status convertRoxygenToHTML(ServerContext* context, const ConvertRoxygenToHTMLRequest* request, ConvertRoxygenToHTMLResponse* response) override; + Status httpdRequest(ServerContext* context, const StringValue* request, HttpdResponse* response) override; + Status getDocumentationForPackage(ServerContext* context, const StringValue* request, HttpdResponse* response) override; + Status getDocumentationForSymbol(ServerContext* context, const DocumentationForSymbolRequest* request, HttpdResponse* response) override; + Status startHttpd(ServerContext* context, const Empty*, Int32Value* response) override; + + Status setSaveOnExit(ServerContext* context, const BoolValue* request, Empty*) override; + Status setRStudioApiEnabled(::grpc::ServerContext *context, const ::google::protobuf::BoolValue *request, ServerWriter* response) override; + + void mainLoop(); + std::string readLineHandler(std::string const& prompt); + void subprocessHandler( + bool askInput, + std::function const& inputCallback, std::function const& interruptCallback); + void subprocessHandlerStop(); + void debugPromptHandler(); + void viewHandler(SEXP expr, SEXP env, SEXP title); + void showFileHandler(std::string const& filePath, std::string const& title); + void showHelpHandler(std::string const& content, std::string const& url); + void browseURLHandler(std::string const& url); + RObject rStudioApiRequest(int32_t functionID, const RObject &args); + + void sendAsyncEvent(AsyncEvent const& e); + void sendAsyncRequestAndWait(AsyncEvent const& e); + void setChildProcessState(); + volatile bool terminate = false; + volatile bool terminateProceed = false; + + void executeOnMainThread(std::function const& f, ServerContext* contextForCancellation = nullptr, bool immediate = false); + + OutputHandler getOutputHandlerForChildProcess(); + + void setValueImpl(RRef const& ref, SEXP value); + SEXP dereference(RRef const& ref); + + OutputHandler replOutputHandler; + void writeToReplOutputHandler(std::string const& s, OutputType type); + + IndexedStorage persistentRefStorage; + +private: + BlockingQueue asyncEvents; + + enum ReplState { + PROMPT, DEBUG_PROMPT, READ_LINE, REPL_BUSY, CHILD_PROCESS, SUBPROCESS_INPUT + }; + bool isReplOutput = false; + ReplState replState = REPL_BUSY; + volatile bool busy = true; + volatile bool subprocessActive = false; + bool subprocessInterrupt = false; + + bool isInClientRequest = false; + bool isInRStudioApiRequest = false; + + std::vector lastErrorStack; + + Status executeCommand(ServerContext* context, const std::string& command, ServerWriter* writer); + + Status replExecuteCommand(ServerContext* context, const std::string& command); + + friend void quitRPIService(); + friend void saveRWrapperCrashReport(std::string const&); +}; + +const int CLIENT_RPC_TIMEOUT_MILLIS = 60000; + +extern std::unique_ptr rpiService; + +void initRPIService(); +void quitRPIService(); + +#endif //RWRAPPER_RPI_SERVICE_IMPL_H diff --git a/temporary/RKernel/RRefs.cpp b/temporary/RKernel/RRefs.cpp new file mode 100644 index 000000000..23328d8f0 --- /dev/null +++ b/temporary/RKernel/RRefs.cpp @@ -0,0 +1,410 @@ +// Rkernel is an execution kernel for R interpreter +// Copyright (C) 2019 JetBrains s.r.o. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "RPIServiceImpl.h" +#include +#include "RStuff/RUtil.h" +#include "debugger/SourceFileManager.h" +#include "EventLoop.h" +#include "RStuff/RObjects.h" +#include "RLoader.h" + +const int EVALUATE_AS_TEXT_MAX_LENGTH = 500000; + +SEXP RPIServiceImpl::dereference(RRef const &ref) { + switch (ref.ref_case()) { + case RRef::kPersistentIndex: { + int i = ref.persistentindex(); + return persistentRefStorage.has(i) ? (SEXP) persistentRefStorage[i] : R_NilValue; + } + case RRef::kGlobalEnv: + return R_GlobalEnv; + case RRef::kCurrentEnv: + return currentEnvironment(); + case RRef::kSysFrameIndex: { + int index = ref.sysframeindex(); + auto const &stack = rDebugger.getSavedStack(); + if (index < 0 || index >= stack.size()) return R_NilValue; + return stack[index].environment; + } + case RRef::kErrorStackSysFrameIndex: { + int index = ref.errorstacksysframeindex(); + if (index < 0 || index >= lastErrorStack.size()) return R_NilValue; + return lastErrorStack[index].environment; + } + case RRef::kMember: { + ShieldSEXP env = dereference(ref.member().env()); + return env.getVar(ref.member().name()); + } + case RRef::kParentEnv: { + PrSEXP env = dereference(ref.parentenv().env()); + int count = ref.parentenv().index(); + for (int i = 0; i < count; ++i) { + if (env.type() != ENVSXP || env == R_EmptyEnv) { + return R_NilValue; + } + env = env.parentEnv(); + } + return env; + } + case RRef::kExpression: { + ShieldSEXP env = dereference(ref.expression().env()); + std::string code = ref.expression().code(); + return RI->eval(parseCode(code), named("envir", env)); + } + case RRef::kListElement: { + ShieldSEXP list = dereference(ref.listelement().list()); + long long index = ref.listelement().index(); + ShieldSEXP unclassed = Rf_inherits(list, "factor") ? (SEXP) list : RI->unclass(list); + return RI->doubleSubscript(unclassed, index + 1); + } + case RRef::kAttributes: { + ShieldSEXP x = dereference(ref.attributes()); + return RI->attributes(Rf_lang2(RI->quote, x)); + } + default: + return R_NilValue; + } +} + +Status RPIServiceImpl::copyToPersistentRef(ServerContext *context, const RRef *request, + CopyToPersistentRefResponse *response) { + executeOnMainThread([&] { + try { + response->set_persistentindex(persistentRefStorage.add(dereference(*request))); + } catch (RExceptionBase const &e) { + response->set_error(e.what()); + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::disposePersistentRefs(ServerContext *, const PersistentRefList *request, Empty *) { + std::vector refs(request->indices().begin(), request->indices().end()); + eventLoopExecute([=] { + for (int ref : refs) { + if (persistentRefStorage.has(ref)) { + persistentRefStorage.remove(ref); + } + } + }); + return Status::OK; +} + +Status RPIServiceImpl::evaluateAsText(ServerContext *context, const RRef *request, StringOrError *response) { + executeOnMainThread([&] { + try { + PrSEXP value = dereference(*request); + if (value.type() == STRSXP) { + value = RI->substring(value, 1, EVALUATE_AS_TEXT_MAX_LENGTH); + } + response->set_value(getPrintedValueWithLimit(value, EVALUATE_AS_TEXT_MAX_LENGTH)); + } catch (RExceptionBase const &e) { + response->set_error(e.what()); + } catch (...) { + response->set_error(""); + throw; + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::evaluateAsBoolean(ServerContext *context, const RRef *request, BoolValue *response) { + executeOnMainThread([&] { + try { + response->set_value(asBool(dereference(*request))); + } catch (RExceptionBase const &) { + response->set_value(false); + } + }, context, true); + return Status::OK; +} + + +Status RPIServiceImpl::getDistinctStrings(ServerContext *context, const RRef *request, StringList *response) { + executeOnMainThread([&] { + ShieldSEXP object = dereference(*request); + if (object.type() != STRSXP && !Rf_inherits(object, "factor")) { + return; + } + ShieldSEXP vector = RI->asCharacter(RI->unique(object)); + int sumLength = 0; + for (int i = 0; i < vector.length(); ++i) { + if (!vector.isNA(i)) { + std::string s = stringEltUTF8(vector, i); + sumLength += s.size(); + if (sumLength > EVALUATE_AS_TEXT_MAX_LENGTH) break; + response->add_list(s); + } + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::loadObjectNames(ServerContext *context, const RRef *request, StringList *response) { + executeOnMainThread([&] { + ShieldSEXP names = RI->ls(dereference(*request), named("all.names", true)); + if (names.type() != STRSXP) return; + for (int i = 0; i < names.length(); ++i) { + response->add_list(stringEltUTF8(names, i)); + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::findInheritorNamedArguments(ServerContext *context, const RRef *request, StringList *response) { + executeOnMainThread([&] { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("findInheritorNamedArguments"); + ShieldSEXP result = func(dereference(*request)); + if (TYPEOF(result) != STRSXP) return; + for (int i = 0; i < result.length(); ++i) { + response->add_list(stringEltUTF8(result, i)); + } + }, context, true); + return Status::OK; +} + +Status +RPIServiceImpl::findExtraNamedArguments(ServerContext *context, const RRef *request, ExtraNamedArguments *response) { + executeOnMainThread([&] { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("findExtraNamedArgs"); + ShieldSEXP result = func(dereference(*request), named("depth", 2)); + if (TYPEOF(result) != VECSXP) return; + for (int i = 0; i < result.length(); ++i) { + ShieldSEXP elem = VECTOR_ELT(result, i); + ShieldSEXP name = VECTOR_ELT(elem, 0); + if (asBool(VECTOR_ELT(elem, 1))) { + response->add_funargnames(stringEltUTF8(name, 0)); + } else { + response->add_argnames(stringEltUTF8(name, 0)); + } + } + }, context, true); + return Status::OK; +} + +Status +RPIServiceImpl::getLoadedShortS4ClassInfos(ServerContext *context, const Empty *, ShortS4ClassInfoList *response) { + executeOnMainThread([&] { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("getLoadedS4ClassInfos"); + ShieldSEXP result = func(); + if (TYPEOF(result) != VECSXP) return; + + for (int i = 0; i < result.length(); ++i) { + ShieldSEXP classRep = VECTOR_ELT(result, i); + if (TYPEOF(classRep) != S4SXP) continue; + + ShortS4ClassInfoList_ShortS4ClassInfo *info = response->add_shorts4classinfos(); + info->set_name(stringEltUTF8(R_do_slot(classRep, toSEXP("className")), 0)); + info->set_package(stringEltUTF8(R_do_slot(classRep, toSEXP("package")), 0)); + info->set_isvirtual(asBool(R_do_slot(classRep, toSEXP("virtual")))); + } + }, context, true); + return Status::OK; +} + +void getS4ClassInfo(const ShieldSEXP &classDef, S4ClassInfo *response) { + if (TYPEOF(classDef) != S4SXP) return; + response->set_classname(stringEltUTF8(R_do_slot(classDef, toSEXP("className")), 0)); + response->set_packagename(stringEltUTF8(R_do_slot(classDef, toSEXP("package")), 0)); + ShieldSEXP slotsList = R_do_slot(classDef, toSEXP("slots")); + ShieldSEXP slotsNames = Rf_getAttrib(slotsList, R_NamesSymbol); + for (int i = 0; i < slotsNames.length(); ++i) { + auto next_slot = response->add_slots(); + next_slot->set_name(stringEltUTF8(slotsNames, i)); + next_slot->set_type(stringEltUTF8(VECTOR_ELT(slotsList, i), 0)); + } + ShieldSEXP containsList = R_do_slot(classDef, toSEXP("contains")); + for (int i = 0; i < containsList.length(); ++i) { + ShieldSEXP superClass = VECTOR_ELT(containsList, i); + response->add_superclasses(stringEltUTF8(R_do_slot(classDef, toSEXP("superClass")), 0)); + } + + response->set_isvirtual(asBool(R_do_slot(classDef, toSEXP("virtual")))); +} + +Status RPIServiceImpl::getS4ClassInfoByObjectName(ServerContext *context, const RRef *request, S4ClassInfo *response) { + executeOnMainThread([&] { + ShieldSEXP obj = dereference(*request); + if (TYPEOF(obj) != S4SXP) return; + ShieldSEXP className = Rf_getAttrib(obj, R_ClassSymbol); + getS4ClassInfo(R_getClassDef_R(className), response); + }, context, true); + return Status::OK; +} + +Status +RPIServiceImpl::getS4ClassInfoByClassName(ServerContext *context, const StringValue *request, S4ClassInfo *response) { + executeOnMainThread([&] { + getS4ClassInfo(R_getClassDef_R(toSEXP(request->value())), response); + }, context, true); + return Status::OK; +} + +bool isObjectFromR6(const ShieldSEXP &object) { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("isObjectFromR6"); + return func(object); +} + +SEXPREC* getR6ClassName(const ShieldSEXP &object) { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("getR6ClassName"); + return func(object); +} + +SEXPREC* getR6ClassInheritanceTree(const ShieldSEXP &object) { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("getR6ClassInheritanceTree"); + return func(object); +} + +SEXPREC* getR6ClassDefMembers(const ShieldSEXP &object) { + ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); + ShieldSEXP func = jetbrainsEnv.getVar("getR6ClassDefMembers"); + return func(object); +} + +void getR6ClassInfo(const ShieldSEXP &classDef, R6ClassInfo *response) { + if (!isObjectFromR6(classDef)) return; + + auto className = getR6ClassName(classDef); + response->set_classname(stringEltUTF8(className, 0)); + + auto classInheritanceNames = getR6ClassInheritanceTree(classDef); + for (int i = 1; i < XLENGTH(classInheritanceNames); ++i) { + response->add_superclasses(stringEltUTF8(classInheritanceNames, i)); + } + + auto classMembers = getR6ClassDefMembers(classDef); + for (int i = 0; i < XLENGTH(classMembers); ++i) { + auto next_member = response->add_members(); + next_member->set_name(stringEltUTF8(classMembers, i)); + next_member->set_ispublic(true); + } +} + +Status RPIServiceImpl::getR6ClassInfoByObjectName(ServerContext *context, const RRef *request, R6ClassInfo *response) { + executeOnMainThread([&] { + ShieldSEXP obj = dereference(*request); + bool isR6 = isObjectFromR6(obj); + if (!isR6) return; + getR6ClassInfo(obj, response); + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::getTableColumnsInfo(ServerContext *context, const TableColumnsInfoRequest *request, + TableColumnsInfo *response) { + executeOnMainThread([&] { + ShieldSEXP table = dereference(request->ref()); + if (!isDataFrame(table)) return; + response->set_tabletype( + Rf_inherits(table, "tbl_df") ? TableColumnsInfo_TableType_DPLYR : + Rf_inherits(table, "data.table") ? TableColumnsInfo_TableType_DATA_TABLE : + Rf_inherits(table, "data.frame") ? TableColumnsInfo_TableType_DATA_FRAME : + TableColumnsInfo_TableType_UNKNOWN); + + ShieldSEXP names = RI->names(table); + if (TYPEOF(names) != STRSXP) return; + int ncol = asInt(RI->ncol(table)); + for (int i = 0; i < ncol; ++i) { + TableColumnsInfo::Column *column = response->add_columns(); + column->set_name(stringEltUTF8(names, i)); + column->set_type(asStringUTF8(RI->paste(RI->classes(table[i]), named("collapse", ",")))); + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::getFormalArguments(ServerContext *context, const RRef *request, StringList *response) { + executeOnMainThread([&] { + ShieldSEXP names = RI->names(RI->formals(dereference(*request))); + if (TYPEOF(names) != STRSXP) return; + for (int i = 0; i < names.length(); ++i) { + response->add_list(stringEltUTF8(names, i)); + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::getRMarkdownChunkOptions(ServerContext *context, const Empty *, StringList *response) { + executeOnMainThread([&] { + ShieldSEXP options = RI->evalCode("names(knitr::opts_chunk$get())", R_BaseEnv); + if (TYPEOF(options) != STRSXP) return; + for (int i = 0; i < options.length(); ++i) { + response->add_list(stringEltUTF8(options, i)); + } + }, context, true); + return Status::OK; +} + +Status RPIServiceImpl::getEqualityObject(ServerContext *context, const RRef *request, Int64Value *response) { + executeOnMainThread([&] { + try { + response->set_value((long long) (SEXP) dereference(*request)); + } catch (RExceptionBase const &) { + response->set_value(0); + } + }, context, true); + return Status::OK; +} + +void RPIServiceImpl::setValueImpl(RRef const &ref, SEXP value) { + SHIELD(value); + switch (ref.ref_case()) { + case RRef::kMember: { + ShieldSEXP env = dereference(ref.member().env()); + RI->assign(ref.member().name(), value, named("envir", env)); + break; + } + case RRef::kListElement: { + ShieldSEXP list = dereference(ref.listelement().list()); + ShieldSEXP newList = RI->doubleSubscriptAssign(list, ref.listelement().index() + 1, value); + setValueImpl(ref.listelement().list(), newList); + break; + } + case RRef::kAttributes: { + ShieldSEXP obj = dereference(ref.attributes()); + ShieldSEXP newObj = RI->attributesAssign(obj, value); + setValueImpl(ref.attributes(), newObj); + break; + } + default: { + throw std::invalid_argument("Invalid reference for setValue"); + } + } +} + +Status RPIServiceImpl::setValue(ServerContext *context, const SetValueRequest *request, ValueInfo *response) { + executeOnMainThread([&] { + try { + ShieldSEXP value = dereference(request->value()); + setValueImpl(request->ref(), value); + getValueInfo(value, response); + } catch (RExceptionBase const &e) { + response->mutable_error()->set_text(e.what()); + } catch (...) { + response->mutable_error()->set_text("Error"); + throw; + } + }, context, true); + return Status::OK; +} diff --git a/temporary/RKernel/init.R b/temporary/RKernel/init.R new file mode 100644 index 000000000..87735dc5b --- /dev/null +++ b/temporary/RKernel/init.R @@ -0,0 +1,504 @@ +# Rkernel is an execution kernel for R interpreter +# Copyright (C) 2019 JetBrains s.r.o. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +if (".jetbrains" %in% ls(globalenv(), all.names = TRUE)) { + rm(".jetbrains", envir = globalenv()) +} + +# Set hook to record previous plot before new vanilla graphics are created +setHook(hookName = "before.plot.new", + value = function() { + .Call(".jetbrains_ther_device_record", FALSE) + }, + action = "append") + +# Set hook to record previous plot before new ggplot2 graphics are created +setHook(hookName = "before.grid.newpage", + value = function() { + .Call(".jetbrains_ther_device_record", TRUE) # Pass TRUE to indicate it was triggered by ggplot2 + }, + action = "append") + +# Force interpreter to restart custom graphics device +options(device = function() { + .Call(".jetbrains_ther_device_restart") +}) + +.jetbrains$defaultPlatformGUI <<- .Platform$GUI + +# Some packages might be not available as binaries. +# The default behaviour of interpreter in such a case +# is to ask user whether he wants to install it from source instead. +# This is not desirable that's why interpreter will be forced to install packages from source +# **when necessary** without user permission +options(install.packages.compile.from.source = "always") + +.jetbrains$init <<- function(rsession.path, project.dir) { + current.wd <- getwd() + tryCatch({ + tools.path <- file.path(rsession.path, "Tools.R") + options.path <- file.path(rsession.path, "Options.R") + api.path <- file.path(rsession.path, "Api.R") + modules.path <- file.path(rsession.path, "modules") + sessionJobs.path <- file.path(rsession.path, "modules/SessionJobs.R") + setwd(modules.path) + source(tools.path, local = TRUE) + source(sessionJobs.path, local = TRUE) + source(api.path, local = TRUE) + source(options.path, local = TRUE) + sapply(Filter(function(s) s != "SessionCompileAttributes.R" & s != "SessionPlots.R", + list.files(modules.path, pattern = ".*\\.r$", ignore.case = TRUE)), + function(x) { source(file.path(modules.path, x), local = TRUE) } ) + .rs.getProjectDirectory <<- function() project.dir + options(BuildTools.Check = NULL) + }, finally = { + setwd(current.wd) + }) +} + +.jetbrains$setRStudioAPIEnabled <<- function(isEnabled) { + unlockBinding(".Platform", baseenv()) + if (isEnabled) { + .Platform$GUI <<- "RStudio" + } else { + .Platform$GUI <<- .jetbrains$defaultPlatformGUI + } + lockBinding(".Platform", baseenv()) +} + +.jetbrains$updatePackageEvents <<- function() { + packageNames <- base::list.dirs(.libPaths(), full.names = FALSE, recursive = FALSE) + sapply(packageNames, function(name) { + if (!(name %in% .rs.jbHookedPackages)) { + loadEventName = packageEvent(name, "onLoad") + onPackageLoaded <- function(name, ...) { + .rs.reattachS3Overrides() + } + setHook(loadEventName, onPackageLoaded, action = "append") + .rs.setVar("jbHookedPackages", append(.rs.jbHookedPackages, name)) + } + }) +} + +.jetbrains$toSystemIndependentPath <<- function(path) { + gsub("\\\\", "/", path) +} + +.jetbrains$createTempDirectory <<- function(suffix) { + pattern <- paste0("jetbrains_", suffix) + path <- tempfile(pattern = pattern) + dir.create(path) + .jetbrains$toSystemIndependentPath(path) +} + +# Fallback values +.jetbrains$chunkOutputDir <<- "." +.jetbrains$externalImageDir <<- "." +.jetbrains$externalImageCounter <<- 0 + +# Note: used in "NotebookHtmlWidgets.R" of RSession +.jetbrains$getNextExternalImagePath <<- function(path) { + snapshot.count <- .Call(".jetbrains_ther_device_snapshot_count") + if (is.null(snapshot.count)) { + snapshot.count <- 0 + } + .jetbrains$externalImageCounter <<- .jetbrains$externalImageCounter + 1 # Also ensure it's > 0 + base.name <- paste0("image_", snapshot.count - 1, "_", .jetbrains$externalImageCounter, ".", tools::file_ext(path)) + file.path(.jetbrains$externalImageDir, base.name) +} + +.jetbrains$runBeforeChunk <<- function(report.text, chunk.text) { + .rs.evaluateRmdParams(report.text) + opts <- .rs.evaluateChunkOptions(chunk.text) + output.dir <- .jetbrains$createTempDirectory("chunk_outputs") + .jetbrains$chunkOutputDir <<- output.dir + data.dir <- file.path(output.dir, "data") + html.lib.dir <- file.path(output.dir, "lib") + image.dir <- file.path(output.dir, "images") + external.image.dir <- file.path(output.dir, "external-images") + dir.create(image.dir, showWarnings = FALSE) + dir.create(external.image.dir, showWarnings = FALSE) + dir.create(html.lib.dir, showWarnings = FALSE) + dir.create(data.dir, showWarnings = FALSE) + + .jetbrains$externalImageDir <<- external.image.dir + .jetbrains$externalImageCounter <<- 0 + .rs.initHtmlCapture(output.dir, html.lib.dir, opts) + .rs.initDataCapture(data.dir, opts) + if (!.rs.hasVar("jbHookedPackages")) { + .rs.setVar("jbHookedPackages", character()) + } + .jetbrains$updatePackageEvents() # Note: supposed to be useless when packages are getting installed within chunk but for my machine it's OK +} + +.jetbrains$runAfterChunk <<- function() { + .rs.releaseHtmlCapture() + .rs.releaseDataCapture() + unlink(.jetbrains$chunkOutputDir, recursive = TRUE) +} + +.jetbrains$getChunkOutputPaths <<- function() { + relative.paths <- list.files(.jetbrains$chunkOutputDir, recursive = TRUE, include.dirs = FALSE, full.names = FALSE) + c(.jetbrains$chunkOutputDir, relative.paths) +} + +.jetbrains$getChunkOutputFullPath <<- function(relative.path) { + file.path(.jetbrains$chunkOutputDir, relative.path) +} + +.jetbrains$findInheritorNamedArguments <<- function(x) { + ignoreErrors <- function(expr) { + as.list(tryCatch(expr, error = function(e) { })) + } + + defenv <- if (!is.na(w <- .knownS3Generics[x])) { + asNamespace(w) + } else { + genfun <- get(x, mode = "function") + if (.isMethodsDispatchOn() && methods::is(genfun, "genericFunction")) { + genfun <- methods::finalDefaultMethod(genfun@default) + } + if (typeof(genfun) == "closure") environment(genfun) + else .BaseNamespaceEnv + } + s3_table <- get(".__S3MethodsTable__.", envir = defenv) + + getS3Names <- function(row) { + functionName <- row[["functionName"]] + from <- row[["from"]] + envir <- tryCatch(as.environment(from), error = function(e) NULL) + if (startsWith(from, "registered S3method")) { + envir <- s3_table + } + if (is.null(envir)) { + envir <- tryCatch(as.environment(paste0("package:", from)), error = function(e) .GlobalEnv) + } + names(formals(get(functionName, mode = "function", envir = envir))) + } + + s4 <- ignoreErrors(names(formals(getMethod(x)))) + if (utils:::findGeneric(x, parent.frame(), warnS4only = FALSE) == "") { + s3 <- NULL + } + else { + s3Info <- attr(.S3methods(x), "info") + s3Info[, "functionName"] <- rownames(s3Info) + s3 <- ignoreErrors(apply(s3Info[, c("functionName", "from")], 1, getS3Names)) + } + unique(unlist(c(s4, s3, names(formals(x))))) +} + +.jetbrains$printAllPackagesToFile <<- function(repo.urls, output.path) { + remove.newlines <- function(s) { + gsub("\r?\n|\r", " ", s) + } + + old.repos <- getOption("repos") + options(repos = repo.urls) + sink(output.path) + p <- available.packages()[, c("Package", "Repository", "Version", "Depends")] + p <- as.data.frame(p) + p$Depends <- sapply(p$Depends, remove.newlines) + with(p, cat(paste(paste(Package, Repository, Version, sep = " "), Depends, sep = "\t"), sep = "\n")) + sink() + options(repos = old.repos) +} + +.jetbrains$getDefaultRepositories <<- function() { + p <- file.path(Sys.getenv("HOME"), ".R", "repositories") + if (!file.exists(p)) + p <- file.path(R.home("etc"), "repositories") + a <- tools:::.read_repositories(p) + a[, "URL"] +} + +.jetbrains$printCranMirrorsToFile <<- function(output.path) { + sink(output.path) + mirrors <- getCRANmirrors()[, c('Name', 'URL')] + with(mirrors, cat(paste(Name, URL, sep = "\t"), sep = "\n")) + sink() +} + +.jetbrains$printInstalledPackagesToFile <<- function(output.path) { + sink(output.path) + versions <- as.data.frame(installed.packages()[, c("Package", "Version", "Priority", "LibPath")]) + with(versions, cat(paste(LibPath, Package, Version, Priority, sep = "\t"), sep = "\n")) + sink() +} + +.jetbrains$initGraphicsDevice <<- function(width, height, resolution, in.memory) { + path <- .jetbrains$createSnapshotGroup() + .Call(".jetbrains_ther_device_init", path, width, height, resolution, in.memory) + path +} + +.jetbrains$findStoredSnapshot <<- function(directory, number) { + pattern <- paste0("^snapshot_normal_", number, "_") # Note: trailing underscore will cut off remaining digits if any + snapshots <- list.files(directory, pattern = pattern, full.names = FALSE) + if (length(snapshots) > 0) { + return(snapshots[1]) + } else { + return(NULL) + } +} + +.jetbrains$createSnapshotGroup <<- function() { + .jetbrains$createTempDirectory("snapshot_group") +} + +.jetbrains$shutdownGraphicsDevice <<- function() { + path <- .Call(".jetbrains_ther_device_shutdown") + if (!is.null(path)) { + unlink(path, recursive = TRUE) + } + NULL +} + +.jetbrains$saveRecordedPlotToFile <<- function(snapshot, output.path) { + .jetbrains.recorded.snapshot <- snapshot + save(.jetbrains.recorded.snapshot, file = output.path) +} + +.jetbrains$replayPlotFromFile <<- function(input.path) { + load(input.path) + plot <- .jetbrains.recorded.snapshot + + # restore native symbols for R >= 3.0 + rVersion <- getRversion() + if (rVersion >= "3.0") { + for (i in 1:length(plot[[1]])) { + # get the symbol then test if it's a native symbol + symbol <- plot[[1]][[i]][[2]][[1]] + if ("NativeSymbolInfo" %in% class(symbol)) { + # determine the dll that the symbol lives in + name = if (!is.null(symbol$package)) symbol$package[["name"]] else symbol$dll[["name"]] + pkgDLL <- getLoadedDLLs()[[name]] + + # reconstruct the native symbol and assign it into the plot + nativeSymbol <- getNativeSymbolInfo(name = symbol$name, PACKAGE = pkgDLL, withRegistrationInfo = TRUE); + plot[[1]][[i]][[2]][[1]] <- nativeSymbol; + } + } + } + + # Replay obtained plot + suppressWarnings(grDevices::replayPlot(plot, reloadPkgs=TRUE)) +} + +.jetbrains$dropRecordedSnapshots <<- function(device.number, from, to) { + for (i in from:to) { + name <- paste0("recordedSnapshot_", device.number, "_", i) + if (exists(name, envir = .jetbrains)) { + assign(name, NULL, .jetbrains) + } + } +} + +.jetbrains$getLoadedS4ClassInfos <<- function() { + classTable <- methods:::.classTable + infos <- lapply(names(classTable), function(className) { + class <- classTable[[className]] + if (inherits(class, "classRepresentation")) class + }) + Filter(function(x) !is.null(x), infos) +} + +.jetbrains$isObjectFromR6 <<- function(object) { + return(is.R6(object)) +} + +.jetbrains$getR6ClassName <<- function(x) { + return(class(x)[1]) +} + +.jetbrains$getR6ClassInheritanceTree <<- function(x) { + return(head(class(x), -1)[-1]) +} + +.jetbrains$getR6ClassDefMembers <<- function(x) { + return(names(x)[-1]) +} + +.jetbrains$getSysEnv <<- function(env_name, flags) { + s <- Sys.getenv(env_name) + s <- strsplit(s, .Platform$path.sep)[[1]] + if ("--normalize-path" %in% flags) { + s <- sapply(s, function (p) { + normalized <- normalizePath(p) + .jetbrains$toSystemIndependentPath(normalized) + }) + } + s +} + +.jetbrains$loadLibraryPath <<- function() { + res <- NULL + for (path in .libPaths()) { + res <- c(res, list(path, file.access(path, 2) == 0)) + } + res +} + +.jetbrains$loadInstalledPackages <<- function() { + versions <- as.data.frame(installed.packages()[, c("Package", "Version", "Priority", "LibPath")]) + canonicalPackagePaths <- data.frame(CanonicalPath = apply(versions, 1, function(row) { + normalizePath(file.path(row["LibPath"], row["Package"])) + })) + description <- data.frame("Title" = I(lapply(versions[, "Package"], function(x) packageDescription(x, fields = "Title"))), + "URL" = I(lapply(versions[, "Package"], function(x) packageDescription(x, fields = "URL")))) + cbind(versions, canonicalPackagePaths, description) +} + +.jetbrains$unloadLibrary <<- function(package.name, with.dynamic.library) { + resource.name <- paste0("package:", package.name) + detach(resource.name, unload = TRUE, character.only = TRUE) + if (with.dynamic.library) { + .jetbrains$unloadDynamicLibrary(package.name) + } +} + +.jetbrains$unloadDynamicLibrary <<- function(package.name) { + if (.jetbrains$isDynamicLibraryLoaded(package.name)) { + pd.file <- attr(packageDescription(package.name), "file") + lib.path <- sub("/Meta.*", "", pd.file) + library.dynam.unload(package.name, libpath = lib.path) + } +} + +.jetbrains$isDynamicLibraryLoaded <<- function(package.name) { + for (lib in .dynLibs()) { + name <- lib[[1]] + if (name == package.name) { + return(TRUE) + } + } + FALSE +} + +.jetbrains$previewDataImportResult <<- NULL + +.jetbrains$previewDataImport <<- function(path, mode, row.count, importOptions) { + result <- if (mode != "base") { + .jetbrains$previewAdvancedDataImport(path, mode, row.count, importOptions) + } else { + .jetbrains$previewBaseDataImport(path, row.count, importOptions) + } + .jetbrains$previewDataImportResult <<- result + if (is.null(result)) { + return(NULL) + } + result$parsingErrors +} + +.jetbrains$commitDataImport <<- function(path, mode, importOptions) { + .jetbrains$previewDataImport(path, mode, NULL, importOptions) + result <- .jetbrains$previewDataImportResult + .jetbrains$previewDataImportResult <<- NULL + if (is.null(result)) { + return(NULL) + } + result$data +} + +.jetbrains$previewAdvancedDataImport <<- function(path, mode, row.count, importOptions) { + importOptions$openDataViewer <- FALSE + importOptions$importLocation <- path + importOptions$modelLocation <- NULL + importOptions$mode <- mode + if (!is.null(row.count)) { + importOptions$maxRows <- row.count + + # Excel's special + if (is.null(importOptions$nMax) || importOptions$nMax > row.count) { + importOptions$nMax <- row.count + } + } + .rs.previewDataImport(importOptions) +} + +.jetbrains$previewBaseDataImport <<- function(path, row.count, importOptions) { + if (!is.null(row.count)) { + importOptions$nrows <- row.count + } + importOptions$file <- path + tryCatch({ + # try to use read.csv directly if possible (since this is a common case + # and since LibreOffice spreadsheet exports produce files unparsable + # by read.table). check Workspace.makeCommand if we want to deduce + # other more concrete read calls. + data <- if (identical(importOptions$sep, ",") && identical(importOptions$dec, ".") && identical(importOptions$quote, "\"")) { + importOptions$sep <- NULL + importOptions$dec <- NULL + importOptions$quote <- NULL + do.call(read.csv, importOptions) + } else { + do.call(read.table, importOptions) + } + return(list(data = data, parsingErrors = 0)) + }, error=function(e) { + data <- data.frame(Error = e$message) + parsingErrors <- if (!is.null(row.count)) row.count else 0 + return(list(data = data, parsingErrors = parsingErrors)) + }) +} + +.jetbrains$convertRoxygenToHTML <<- function(functionName, text) { + text <- format( + roxygen2:::roclet_process.roclet_rd(, roxygen2:::parse_text(text), base_path = ".")[[paste0(functionName, ".Rd")]]) + links = gsub("^\\.\\./\\.\\./", "/library/", tools::findHTMLlinks()) + text <- utils::capture.output(tools::Rd2HTML(textConnection(text), Links = links)) + return(paste(text, collapse = "\n")) +} + +local({ + handlersEnv <- tools:::.httpd.handlers.env + handlersEnv$jb_get_file <- function(path, query, ...) { + prefix <- "/custom/jb_get_file/" + file <- substr(path, nchar(prefix) + 1, nchar(path)) + if (!file.exists(file)) { + return(list(payload = paste0("No such file ", file))) + } + mimeType <- function(path) { + ext <- strsplit(path, ".", fixed = TRUE)[[1]] + if (n <- length(ext)) ext <- ext[n] else "" + switch(ext, css = "text/css", gif = "image/gif", jpg = "image/jpeg", png = "image/png", svg = "image/svg+xml", + html = "text/html", pdf = "application/pdf", eps = , ps = "application/postscript", + sgml = "text/sgml", xml = "text/xml", "text/plain") + } + return(list(file = file, `content-type` = mimeType(path))) + } +}) + +local({ + env <- as.environment("package:utils") + unlockBinding("View", env) + env$View <- function(x, title = paste(deparse(substitute(x)), collapse = " ")) + invisible(.Call(".jetbrains_View", substitute(x), parent.frame(), title)) + lockBinding("View", env) +}) + +if (.Platform$OS.type == "unix" && !("UTF-8" %in% localeToCharset(Sys.getlocale("LC_CTYPE")))) { + if (grepl("^darwin", R.version$os)) { + Sys.setlocale("LC_CTYPE", "UTF-8") + } else { + Sys.setlocale("LC_CTYPE", "C.UTF-8") + } +} + +options(warn = 1) +options(demo.ask = TRUE); +assign(".Last.sys", function() .Call(".jetbrains_quitRWrapper"), envir = baseenv()) From 64496aca218ad4afa485bee4a1118c977311feec Mon Sep 17 00:00:00 2001 From: deaglegross Date: Tue, 20 Apr 2021 02:01:31 +0300 Subject: [PATCH 45/52] library summary upd --- grammars/library_summary.proto | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/grammars/library_summary.proto b/grammars/library_summary.proto index a1fe4ded3..90a370167 100644 --- a/grammars/library_summary.proto +++ b/grammars/library_summary.proto @@ -38,22 +38,18 @@ message RLibrarySymbol { } message R6ClassRepresentation { - message R6ClassField { + message R6ClassMember { string name = 1; - bool isPublic = 3; - } - message R6ClassMethod { - string name = 1; - bool isPublic = 3; + bool isPublic = 2; } + message R6ClassActiveBinding { string name = 1; } - string packageName = 1; + repeated string superClasses = 2; - repeated R6ClassField fields = 3; - repeated R6ClassField methods = 4; - repeated R6ClassField activeBindings = 5; + repeated R6ClassMember members = 3; + repeated R6ClassActiveBinding activeBindings = 4; } string name = 1; From cd7f6932fffc9deb16a259130707432056d26d2f Mon Sep 17 00:00:00 2001 From: deaglegross Date: Wed, 5 May 2021 01:30:55 +0300 Subject: [PATCH 46/52] change .proto definition to separate fields msg and methods msg --- src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 29 ++++++-- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 73 +++++++++++++++---- .../r/editor/RCompletionContributor.kt | 16 ++-- src/org/jetbrains/r/rinterop/RInterop.kt | 12 ++- .../r/skeleton/RSkeletonFileStubBuilder.kt | 3 +- .../r/classes/r6/R6ClassInfoUtilTests.kt | 42 +++++++++-- 6 files changed, 134 insertions(+), 41 deletions(-) diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt index 4016c413d..2906bc41f 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -12,16 +12,18 @@ import com.intellij.util.io.StringRef // no need to care about overloads because R6 lib doesn't support it: // "All items in public, private, and active must have unique names." interface IR6ClassMember { val name: String } -data class R6ClassMember(override val name: String, val isPublic: Boolean = true) : IR6ClassMember +data class R6ClassField(override val name: String, val isPublic: Boolean = true) : IR6ClassMember +data class R6ClassMethod(override val name: String, val isPublic: Boolean = true) : IR6ClassMember data class R6ClassActiveBinding(override val name: String) : IR6ClassMember data class R6ClassInfo(val className: String, val superClasses: List, - val members: List, + val fields: List, + val methods: List, val activeBindings: List) { fun containsMember(memberName: String) : Boolean { - return (members.map { it.name }.contains(memberName) || + return ((fields + methods).map { it.name }.contains(memberName) || activeBindings.map { it.name }.contains(memberName)) } @@ -29,7 +31,12 @@ data class R6ClassInfo(val className: String, dataStream.writeName(className) DataInputOutputUtilRt.writeSeq(dataStream, superClasses) { dataStream.writeName(it) } - DataInputOutputUtilRt.writeSeq(dataStream, members) { + DataInputOutputUtilRt.writeSeq(dataStream, fields) { + dataStream.writeName(it.name); + dataStream.writeBoolean(it.isPublic) + } + + DataInputOutputUtilRt.writeSeq(dataStream, methods) { dataStream.writeName(it.name); dataStream.writeBoolean(it.isPublic) } @@ -44,10 +51,16 @@ data class R6ClassInfo(val className: String, val className = StringRef.toString(dataStream.readName()) val superClasses = DataInputOutputUtilRt.readSeq(dataStream) { StringRef.toString(dataStream.readName()) } - val members = DataInputOutputUtilRt.readSeq(dataStream) { + val fields = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + val isPublic = dataStream.readBoolean() + R6ClassField(name, isPublic) + } + + val methods = DataInputOutputUtilRt.readSeq(dataStream) { val name = StringRef.toString(dataStream.readName()) val isPublic = dataStream.readBoolean() - R6ClassMember(name, isPublic) + R6ClassMethod(name, isPublic) } val activeBindings = DataInputOutputUtilRt.readSeq(dataStream) { @@ -55,7 +68,7 @@ data class R6ClassInfo(val className: String, R6ClassActiveBinding(name) } - return R6ClassInfo(className, superClasses, members, activeBindings) + return R6ClassInfo(className, superClasses, fields, methods, activeBindings) } } } @@ -63,7 +76,7 @@ data class R6ClassInfo(val className: String, class R6ClassKeywordsProvider { companion object { val predefinedClassMethods = listOf( - R6ClassMember("clone", true) + R6ClassMethod("clone", true) ) val visibilityModifiers = listOf( diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 145a64f0d..84ddd4f53 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -8,7 +8,6 @@ import com.intellij.openapi.util.Key import com.intellij.psi.util.CachedValuesManager import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil -import org.jetbrains.r.packages.RPackageProjectManager import org.jetbrains.r.psi.RElementFactory import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl @@ -117,8 +116,8 @@ object R6ClassInfoUtil { val project = callExpression.project if (allSuperClasses != null) { - return (r6ClassInfo.members + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.members + it.activeBindings } + return (r6ClassInfo.fields + r6ClassInfo.methods + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> + LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.fields + it.methods + it.activeBindings } }).distinctBy { it.name } } @@ -127,18 +126,51 @@ object R6ClassInfoUtil { fun getAssociatedMembers(callExpression: RCallExpression, argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), - onlyPublic: Boolean = false): List? { + onlyPublic: Boolean = false): List? { argumentInfo ?: return null if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null - val r6ClassMethods = mutableListOf() + val r6ClassFields = getAssociatedFields(callExpression, argumentInfo, onlyPublic) + val r6ClassMethods = getAssociatedMethods(callExpression, argumentInfo, onlyPublic) + + val r6ClassMembers = mutableListOf() + if (r6ClassFields != null) r6ClassMembers.addAll(r6ClassFields) + if (r6ClassMethods != null) r6ClassMembers.addAll(r6ClassMethods) + + return r6ClassMembers + } + + fun getAssociatedFields(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + + val r6ClassFields = mutableListOf() val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!publicContents.isNullOrEmpty()) getMembersFromExpressionList(r6ClassMethods, publicContents, true) + if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) if (!onlyPublic) { - val privateContents = (argumentInfo.getArgumentPassedToParameter( - argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList - if (!privateContents.isNullOrEmpty()) getMembersFromExpressionList(r6ClassMethods, privateContents, false) + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) + } + + return r6ClassFields + } + + fun getAssociatedMethods(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + + val r6ClassMethods = mutableListOf() + val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) + + if (!onlyPublic) { + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) } return r6ClassMethods @@ -170,18 +202,29 @@ object R6ClassInfoUtil { val argumentInfo = RParameterInfoUtil.getArgumentInfo(callExpression, definition) ?: return null val className = getAssociatedClassNameFromR6ClassCall(callExpression, argumentInfo) ?: return null val superClassesHierarchy = getAssociatedSuperClassesHierarchy(callExpression, argumentInfo) ?: emptyList() - val members = getAssociatedMembers(callExpression, argumentInfo) ?: emptyList() + val fields = getAssociatedFields(callExpression, argumentInfo) ?: emptyList() + val methods = getAssociatedMethods(callExpression, argumentInfo) ?: emptyList() val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) ?: emptyList() - return R6ClassInfo(className, superClassesHierarchy, members, activeBindings) + return R6ClassInfo(className, superClassesHierarchy, fields, methods, activeBindings) } - private fun getMembersFromExpressionList(r6ClassFields: MutableList, + private fun getFieldsFromExpressionList(r6ClassFields: MutableList, callExpressions: List, - isFromPublicScope: Boolean) { + isPublicScope: Boolean) { callExpressions.forEach { - if (it.lastChild != null && !it.name.isNullOrEmpty()) { - r6ClassFields.add(R6ClassMember(it.name!!, isFromPublicScope)) + if (it.lastChild !is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassFields.add(R6ClassField(it.name!!, isPublicScope)) + } + } + } + + private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, + callExpressions: List, + isPublicScope: Boolean) { + callExpressions.forEach { + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassMethods.add(R6ClassMethod(it.name!!, isPublicScope)) } } } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 4950f3faf..dc0743f41 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -20,9 +20,7 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.r.RLanguage -import org.jetbrains.r.classes.r6.R6ClassInfoUtil -import org.jetbrains.r.classes.r6.IR6ClassMember -import org.jetbrains.r.classes.r6.R6ClassKeywordsProvider +import org.jetbrains.r.classes.r6.* import org.jetbrains.r.classes.r6.context.* import org.jetbrains.r.classes.s4.* import org.jetbrains.r.classes.s4.context.* @@ -142,8 +140,8 @@ class RCompletionContributor : CompletionContributor() { } val text = obj.text - runtimeInfo.loadR6ClassInfoByObjectName(text)?.let { info -> - return addMembersCompletion(info.members + info.activeBindings, shownNames, result) + runtimeInfo.loadR6ClassInfoByObjectName(text)?.let { classInfo -> + return addMembersCompletion(classInfo.fields + classInfo.methods + classInfo.activeBindings, shownNames, result) } return false } @@ -170,7 +168,13 @@ class RCompletionContributor : CompletionContributor() { for (r6Member in r6ClassMembers) { if (r6Member.name in shownNames) continue - result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + + when (r6Member){ + // TODO fix specific rCompletionElementFactory + is R6ClassField -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + is R6ClassMethod -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + } + shownNames.add(r6Member.name) hasNewResults = true } diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index b6f36125c..2f18a2422 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -879,7 +879,7 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getLoadedShortR6ClassInfos(): List? { return try { executeWithCheckCancel(asyncStub::getLoadedShortR6ClassInfos, Empty.getDefaultInstance()).shortR6ClassInfosList.map { - R6ClassInfo(it.name, emptyList(), emptyList(), emptyList()) + R6ClassInfo(it.name, emptyList(), emptyList(), emptyList(), emptyList()) } } catch (e: RInteropTerminated) { null @@ -889,7 +889,10 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByObjectName(ref: RReference): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) - R6ClassInfo(res.className, res.superClassesList, res.membersList.map { R6ClassMember(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + R6ClassInfo(res.className, res.superClassesList, + res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, + res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) } catch (e: RInteropTerminated) { null } @@ -898,7 +901,10 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler fun getR6ClassInfoByClassName(className: String): R6ClassInfo? { return try { val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByClassName, StringValue.of(className)) - R6ClassInfo(res.className, res.superClassesList, res.membersList.map { R6ClassMember(it.name, it.isPublic) }, res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + R6ClassInfo(res.className, res.superClassesList, + res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, + res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) } catch (e: RInteropTerminated) { null } diff --git a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt index 1c5315041..f02d32581 100644 --- a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt +++ b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt @@ -52,7 +52,8 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { null, R6ClassInfo(symbol.name, r6ClassRepresentation.superClassesList, - r6ClassRepresentation.membersList.map { R6ClassMember(it.name, it.isPublic) }, + r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) } diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt index f97edddd8..0f7d2ba90 100644 --- a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -100,27 +100,37 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { fun testClassContainsFields(){ val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classFields = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) + val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) assertNotNull(classFields) val classFieldsNames = classFields!!.map { it.name } assertContainsElements(classFieldsNames, "weight") + assertEquals(true, classFields[0].isPublic) + assertContainsElements(classFieldsNames, "speed") + assertEquals(true, classFields[1].isPublic) + assertContainsElements(classFieldsNames, "engine_rpm") + assertEquals(false, classFields[2].isPublic) } fun testClassContainsMethods(){ val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classMethods = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) + val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) - TestCase.assertNotNull(classMethods) + assertNotNull(classMethods) val classMethodsNames = classMethods!!.map { it.name } assertContainsElements(classMethodsNames, "accelerate") + assertEquals(classMethods[0].isPublic, true) + assertContainsElements(classMethodsNames, "slowDown") + assertEquals(classMethods[1].isPublic, true) + assertContainsElements(classMethodsNames, "maximize") + assertEquals(classMethods[2].isPublic, false) } fun testGetAssociatedActiveBindings(){ @@ -128,7 +138,7 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) val classActiveBindings = R6ClassInfoUtil.getAssociatedActiveBindings(rCallExpression!!) - TestCase.assertNotNull(classActiveBindings) + assertNotNull(classActiveBindings) assertEquals(classActiveBindings!!.size, 1) val classActiveBindingsNames = classActiveBindings.map { it.name } @@ -138,9 +148,9 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { fun testGetShortenedClassAssociatedFields(){ val rAssignmentStatement = getRootElementOfPsi(shortedClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classFields = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) + val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) - TestCase.assertNotNull(classFields) + assertNotNull(classFields) val classFieldsNames = classFields!!.map { it.name } assertContainsElements(classFieldsNames, "weight") @@ -151,13 +161,29 @@ class R6ClassInfoUtilTests : RClassesUtilTestsBase() { fun testGetShortenedClassAssociatedMethods(){ val rAssignmentStatement = getRootElementOfPsi(shortedClassCodeDefinition) as RAssignmentStatement val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) - val classMethods = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) + val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) - TestCase.assertNotNull(classMethods) + assertNotNull(classMethods) val classMethodsNames = classMethods!!.map { it.name } assertContainsElements(classMethodsNames, "accelerate") assertContainsElements(classMethodsNames, "slowDown") classMethods.forEach { assertEquals(true, it.isPublic) } } + + fun testGetMembers(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classMethods = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) + + assertNotNull(classMethods) + + val classMethodsNames = classMethods!!.map { it.name } + assertContainsElements(classMethodsNames, "weight") + assertContainsElements(classMethodsNames, "speed") + assertContainsElements(classMethodsNames, "engine_rpm") + assertContainsElements(classMethodsNames, "maximize") + assertContainsElements(classMethodsNames, "accelerate") + assertContainsElements(classMethodsNames, "slowDown") + } } \ No newline at end of file From 47a9fe58978a36ba0ee7240812bfbc7323220ea0 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Wed, 5 May 2021 01:33:51 +0300 Subject: [PATCH 47/52] remove temporary/ directory --- temporary/RKernel-Proto/service.proto | 914 -------------------------- temporary/RKernel/RPIServiceImpl.h | 212 ------ temporary/RKernel/RRefs.cpp | 410 ------------ temporary/RKernel/init.R | 504 -------------- 4 files changed, 2040 deletions(-) delete mode 100644 temporary/RKernel-Proto/service.proto delete mode 100644 temporary/RKernel/RPIServiceImpl.h delete mode 100644 temporary/RKernel/RRefs.cpp delete mode 100644 temporary/RKernel/init.R diff --git a/temporary/RKernel-Proto/service.proto b/temporary/RKernel-Proto/service.proto deleted file mode 100644 index eeaebc1d6..000000000 --- a/temporary/RKernel-Proto/service.proto +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -syntax = "proto3"; -import "google/protobuf/empty.proto"; -import "google/protobuf/wrappers.proto"; - -option java_package = "org.jetbrains.r.rinterop"; -option java_multiple_files = true; - -package rplugininterop; - -service RPIService { - rpc getInfo(google.protobuf.Empty) returns (GetInfoResponse) {} - rpc isBusy(google.protobuf.Empty) returns (google.protobuf.BoolValue) {} - - rpc init(Init) returns (stream CommandOutput) {} - rpc quit(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc quitProceed(google.protobuf.Empty) returns (google.protobuf.Empty) {} - - rpc executeCode(ExecuteCodeRequest) returns (stream ExecuteCodeResponse) {} - rpc sendReadLn(google.protobuf.StringValue) returns (google.protobuf.Empty) {} - rpc sendEof(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc replInterrupt(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc getAsyncEvents(google.protobuf.Empty) returns (stream AsyncEvent) {} - - // Debugger - rpc debugAddOrModifyBreakpoint(DebugAddOrModifyBreakpointRequest) returns (google.protobuf.Empty) {} - rpc debugSetMasterBreakpoint(DebugSetMasterBreakpointRequest) returns (google.protobuf.Empty) {} - rpc debugRemoveBreakpoint(google.protobuf.Int32Value) returns (google.protobuf.Empty) {} - rpc debugCommandContinue(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandPause(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandStop(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandStepOver(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandStepInto(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandStepIntoMyCode(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandStepOut(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc debugCommandRunToPosition(SourcePosition) returns (google.protobuf.Empty) {} - rpc debugMuteBreakpoints(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} - rpc getFunctionSourcePosition(RRef) returns (GetFunctionSourcePositionResponse) {} - rpc getSourceFileText(google.protobuf.StringValue) returns (google.protobuf.StringValue) {} - rpc getSourceFileName(google.protobuf.StringValue) returns (google.protobuf.StringValue) {} - - // Graphics device service points - rpc graphicsInit(GraphicsInitRequest) returns (stream CommandOutput) {} - rpc graphicsDump(google.protobuf.Empty) returns (GraphicsDumpResponse) {} - rpc graphicsRescale(GraphicsRescaleRequest) returns (stream CommandOutput) {} - rpc graphicsRescaleStored(GraphicsRescaleStoredRequest) returns (stream CommandOutput) {} - rpc graphicsSetParameters(ScreenParameters) returns (google.protobuf.Empty) {} - rpc graphicsGetSnapshotPath(GraphicsGetSnapshotPathRequest) returns (GraphicsGetSnapshotPathResponse) {} - rpc graphicsFetchPlot(google.protobuf.Int32Value) returns (GraphicsFetchPlotResponse) {} - rpc graphicsCreateGroup(google.protobuf.Empty) returns (stream CommandOutput) {} - rpc graphicsRemoveGroup(google.protobuf.StringValue) returns (stream CommandOutput) {} - rpc graphicsShutdown(google.protobuf.Empty) returns (stream CommandOutput) {} - - // RMarkdown chunks - rpc beforeChunkExecution(ChunkParameters) returns (stream CommandOutput) {} - rpc afterChunkExecution(google.protobuf.Empty) returns (stream CommandOutput) {} - rpc pullChunkOutputPaths(google.protobuf.Empty) returns (StringList) {} - - // Repo utils service points - rpc repoGetPackageVersion(google.protobuf.StringValue) returns (stream CommandOutput) {} - rpc repoInstallPackage(RepoInstallPackageRequest) returns (google.protobuf.Empty) {} - rpc repoAddLibraryPath(google.protobuf.StringValue) returns (stream CommandOutput) {} - rpc repoCheckPackageInstalled(google.protobuf.StringValue) returns (stream CommandOutput) {} - rpc repoRemovePackage(RepoRemovePackageRequest) returns (google.protobuf.Empty) {} - - // Dataset import service points - rpc previewDataImport(PreviewDataImportRequest) returns (stream CommandOutput) {} - rpc commitDataImport(CommitDataImportRequest) returns (google.protobuf.Empty) {} - - // Methods for RRef and RVariableLoader - rpc copyToPersistentRef(RRef) returns (CopyToPersistentRefResponse) {} - rpc disposePersistentRefs(PersistentRefList) returns (google.protobuf.Empty) {} - rpc loaderGetParentEnvs(RRef) returns (ParentEnvsResponse) {} - rpc loaderGetVariables(GetVariablesRequest) returns (VariablesResponse) {} - rpc loaderGetLoadedNamespaces(google.protobuf.Empty) returns (StringList) {} - rpc loaderGetValueInfo(RRef) returns (ValueInfo) {} - rpc evaluateAsText(RRef) returns (StringOrError) {} - rpc evaluateAsBoolean(RRef) returns (google.protobuf.BoolValue) {} - rpc getDistinctStrings(RRef) returns (StringList) {} - rpc loadObjectNames(RRef) returns (StringList) {} - rpc findInheritorNamedArguments(RRef) returns (StringList) {} - rpc findExtraNamedArguments(RRef) returns (ExtraNamedArguments) {} - rpc getS4ClassInfoByObjectName(RRef) returns (S4ClassInfo) {} - rpc getR6ClassInfoByObjectName(RRef) returns (R6ClassInfo) {} - rpc getTableColumnsInfo(TableColumnsInfoRequest) returns (TableColumnsInfo) {} - rpc getFormalArguments(RRef) returns (StringList) {} - rpc getEqualityObject(RRef) returns (google.protobuf.Int64Value) {} - rpc setValue(SetValueRequest) returns (ValueInfo) {} - rpc getObjectSizes(RRefList) returns (Int64List) {} - - rpc getRMarkdownChunkOptions(google.protobuf.Empty) returns (StringList) {} - - // Data frame viewer - rpc dataFrameRegister(RRef) returns (google.protobuf.Int32Value) {} - rpc dataFrameGetInfo(RRef) returns (DataFrameInfoResponse) {} - rpc dataFrameGetData(DataFrameGetDataRequest) returns (DataFrameGetDataResponse) {} - rpc dataFrameSort(DataFrameSortRequest) returns (google.protobuf.Int32Value) {} - rpc dataFrameFilter(DataFrameFilterRequest) returns (google.protobuf.Int32Value) {} - rpc dataFrameRefresh(RRef) returns (google.protobuf.BoolValue) {} - - // Documentation and http - rpc convertRoxygenToHTML(ConvertRoxygenToHTMLRequest) returns (ConvertRoxygenToHTMLResponse) {} - rpc httpdRequest(google.protobuf.StringValue) returns (HttpdResponse) {} - rpc getDocumentationForPackage(google.protobuf.StringValue) returns (HttpdResponse) {} - rpc getDocumentationForSymbol(DocumentationForSymbolRequest) returns (HttpdResponse) {} - rpc startHttpd(google.protobuf.Empty) returns (google.protobuf.Int32Value) {} - - // Misc - rpc getWorkingDir(google.protobuf.Empty) returns (google.protobuf.StringValue) {} - rpc setWorkingDir(google.protobuf.StringValue) returns (google.protobuf.Empty) {} - rpc clearEnvironment(RRef) returns (google.protobuf.Empty) {} - rpc getSysEnv(GetSysEnvRequest) returns (StringList) {} - rpc loadInstalledPackages(google.protobuf.Empty) returns (RInstalledPackageList) {} - rpc loadLibPaths(google.protobuf.Empty) returns (RLibraryPathList) {} - rpc loadLibrary(google.protobuf.StringValue) returns (google.protobuf.Empty) {} - rpc unloadLibrary(UnloadLibraryRequest) returns (google.protobuf.Empty) {} - rpc saveGlobalEnvironment(google.protobuf.StringValue) returns (google.protobuf.Empty) {} - rpc loadEnvironment(LoadEnvironmentRequest) returns (google.protobuf.Empty) {} - rpc setOutputWidth(google.protobuf.Int32Value) returns (google.protobuf.Empty) {} - rpc clientRequestFinished(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc rStudioApiResponse(RObject) returns (google.protobuf.Empty) {} - rpc setSaveOnExit(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} - rpc setRStudioApiEnabled(google.protobuf.BoolValue) returns (stream CommandOutput) {} - rpc getLoadedShortS4ClassInfos(google.protobuf.Empty) returns (ShortS4ClassInfoList) {} - rpc getS4ClassInfoByClassName(google.protobuf.StringValue) returns (S4ClassInfo) {} - rpc getLoadedShortR6ClassInfos(google.protobuf.Empty) returns (ShortR6ClassInfoList) {} - rpc getR6ClassInfoByClassName(google.protobuf.StringValue) returns (R6ClassInfo) {} -} - -message Init { - string projectDir = 1; - string rScriptsPath = 2; - - string workspaceFile = 3; - bool loadWorkspace = 4; - bool saveOnExit = 5; - string httpUserAgent = 6; - bool enableRStudioApi = 7; -} - -message GetInfoResponse { - string rVersion = 1; - int32 pid = 2; -} - -message ExecuteCodeRequest { - enum DebugCommand { - CONTINUE = 0; - STOP = 1; - KEEP_PREVIOUS = 2; - } - string code = 1; - string sourceFileId = 2; - int32 sourceFileLineOffset = 3; - int32 sourceFileFirstLineOffset = 4; - bool withEcho = 5; - bool streamOutput = 6; - bool isRepl = 7; - bool isDebug = 8; - DebugCommand firstDebugCommand = 9; - bool setLastValue = 10; -} - -message CommandOutput { - enum Type { - STDOUT = 0; - STDERR = 1; - } - Type type = 1; - bytes text = 2; -} - -message ExceptionInfo { - string message = 1; - string call = 2; - oneof details { - google.protobuf.Empty simpleError = 3; - google.protobuf.Empty interrupted = 4; - string packageNotFound = 5; - } -} - -message ExecuteCodeResponse { - oneof msg { - CommandOutput output = 1; - string exception = 2; - } -} - -message StackFrame { - SourcePosition position = 1; - string functionName = 2; - int64 equalityObject = 3; - ExtendedSourcePosition extendedSourcePosition = 4; - string sourcePositionText = 5; -} - -message StackFrameList { - repeated StackFrame frames = 1; -} - -message RObject { - message RString { - repeated string strings = 1; - } - message RInt { - repeated int64 ints = 1; - } - message RDouble { - repeated double doubles = 1; - } - message List { - repeated RObject rObjects = 1; - } - message RNull {} - message RBoolean { - repeated bool booleans = 1; - } - message KeyValue { - string key = 1; - RObject value = 2; - } - message NamedList { - repeated KeyValue rObjects = 1; - } - oneof object { - RString rString = 1; - RInt rInt = 2; - RDouble rDouble = 3; - List list = 4; - RNull rNull = 5; - RBoolean rBoolean = 6; - NamedList namedList = 7; - string error = 8; - } -} - -message AsyncEvent { - message RequestReadLn { - string prompt = 1; - } - message DebugPrompt { - bool changed = 1; - StackFrameList stack = 2; - } - message ViewRequest { - int32 persistentRefIndex = 1; - string title = 2; - ValueInfo value = 3; - } - message ViewTableRequest { - int32 persistentRefIndex = 1; - string title = 2; - } - message Exception { - ExceptionInfo exception = 1; - StackFrameList stack = 2; - } - message ShowFileRequest { - string filePath = 1; - string title = 2; - } - message RStudioApiRequest { - int32 functionID = 1; - RObject args = 2; - } - oneof event { - google.protobuf.Empty busy = 1; - CommandOutput text = 2; - RequestReadLn requestReadLn = 3; - google.protobuf.Empty prompt = 4; - DebugPrompt debugPrompt = 5; - google.protobuf.Empty termination = 6; - Exception exception = 7; - ViewRequest viewRequest = 8; - ShowFileRequest showFileRequest = 9; - HttpdResponse showHelpRequest = 10; - google.protobuf.Empty subprocessInput = 11; - string browseURLRequest = 12; - RStudioApiRequest rStudioApiRequest = 13; - int32 debugRemoveBreakpointRequest = 14; - SourcePosition debugPrintSourcePositionToConsoleRequest = 15; - ViewTableRequest viewTableRequest = 16; - } -} - -message SourcePosition { - string fileId = 1; - int32 line = 2; -} - -message GetFunctionSourcePositionResponse { - SourcePosition position = 1; - string sourcePositionText = 2; -} - -message ExtendedSourcePosition { - int32 startLine = 1; - int32 startOffset = 2; - int32 endLine = 3; - int32 endOffset = 4; -} - -message DebugAddOrModifyBreakpointRequest { - int32 id = 1; - SourcePosition position = 2; - bool enabled = 3; - bool suspend = 4; - string evaluateAndLog = 5; - string condition = 6; - bool removeAfterHit = 7; - bool hitMessage = 8; - bool printStack = 9; -} - -message DebugSetMasterBreakpointRequest { - int32 breakpointId = 1; - oneof master { - google.protobuf.Empty none = 2; - int32 masterId = 3; - } - bool leaveEnabled = 4; -} - -message RRef { - message Member { - RRef env = 1; - string name = 2; - } - message ParentEnv { - RRef env = 1; - int32 index = 2; - } - message Expression { - RRef env = 1; - string code = 2; - } - message ListElement { - RRef list = 1; - int64 index = 2; - } - oneof ref { - int32 persistentIndex = 1; - google.protobuf.Empty globalEnv = 2; - google.protobuf.Empty currentEnv = 3; - int32 sysFrameIndex = 4; - Member member = 5; - ParentEnv parentEnv = 6; - Expression expression = 7; - ListElement listElement = 8; - int32 errorStackSysFrameIndex = 9; - RRef attributes = 10; - } -} - -message CopyToPersistentRefResponse { - oneof response { - int32 persistentIndex = 1; - string error = 2; - } -} - -message PersistentRefList { - repeated int32 indices = 1; -} - -message ParentEnvsResponse { - message EnvInfo { - string name = 1; - } - repeated EnvInfo envs = 1; -} - -message GetVariablesRequest { - RRef obj = 1; - int64 start = 2; - int64 end = 3; - // These parameters are for environments only - bool noHidden = 4; - bool noFunctions = 5; - bool onlyFunctions = 6; -} - -message VariablesResponse { - message Variable { - string name = 1; - ValueInfo value = 2; - } - bool isEnv = 1; - int64 totalCount = 2; - repeated Variable vars = 3; -} - -message ValueInfo { - message Unevaluated { - string code = 1; - } - message Value { - string textValue = 1; - bool isComplete = 2; - bool isVector = 3; - bool isS4 = 4; - bool isR6 = 5; - } - message List { - int64 length = 1; - } - message DataFrame { - int32 rows = 1; - int32 cols = 2; - } - message Function { - string header = 1; - } - message Environment { - string name = 1; - } - message Error { - string text = 1; - } - message Matrix { - repeated int32 dim = 1; - } - repeated string cls = 1; - oneof info { - Unevaluated unevaluated = 2; - Value value = 3; - List list = 4; - DataFrame dataFrame = 5; - Function function = 6; - Environment environment = 7; - google.protobuf.Empty graph = 8; - Error error = 9; - Matrix matrix = 10; - } -} - -message StringList { - repeated string list = 1; -} - -message Int32List { - string message = 1; - repeated int32 value = 2; -} - -message GraphicsInstallRequest { - string packagePath = 1; - string libraryPath = 2; - string packageType = 3; -} - -message ScreenParameters { - int32 width = 1; - int32 height = 2; - int32 resolution = 3; -} - -message GraphicsInitRequest { - ScreenParameters screenParameters = 1; - bool inMemory = 2; -} - -message GraphicsDumpResponse { - string message = 1; - map number2Parameters = 2; -} - -message GraphicsRescaleRequest { - int32 snapshotNumber = 1; - ScreenParameters newParameters = 2; -} - -message GraphicsRescaleStoredRequest { - string groupId = 1; - int32 snapshotNumber = 2; - int32 snapshotVersion = 3; - ScreenParameters newParameters = 4; -} - -message GraphicsGetSnapshotPathRequest { - string groupId = 1; - int32 snapshotNumber = 2; -} - -message GraphicsGetSnapshotPathResponse { - string message = 1; - string snapshotName = 2; - string directory = 3; -} - -message Font { - string name = 1; - float size = 2; - int32 style = 3; -} - -message Stroke { - float width = 1; - int32 cap = 2; - int32 join = 3; - float miterLimit = 4; - int32 pattern = 5; -} - -message Polyline { - repeated fixed64 point = 1; - int32 previewCount = 2; -} - -message RasterImage { - int32 width = 1; - int32 height = 2; - bytes data = 3; // little-endian uint32[] ARGB -} - -message FixedViewport { - float ratio = 1; - float delta = 2; - int32 parentIndex = 3; -} - -message FreeViewport { - fixed64 from = 1; - fixed64 to = 2; - int32 parentIndex = 3; -} - -message Viewport { - oneof kind { - FixedViewport fixed = 1; - FreeViewport free = 2; - } -} - -message CircleFigure { - fixed64 center = 1; - fixed32 radius = 2; - int32 strokeIndex = 3; - int32 colorIndex = 4; - int32 fillIndex = 5; -} - -message LineFigure { - fixed64 from = 1; - fixed64 to = 2; - int32 strokeIndex = 3; - int32 colorIndex = 4; -} - -message PathFigure { - repeated Polyline subPath = 1; - bool winding = 2; - int32 strokeIndex = 3; - int32 colorIndex = 4; - int32 fillIndex = 5; -} - -message PolygonFigure { - Polyline polyline = 1; - int32 strokeIndex = 2; - int32 colorIndex = 3; - int32 fillIndex = 4; -} - -message PolylineFigure { - Polyline polyline = 1; - int32 strokeIndex = 2; - int32 colorIndex = 3; -} - -message RasterFigure { - RasterImage image = 1; - fixed64 from = 2; - fixed64 to = 3; - float angle = 4; - bool interpolate = 5; -} - -message RectangleFigure { - fixed64 from = 1; - fixed64 to = 2; - int32 strokeIndex = 3; - int32 colorIndex = 4; - int32 fillIndex = 5; -} - -message TextFigure { - string text = 1; - fixed64 position = 2; - float angle = 3; - float anchor = 4; - int32 fontIndex = 5; - int32 colorIndex = 6; -} - -message Figure { - oneof kind { - CircleFigure circle = 1; - LineFigure line = 2; - PathFigure path = 3; - PolygonFigure polygon = 4; - PolylineFigure polyline = 5; - RasterFigure raster = 6; - RectangleFigure rectangle = 7; - TextFigure text = 8; - } -} - -message Layer { - int32 viewportIndex = 1; - int32 clippingAreaIndex = 2; - repeated Figure figure = 3; - bool isAxisText = 4; -} - -message Plot { - repeated Font font = 1; - repeated int32 color = 2; - repeated Stroke stroke = 3; - repeated Viewport viewport = 4; - repeated Layer layer = 5; - int32 previewComplexity = 6; - int32 totalComplexity = 7; - int32 error = 8; -} - -message GraphicsFetchPlotResponse { - string message = 1; - Plot plot = 2; -} - -message ChunkParameters { - string rmarkdownParameters = 1; - string chunkText = 2; -} - -message RepoInstallPackageRequest { - string packageName = 1; - string fallbackMethod = 2; - map arguments = 3; -} - -message RepoRemovePackageRequest { - string packageName = 1; - string libraryPath = 2; -} - -message PreviewDataImportRequest { - string path = 1; - string mode = 2; - int32 rowCount = 3; - map options = 4; -} - -message CommitDataImportRequest { - string name = 1; - string path = 2; - string mode = 3; - map options = 4; -} - -message TableColumnsInfoRequest { - RRef ref = 1; -} - -message TableColumnsInfo { - enum TableType { - UNKNOWN = 0; - DPLYR = 1; - DATA_TABLE = 2; - DATA_FRAME = 3; - } - message Column { - string name = 1; - string type = 2; - } - repeated Column columns = 1; - TableType tableType = 2; -} - -message DataFrameInfoResponse { - enum ColumnType { - INTEGER = 0; - DOUBLE = 1; - BOOLEAN = 3; - STRING = 4; - } - message Column { - string name = 1; - ColumnType type = 2; - bool sortable = 3; - bool isRowNames = 4; - } - int32 nRows = 1; - repeated Column columns = 2; - bool canRefresh = 3; -} - -message DataFrameGetDataRequest { - RRef ref = 1; - int32 start = 2; - int32 end = 3; -} - -message DataFrameGetDataResponse { - message Value { - oneof value { - google.protobuf.Empty na = 1; - int32 intValue = 2; - double doubleValue = 3; - bool booleanValue = 4; - string stringValue = 5; - } - } - message Column { - repeated Value values = 1; - } - repeated Column columns = 1; -} - -message DataFrameSortRequest { - message SortKey { - int32 columnIndex = 1; - bool descending = 2; - } - RRef ref = 1; - repeated SortKey keys = 2; -} - -message DataFrameFilterRequest { - message Filter { - message ComposedFilter { - enum Type { - AND = 0; - OR = 1; - NOT = 2; - } - Type type = 1; - repeated Filter filters = 2; - } - message Operator { - enum Type { - EQ = 0; - NEQ = 1; - LESS = 2; - GREATER = 3; - LEQ = 4; - GEQ = 5; - REGEX = 6; - } - int32 column = 1; - Type type = 2; - string value = 3; - } - message NaFilter { - int32 column = 1; - bool isNa = 2; - } - oneof filter { - google.protobuf.Empty true = 1; - ComposedFilter composed = 2; - Operator operator = 3; - NaFilter naFilter = 4; - } - } - RRef ref = 1; - Filter filter = 2; -} - -message ConvertRoxygenToHTMLRequest { - string functionName = 1; - string functionText = 2; -} - -message ConvertRoxygenToHTMLResponse { - oneof result { - string text = 1; - string error = 2; - } -} - -message UnloadLibraryRequest { - string packageName = 1; - bool withDynamicLibrary = 2; -} - -message HttpdResponse { - bool success = 1; - string content = 2; - string url = 3; -} - -message DocumentationForSymbolRequest { - string symbol = 1; - string package = 2; -} - -message SetValueRequest { - RRef ref = 1; - RRef value = 2; -} - -message LoadEnvironmentRequest { - string file = 1; - // empty variable name means load into global env - string variable = 2; -} - -message RRefList { - repeated RRef refs = 1; -} - -message Int64List { - repeated int64 list = 1; -} - -message StringOrError { - oneof result { - string value = 1; - string error = 2; - } -} - -message ExtraNamedArguments { - // See org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo for details - repeated string argNames = 1; - repeated string funArgNames = 2; -} - -message GetSysEnvRequest { - string envName = 1; - repeated string flags = 2; -} - -message RLibraryPathList { - message RLibraryPath { - string path = 1; - bool isWritable = 2; - } - - repeated RLibraryPath libPaths = 1; -} - -message RInstalledPackageList { - message RInstalledPackage { - message MapEntry { - string key = 1; - string value = 2; - } - enum RPackagePriority { - BASE = 0; - RECOMMENDED = 1; - NA = 2; - } - string packageName = 1; - string packageVersion = 2; - RPackagePriority priority = 3; - string libraryPath = 4; - string canonicalPackagePath = 5; - repeated MapEntry description = 6; - } - - repeated RInstalledPackage packages = 1; -} - -message S4ClassInfo { - message S4ClassSlot { - string name = 1; - string type = 2; - } - string className = 1; - string packageName = 2; - repeated S4ClassSlot slots = 3; - repeated string superClasses = 4; - bool isVirtual = 5; -} - -message ShortS4ClassInfoList { - message ShortS4ClassInfo { - string name = 1; - string package = 2; - bool isVirtual = 3; - } - - repeated ShortS4ClassInfo shortS4ClassInfos = 1; -} - -message R6ClassInfo { - message R6ClassMember { - string name = 1; - bool isPublic = 2; - } - - message R6ClassActiveBinding { - string name = 1; - } - - string className = 1; - repeated string superClasses = 2; - repeated R6ClassMember members = 3; - repeated R6ClassActiveBinding activeBindings = 4; -} - -message ShortR6ClassInfoList { - message ShortR6ClassInfo { - string name = 1; - } - - repeated ShortR6ClassInfo shortR6ClassInfos = 1; -} \ No newline at end of file diff --git a/temporary/RKernel/RPIServiceImpl.h b/temporary/RKernel/RPIServiceImpl.h deleted file mode 100644 index 726a57617..000000000 --- a/temporary/RKernel/RPIServiceImpl.h +++ /dev/null @@ -1,212 +0,0 @@ -// Rkernel is an execution kernel for R interpreter -// Copyright (C) 2019 JetBrains s.r.o. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - - -#ifndef RWRAPPER_RPI_SERVICE_IMPL_H -#define RWRAPPER_RPI_SERVICE_IMPL_H - -#include "protos/service.grpc.pb.h" -#include -#include -#include "util/BlockingQueue.h" -#include "util/IndexedStorage.h" -#include "IO.h" -#include "Options.h" -#include "debugger/RDebugger.h" -#include "RStuff/MySEXP.h" - -using grpc::Status; -using grpc::ServerContext; -using grpc::ServerWriter; -using namespace rplugininterop; -using namespace google::protobuf; - -class RPIServiceImpl : public RPIService::Service { -public: - RPIServiceImpl(); - ~RPIServiceImpl() override; - - Status getInfo(ServerContext* context, const Empty*, GetInfoResponse* response) override; - Status isBusy(ServerContext* context, const Empty*, BoolValue* response) override; - Status init(ServerContext* context, const Init* request, ServerWriter* response) override; - Status quit(ServerContext* context, const Empty*, Empty*) override; - Status quitProceed(ServerContext* context, const Empty*, Empty*) override; - - Status executeCode(ServerContext* context, const ExecuteCodeRequest* request, ServerWriter* writer) override; - Status sendReadLn(ServerContext* context, const StringValue* request, Empty*) override; - Status sendEof(ServerContext* context, const Empty*, Empty*) override; - Status replInterrupt(ServerContext* context, const Empty*, Empty*) override; - Status getAsyncEvents(ServerContext* context, const Empty*, ServerWriter* writer) override; - - Status debugAddOrModifyBreakpoint(ServerContext* context, const DebugAddOrModifyBreakpointRequest* request, Empty*) override; - Status debugSetMasterBreakpoint(ServerContext* context, const DebugSetMasterBreakpointRequest* request, Empty*) override; - Status debugRemoveBreakpoint(ServerContext* context, const Int32Value* request, Empty*) override; - Status debugCommandContinue(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandPause(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandStop(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandStepOver(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandStepInto(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandStepIntoMyCode(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandStepOut(ServerContext* context, const Empty*, Empty*) override; - Status debugCommandRunToPosition(ServerContext* context, const SourcePosition* request, Empty*) override; - Status debugMuteBreakpoints(ServerContext* context, const BoolValue* request, Empty*) override; - - Status copyToPersistentRef(ServerContext* context, const RRef* request, CopyToPersistentRefResponse* response) override; - Status disposePersistentRefs(ServerContext* context, const PersistentRefList* request, Empty*) override; - - Status loaderGetParentEnvs(ServerContext* context, const RRef* request, ParentEnvsResponse* response) override; - Status loaderGetVariables(ServerContext* context, const GetVariablesRequest* request, VariablesResponse* response) override; - Status loaderGetLoadedNamespaces(ServerContext* context, const Empty*, StringList* response) override; - Status loaderGetValueInfo(ServerContext* context, const RRef* request, ValueInfo* response) override; - Status evaluateAsText(ServerContext* context, const RRef* request, StringOrError* response) override; - Status evaluateAsBoolean(ServerContext* context, const RRef* request, BoolValue* response) override; - Status getDistinctStrings(ServerContext* context, const RRef* request, StringList* response) override; - Status getFunctionSourcePosition(ServerContext* context, const RRef* request, GetFunctionSourcePositionResponse* response) override; - Status getSourceFileText(ServerContext* context, const StringValue* request, StringValue* response) override; - Status getSourceFileName(ServerContext* context, const StringValue* request, StringValue* response) override; - Status loadObjectNames(ServerContext* context, const RRef* request, StringList* response) override; - Status findInheritorNamedArguments(ServerContext* context, const RRef* request, StringList* response) override; - Status findExtraNamedArguments(ServerContext* context, const RRef* request, ExtraNamedArguments* response) override; - Status getS4ClassInfoByObjectName(ServerContext* context, const RRef* request, S4ClassInfo* response) override; - Status getS4ClassInfoByClassName(ServerContext* context, const StringValue* request, S4ClassInfo* response) override; - Status getR6ClassInfoByObjectName(ServerContext* context, const RRef* request, R6ClassInfo* response) override; - Status getLoadedShortS4ClassInfos(ServerContext* context, const Empty* request, ShortS4ClassInfoList* response) override; - Status getTableColumnsInfo(ServerContext* context, const TableColumnsInfoRequest* request, TableColumnsInfo* response) override; - Status getFormalArguments(ServerContext* context, const RRef* request, StringList* response) override; - Status getEqualityObject(ServerContext* context, const RRef* request, Int64Value* response) override; - Status setValue(ServerContext* context, const SetValueRequest* request, ValueInfo* response) override; - Status getObjectSizes(ServerContext* context, const RRefList* request, Int64List* response) override; - - Status getRMarkdownChunkOptions(ServerContext* context, const Empty*, StringList* response) override; - - Status graphicsInit(ServerContext* context, const GraphicsInitRequest* request, ServerWriter* writer) override; - Status graphicsDump(ServerContext* context, const Empty*, GraphicsDumpResponse* response) override; - Status graphicsRescale(ServerContext* context, const GraphicsRescaleRequest* request, ServerWriter* writer) override; - Status graphicsRescaleStored(ServerContext* context, const GraphicsRescaleStoredRequest* request, ServerWriter* writer) override; - Status graphicsSetParameters(ServerContext* context, const ScreenParameters* request, Empty*) override; - Status graphicsGetSnapshotPath(ServerContext* context, const GraphicsGetSnapshotPathRequest* request, GraphicsGetSnapshotPathResponse* response) override; - Status graphicsFetchPlot(ServerContext* context, const Int32Value* request, GraphicsFetchPlotResponse* response) override; - Status graphicsCreateGroup(ServerContext* context, const google::protobuf::Empty* request, ServerWriter* writer) override; - Status graphicsRemoveGroup(ServerContext* context, const google::protobuf::StringValue* request, ServerWriter* writer) override; - Status graphicsShutdown(ServerContext* context, const Empty*, ServerWriter* writer) override; - - Status beforeChunkExecution(ServerContext *context, const ChunkParameters *request, ServerWriter *writer) override; - Status afterChunkExecution(ServerContext *context, const Empty*, ServerWriter *writer) override; - Status pullChunkOutputPaths(ServerContext *context, const Empty*, StringList* response) override; - - Status repoGetPackageVersion(ServerContext* context, const StringValue* request, ServerWriter* writer) override; - Status repoInstallPackage(ServerContext* context, const RepoInstallPackageRequest* request, Empty*) override; - Status repoAddLibraryPath(ServerContext* context, const StringValue* request, ServerWriter* writer) override; - Status repoCheckPackageInstalled(ServerContext* context, const StringValue* request, ServerWriter* writer) override; - Status repoRemovePackage(ServerContext* context, const RepoRemovePackageRequest* request, Empty*) override; - - Status previewDataImport(ServerContext* context, const PreviewDataImportRequest* request, ServerWriter* writer) override; - Status commitDataImport(ServerContext* context, const CommitDataImportRequest* request, Empty*) override; - - Status dataFrameRegister(ServerContext* context, const RRef* request, Int32Value* response) override; - Status dataFrameGetInfo(ServerContext* context, const RRef* request, DataFrameInfoResponse* response) override; - Status dataFrameGetData(ServerContext* context, const DataFrameGetDataRequest* request, DataFrameGetDataResponse* response) override; - Status dataFrameSort(ServerContext* context, const DataFrameSortRequest* request, Int32Value* response) override; - Status dataFrameFilter(ServerContext* context, const DataFrameFilterRequest* request, Int32Value* response) override; - Status dataFrameRefresh(ServerContext* context, const RRef* request, BoolValue* response) override; - - Status getWorkingDir(ServerContext* context, const Empty*, StringValue* response) override; - Status setWorkingDir(ServerContext* context, const StringValue* request, Empty*) override; - Status clearEnvironment(ServerContext* context, const RRef* request, Empty*) override; - Status getSysEnv(ServerContext* context, const GetSysEnvRequest* request, StringList* response) override; - Status loadInstalledPackages(ServerContext* context, const Empty*, RInstalledPackageList* response) override; - Status loadLibPaths(ServerContext* context, const Empty*, RLibraryPathList* response) override; - Status loadLibrary(ServerContext* context, const StringValue* request, Empty*) override; - Status unloadLibrary(ServerContext* context, const UnloadLibraryRequest* request, Empty*) override; - Status saveGlobalEnvironment(ServerContext *context, const StringValue *request, Empty*) override; - Status loadEnvironment(ServerContext *context, const LoadEnvironmentRequest *request, Empty*) override; - Status setOutputWidth(ServerContext* context, const Int32Value* request, Empty*) override; - Status clientRequestFinished(ServerContext* context, const Empty*, Empty*) override; - Status rStudioApiResponse(ServerContext* context, const RObject* request, Empty* response) override; - - Status convertRoxygenToHTML(ServerContext* context, const ConvertRoxygenToHTMLRequest* request, ConvertRoxygenToHTMLResponse* response) override; - Status httpdRequest(ServerContext* context, const StringValue* request, HttpdResponse* response) override; - Status getDocumentationForPackage(ServerContext* context, const StringValue* request, HttpdResponse* response) override; - Status getDocumentationForSymbol(ServerContext* context, const DocumentationForSymbolRequest* request, HttpdResponse* response) override; - Status startHttpd(ServerContext* context, const Empty*, Int32Value* response) override; - - Status setSaveOnExit(ServerContext* context, const BoolValue* request, Empty*) override; - Status setRStudioApiEnabled(::grpc::ServerContext *context, const ::google::protobuf::BoolValue *request, ServerWriter* response) override; - - void mainLoop(); - std::string readLineHandler(std::string const& prompt); - void subprocessHandler( - bool askInput, - std::function const& inputCallback, std::function const& interruptCallback); - void subprocessHandlerStop(); - void debugPromptHandler(); - void viewHandler(SEXP expr, SEXP env, SEXP title); - void showFileHandler(std::string const& filePath, std::string const& title); - void showHelpHandler(std::string const& content, std::string const& url); - void browseURLHandler(std::string const& url); - RObject rStudioApiRequest(int32_t functionID, const RObject &args); - - void sendAsyncEvent(AsyncEvent const& e); - void sendAsyncRequestAndWait(AsyncEvent const& e); - void setChildProcessState(); - volatile bool terminate = false; - volatile bool terminateProceed = false; - - void executeOnMainThread(std::function const& f, ServerContext* contextForCancellation = nullptr, bool immediate = false); - - OutputHandler getOutputHandlerForChildProcess(); - - void setValueImpl(RRef const& ref, SEXP value); - SEXP dereference(RRef const& ref); - - OutputHandler replOutputHandler; - void writeToReplOutputHandler(std::string const& s, OutputType type); - - IndexedStorage persistentRefStorage; - -private: - BlockingQueue asyncEvents; - - enum ReplState { - PROMPT, DEBUG_PROMPT, READ_LINE, REPL_BUSY, CHILD_PROCESS, SUBPROCESS_INPUT - }; - bool isReplOutput = false; - ReplState replState = REPL_BUSY; - volatile bool busy = true; - volatile bool subprocessActive = false; - bool subprocessInterrupt = false; - - bool isInClientRequest = false; - bool isInRStudioApiRequest = false; - - std::vector lastErrorStack; - - Status executeCommand(ServerContext* context, const std::string& command, ServerWriter* writer); - - Status replExecuteCommand(ServerContext* context, const std::string& command); - - friend void quitRPIService(); - friend void saveRWrapperCrashReport(std::string const&); -}; - -const int CLIENT_RPC_TIMEOUT_MILLIS = 60000; - -extern std::unique_ptr rpiService; - -void initRPIService(); -void quitRPIService(); - -#endif //RWRAPPER_RPI_SERVICE_IMPL_H diff --git a/temporary/RKernel/RRefs.cpp b/temporary/RKernel/RRefs.cpp deleted file mode 100644 index 23328d8f0..000000000 --- a/temporary/RKernel/RRefs.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// Rkernel is an execution kernel for R interpreter -// Copyright (C) 2019 JetBrains s.r.o. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - - -#include "RPIServiceImpl.h" -#include -#include "RStuff/RUtil.h" -#include "debugger/SourceFileManager.h" -#include "EventLoop.h" -#include "RStuff/RObjects.h" -#include "RLoader.h" - -const int EVALUATE_AS_TEXT_MAX_LENGTH = 500000; - -SEXP RPIServiceImpl::dereference(RRef const &ref) { - switch (ref.ref_case()) { - case RRef::kPersistentIndex: { - int i = ref.persistentindex(); - return persistentRefStorage.has(i) ? (SEXP) persistentRefStorage[i] : R_NilValue; - } - case RRef::kGlobalEnv: - return R_GlobalEnv; - case RRef::kCurrentEnv: - return currentEnvironment(); - case RRef::kSysFrameIndex: { - int index = ref.sysframeindex(); - auto const &stack = rDebugger.getSavedStack(); - if (index < 0 || index >= stack.size()) return R_NilValue; - return stack[index].environment; - } - case RRef::kErrorStackSysFrameIndex: { - int index = ref.errorstacksysframeindex(); - if (index < 0 || index >= lastErrorStack.size()) return R_NilValue; - return lastErrorStack[index].environment; - } - case RRef::kMember: { - ShieldSEXP env = dereference(ref.member().env()); - return env.getVar(ref.member().name()); - } - case RRef::kParentEnv: { - PrSEXP env = dereference(ref.parentenv().env()); - int count = ref.parentenv().index(); - for (int i = 0; i < count; ++i) { - if (env.type() != ENVSXP || env == R_EmptyEnv) { - return R_NilValue; - } - env = env.parentEnv(); - } - return env; - } - case RRef::kExpression: { - ShieldSEXP env = dereference(ref.expression().env()); - std::string code = ref.expression().code(); - return RI->eval(parseCode(code), named("envir", env)); - } - case RRef::kListElement: { - ShieldSEXP list = dereference(ref.listelement().list()); - long long index = ref.listelement().index(); - ShieldSEXP unclassed = Rf_inherits(list, "factor") ? (SEXP) list : RI->unclass(list); - return RI->doubleSubscript(unclassed, index + 1); - } - case RRef::kAttributes: { - ShieldSEXP x = dereference(ref.attributes()); - return RI->attributes(Rf_lang2(RI->quote, x)); - } - default: - return R_NilValue; - } -} - -Status RPIServiceImpl::copyToPersistentRef(ServerContext *context, const RRef *request, - CopyToPersistentRefResponse *response) { - executeOnMainThread([&] { - try { - response->set_persistentindex(persistentRefStorage.add(dereference(*request))); - } catch (RExceptionBase const &e) { - response->set_error(e.what()); - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::disposePersistentRefs(ServerContext *, const PersistentRefList *request, Empty *) { - std::vector refs(request->indices().begin(), request->indices().end()); - eventLoopExecute([=] { - for (int ref : refs) { - if (persistentRefStorage.has(ref)) { - persistentRefStorage.remove(ref); - } - } - }); - return Status::OK; -} - -Status RPIServiceImpl::evaluateAsText(ServerContext *context, const RRef *request, StringOrError *response) { - executeOnMainThread([&] { - try { - PrSEXP value = dereference(*request); - if (value.type() == STRSXP) { - value = RI->substring(value, 1, EVALUATE_AS_TEXT_MAX_LENGTH); - } - response->set_value(getPrintedValueWithLimit(value, EVALUATE_AS_TEXT_MAX_LENGTH)); - } catch (RExceptionBase const &e) { - response->set_error(e.what()); - } catch (...) { - response->set_error(""); - throw; - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::evaluateAsBoolean(ServerContext *context, const RRef *request, BoolValue *response) { - executeOnMainThread([&] { - try { - response->set_value(asBool(dereference(*request))); - } catch (RExceptionBase const &) { - response->set_value(false); - } - }, context, true); - return Status::OK; -} - - -Status RPIServiceImpl::getDistinctStrings(ServerContext *context, const RRef *request, StringList *response) { - executeOnMainThread([&] { - ShieldSEXP object = dereference(*request); - if (object.type() != STRSXP && !Rf_inherits(object, "factor")) { - return; - } - ShieldSEXP vector = RI->asCharacter(RI->unique(object)); - int sumLength = 0; - for (int i = 0; i < vector.length(); ++i) { - if (!vector.isNA(i)) { - std::string s = stringEltUTF8(vector, i); - sumLength += s.size(); - if (sumLength > EVALUATE_AS_TEXT_MAX_LENGTH) break; - response->add_list(s); - } - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::loadObjectNames(ServerContext *context, const RRef *request, StringList *response) { - executeOnMainThread([&] { - ShieldSEXP names = RI->ls(dereference(*request), named("all.names", true)); - if (names.type() != STRSXP) return; - for (int i = 0; i < names.length(); ++i) { - response->add_list(stringEltUTF8(names, i)); - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::findInheritorNamedArguments(ServerContext *context, const RRef *request, StringList *response) { - executeOnMainThread([&] { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("findInheritorNamedArguments"); - ShieldSEXP result = func(dereference(*request)); - if (TYPEOF(result) != STRSXP) return; - for (int i = 0; i < result.length(); ++i) { - response->add_list(stringEltUTF8(result, i)); - } - }, context, true); - return Status::OK; -} - -Status -RPIServiceImpl::findExtraNamedArguments(ServerContext *context, const RRef *request, ExtraNamedArguments *response) { - executeOnMainThread([&] { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("findExtraNamedArgs"); - ShieldSEXP result = func(dereference(*request), named("depth", 2)); - if (TYPEOF(result) != VECSXP) return; - for (int i = 0; i < result.length(); ++i) { - ShieldSEXP elem = VECTOR_ELT(result, i); - ShieldSEXP name = VECTOR_ELT(elem, 0); - if (asBool(VECTOR_ELT(elem, 1))) { - response->add_funargnames(stringEltUTF8(name, 0)); - } else { - response->add_argnames(stringEltUTF8(name, 0)); - } - } - }, context, true); - return Status::OK; -} - -Status -RPIServiceImpl::getLoadedShortS4ClassInfos(ServerContext *context, const Empty *, ShortS4ClassInfoList *response) { - executeOnMainThread([&] { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("getLoadedS4ClassInfos"); - ShieldSEXP result = func(); - if (TYPEOF(result) != VECSXP) return; - - for (int i = 0; i < result.length(); ++i) { - ShieldSEXP classRep = VECTOR_ELT(result, i); - if (TYPEOF(classRep) != S4SXP) continue; - - ShortS4ClassInfoList_ShortS4ClassInfo *info = response->add_shorts4classinfos(); - info->set_name(stringEltUTF8(R_do_slot(classRep, toSEXP("className")), 0)); - info->set_package(stringEltUTF8(R_do_slot(classRep, toSEXP("package")), 0)); - info->set_isvirtual(asBool(R_do_slot(classRep, toSEXP("virtual")))); - } - }, context, true); - return Status::OK; -} - -void getS4ClassInfo(const ShieldSEXP &classDef, S4ClassInfo *response) { - if (TYPEOF(classDef) != S4SXP) return; - response->set_classname(stringEltUTF8(R_do_slot(classDef, toSEXP("className")), 0)); - response->set_packagename(stringEltUTF8(R_do_slot(classDef, toSEXP("package")), 0)); - ShieldSEXP slotsList = R_do_slot(classDef, toSEXP("slots")); - ShieldSEXP slotsNames = Rf_getAttrib(slotsList, R_NamesSymbol); - for (int i = 0; i < slotsNames.length(); ++i) { - auto next_slot = response->add_slots(); - next_slot->set_name(stringEltUTF8(slotsNames, i)); - next_slot->set_type(stringEltUTF8(VECTOR_ELT(slotsList, i), 0)); - } - ShieldSEXP containsList = R_do_slot(classDef, toSEXP("contains")); - for (int i = 0; i < containsList.length(); ++i) { - ShieldSEXP superClass = VECTOR_ELT(containsList, i); - response->add_superclasses(stringEltUTF8(R_do_slot(classDef, toSEXP("superClass")), 0)); - } - - response->set_isvirtual(asBool(R_do_slot(classDef, toSEXP("virtual")))); -} - -Status RPIServiceImpl::getS4ClassInfoByObjectName(ServerContext *context, const RRef *request, S4ClassInfo *response) { - executeOnMainThread([&] { - ShieldSEXP obj = dereference(*request); - if (TYPEOF(obj) != S4SXP) return; - ShieldSEXP className = Rf_getAttrib(obj, R_ClassSymbol); - getS4ClassInfo(R_getClassDef_R(className), response); - }, context, true); - return Status::OK; -} - -Status -RPIServiceImpl::getS4ClassInfoByClassName(ServerContext *context, const StringValue *request, S4ClassInfo *response) { - executeOnMainThread([&] { - getS4ClassInfo(R_getClassDef_R(toSEXP(request->value())), response); - }, context, true); - return Status::OK; -} - -bool isObjectFromR6(const ShieldSEXP &object) { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("isObjectFromR6"); - return func(object); -} - -SEXPREC* getR6ClassName(const ShieldSEXP &object) { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("getR6ClassName"); - return func(object); -} - -SEXPREC* getR6ClassInheritanceTree(const ShieldSEXP &object) { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("getR6ClassInheritanceTree"); - return func(object); -} - -SEXPREC* getR6ClassDefMembers(const ShieldSEXP &object) { - ShieldSEXP jetbrainsEnv = RI->baseEnv.getVar(".jetbrains"); - ShieldSEXP func = jetbrainsEnv.getVar("getR6ClassDefMembers"); - return func(object); -} - -void getR6ClassInfo(const ShieldSEXP &classDef, R6ClassInfo *response) { - if (!isObjectFromR6(classDef)) return; - - auto className = getR6ClassName(classDef); - response->set_classname(stringEltUTF8(className, 0)); - - auto classInheritanceNames = getR6ClassInheritanceTree(classDef); - for (int i = 1; i < XLENGTH(classInheritanceNames); ++i) { - response->add_superclasses(stringEltUTF8(classInheritanceNames, i)); - } - - auto classMembers = getR6ClassDefMembers(classDef); - for (int i = 0; i < XLENGTH(classMembers); ++i) { - auto next_member = response->add_members(); - next_member->set_name(stringEltUTF8(classMembers, i)); - next_member->set_ispublic(true); - } -} - -Status RPIServiceImpl::getR6ClassInfoByObjectName(ServerContext *context, const RRef *request, R6ClassInfo *response) { - executeOnMainThread([&] { - ShieldSEXP obj = dereference(*request); - bool isR6 = isObjectFromR6(obj); - if (!isR6) return; - getR6ClassInfo(obj, response); - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::getTableColumnsInfo(ServerContext *context, const TableColumnsInfoRequest *request, - TableColumnsInfo *response) { - executeOnMainThread([&] { - ShieldSEXP table = dereference(request->ref()); - if (!isDataFrame(table)) return; - response->set_tabletype( - Rf_inherits(table, "tbl_df") ? TableColumnsInfo_TableType_DPLYR : - Rf_inherits(table, "data.table") ? TableColumnsInfo_TableType_DATA_TABLE : - Rf_inherits(table, "data.frame") ? TableColumnsInfo_TableType_DATA_FRAME : - TableColumnsInfo_TableType_UNKNOWN); - - ShieldSEXP names = RI->names(table); - if (TYPEOF(names) != STRSXP) return; - int ncol = asInt(RI->ncol(table)); - for (int i = 0; i < ncol; ++i) { - TableColumnsInfo::Column *column = response->add_columns(); - column->set_name(stringEltUTF8(names, i)); - column->set_type(asStringUTF8(RI->paste(RI->classes(table[i]), named("collapse", ",")))); - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::getFormalArguments(ServerContext *context, const RRef *request, StringList *response) { - executeOnMainThread([&] { - ShieldSEXP names = RI->names(RI->formals(dereference(*request))); - if (TYPEOF(names) != STRSXP) return; - for (int i = 0; i < names.length(); ++i) { - response->add_list(stringEltUTF8(names, i)); - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::getRMarkdownChunkOptions(ServerContext *context, const Empty *, StringList *response) { - executeOnMainThread([&] { - ShieldSEXP options = RI->evalCode("names(knitr::opts_chunk$get())", R_BaseEnv); - if (TYPEOF(options) != STRSXP) return; - for (int i = 0; i < options.length(); ++i) { - response->add_list(stringEltUTF8(options, i)); - } - }, context, true); - return Status::OK; -} - -Status RPIServiceImpl::getEqualityObject(ServerContext *context, const RRef *request, Int64Value *response) { - executeOnMainThread([&] { - try { - response->set_value((long long) (SEXP) dereference(*request)); - } catch (RExceptionBase const &) { - response->set_value(0); - } - }, context, true); - return Status::OK; -} - -void RPIServiceImpl::setValueImpl(RRef const &ref, SEXP value) { - SHIELD(value); - switch (ref.ref_case()) { - case RRef::kMember: { - ShieldSEXP env = dereference(ref.member().env()); - RI->assign(ref.member().name(), value, named("envir", env)); - break; - } - case RRef::kListElement: { - ShieldSEXP list = dereference(ref.listelement().list()); - ShieldSEXP newList = RI->doubleSubscriptAssign(list, ref.listelement().index() + 1, value); - setValueImpl(ref.listelement().list(), newList); - break; - } - case RRef::kAttributes: { - ShieldSEXP obj = dereference(ref.attributes()); - ShieldSEXP newObj = RI->attributesAssign(obj, value); - setValueImpl(ref.attributes(), newObj); - break; - } - default: { - throw std::invalid_argument("Invalid reference for setValue"); - } - } -} - -Status RPIServiceImpl::setValue(ServerContext *context, const SetValueRequest *request, ValueInfo *response) { - executeOnMainThread([&] { - try { - ShieldSEXP value = dereference(request->value()); - setValueImpl(request->ref(), value); - getValueInfo(value, response); - } catch (RExceptionBase const &e) { - response->mutable_error()->set_text(e.what()); - } catch (...) { - response->mutable_error()->set_text("Error"); - throw; - } - }, context, true); - return Status::OK; -} diff --git a/temporary/RKernel/init.R b/temporary/RKernel/init.R deleted file mode 100644 index 87735dc5b..000000000 --- a/temporary/RKernel/init.R +++ /dev/null @@ -1,504 +0,0 @@ -# Rkernel is an execution kernel for R interpreter -# Copyright (C) 2019 JetBrains s.r.o. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -if (".jetbrains" %in% ls(globalenv(), all.names = TRUE)) { - rm(".jetbrains", envir = globalenv()) -} - -# Set hook to record previous plot before new vanilla graphics are created -setHook(hookName = "before.plot.new", - value = function() { - .Call(".jetbrains_ther_device_record", FALSE) - }, - action = "append") - -# Set hook to record previous plot before new ggplot2 graphics are created -setHook(hookName = "before.grid.newpage", - value = function() { - .Call(".jetbrains_ther_device_record", TRUE) # Pass TRUE to indicate it was triggered by ggplot2 - }, - action = "append") - -# Force interpreter to restart custom graphics device -options(device = function() { - .Call(".jetbrains_ther_device_restart") -}) - -.jetbrains$defaultPlatformGUI <<- .Platform$GUI - -# Some packages might be not available as binaries. -# The default behaviour of interpreter in such a case -# is to ask user whether he wants to install it from source instead. -# This is not desirable that's why interpreter will be forced to install packages from source -# **when necessary** without user permission -options(install.packages.compile.from.source = "always") - -.jetbrains$init <<- function(rsession.path, project.dir) { - current.wd <- getwd() - tryCatch({ - tools.path <- file.path(rsession.path, "Tools.R") - options.path <- file.path(rsession.path, "Options.R") - api.path <- file.path(rsession.path, "Api.R") - modules.path <- file.path(rsession.path, "modules") - sessionJobs.path <- file.path(rsession.path, "modules/SessionJobs.R") - setwd(modules.path) - source(tools.path, local = TRUE) - source(sessionJobs.path, local = TRUE) - source(api.path, local = TRUE) - source(options.path, local = TRUE) - sapply(Filter(function(s) s != "SessionCompileAttributes.R" & s != "SessionPlots.R", - list.files(modules.path, pattern = ".*\\.r$", ignore.case = TRUE)), - function(x) { source(file.path(modules.path, x), local = TRUE) } ) - .rs.getProjectDirectory <<- function() project.dir - options(BuildTools.Check = NULL) - }, finally = { - setwd(current.wd) - }) -} - -.jetbrains$setRStudioAPIEnabled <<- function(isEnabled) { - unlockBinding(".Platform", baseenv()) - if (isEnabled) { - .Platform$GUI <<- "RStudio" - } else { - .Platform$GUI <<- .jetbrains$defaultPlatformGUI - } - lockBinding(".Platform", baseenv()) -} - -.jetbrains$updatePackageEvents <<- function() { - packageNames <- base::list.dirs(.libPaths(), full.names = FALSE, recursive = FALSE) - sapply(packageNames, function(name) { - if (!(name %in% .rs.jbHookedPackages)) { - loadEventName = packageEvent(name, "onLoad") - onPackageLoaded <- function(name, ...) { - .rs.reattachS3Overrides() - } - setHook(loadEventName, onPackageLoaded, action = "append") - .rs.setVar("jbHookedPackages", append(.rs.jbHookedPackages, name)) - } - }) -} - -.jetbrains$toSystemIndependentPath <<- function(path) { - gsub("\\\\", "/", path) -} - -.jetbrains$createTempDirectory <<- function(suffix) { - pattern <- paste0("jetbrains_", suffix) - path <- tempfile(pattern = pattern) - dir.create(path) - .jetbrains$toSystemIndependentPath(path) -} - -# Fallback values -.jetbrains$chunkOutputDir <<- "." -.jetbrains$externalImageDir <<- "." -.jetbrains$externalImageCounter <<- 0 - -# Note: used in "NotebookHtmlWidgets.R" of RSession -.jetbrains$getNextExternalImagePath <<- function(path) { - snapshot.count <- .Call(".jetbrains_ther_device_snapshot_count") - if (is.null(snapshot.count)) { - snapshot.count <- 0 - } - .jetbrains$externalImageCounter <<- .jetbrains$externalImageCounter + 1 # Also ensure it's > 0 - base.name <- paste0("image_", snapshot.count - 1, "_", .jetbrains$externalImageCounter, ".", tools::file_ext(path)) - file.path(.jetbrains$externalImageDir, base.name) -} - -.jetbrains$runBeforeChunk <<- function(report.text, chunk.text) { - .rs.evaluateRmdParams(report.text) - opts <- .rs.evaluateChunkOptions(chunk.text) - output.dir <- .jetbrains$createTempDirectory("chunk_outputs") - .jetbrains$chunkOutputDir <<- output.dir - data.dir <- file.path(output.dir, "data") - html.lib.dir <- file.path(output.dir, "lib") - image.dir <- file.path(output.dir, "images") - external.image.dir <- file.path(output.dir, "external-images") - dir.create(image.dir, showWarnings = FALSE) - dir.create(external.image.dir, showWarnings = FALSE) - dir.create(html.lib.dir, showWarnings = FALSE) - dir.create(data.dir, showWarnings = FALSE) - - .jetbrains$externalImageDir <<- external.image.dir - .jetbrains$externalImageCounter <<- 0 - .rs.initHtmlCapture(output.dir, html.lib.dir, opts) - .rs.initDataCapture(data.dir, opts) - if (!.rs.hasVar("jbHookedPackages")) { - .rs.setVar("jbHookedPackages", character()) - } - .jetbrains$updatePackageEvents() # Note: supposed to be useless when packages are getting installed within chunk but for my machine it's OK -} - -.jetbrains$runAfterChunk <<- function() { - .rs.releaseHtmlCapture() - .rs.releaseDataCapture() - unlink(.jetbrains$chunkOutputDir, recursive = TRUE) -} - -.jetbrains$getChunkOutputPaths <<- function() { - relative.paths <- list.files(.jetbrains$chunkOutputDir, recursive = TRUE, include.dirs = FALSE, full.names = FALSE) - c(.jetbrains$chunkOutputDir, relative.paths) -} - -.jetbrains$getChunkOutputFullPath <<- function(relative.path) { - file.path(.jetbrains$chunkOutputDir, relative.path) -} - -.jetbrains$findInheritorNamedArguments <<- function(x) { - ignoreErrors <- function(expr) { - as.list(tryCatch(expr, error = function(e) { })) - } - - defenv <- if (!is.na(w <- .knownS3Generics[x])) { - asNamespace(w) - } else { - genfun <- get(x, mode = "function") - if (.isMethodsDispatchOn() && methods::is(genfun, "genericFunction")) { - genfun <- methods::finalDefaultMethod(genfun@default) - } - if (typeof(genfun) == "closure") environment(genfun) - else .BaseNamespaceEnv - } - s3_table <- get(".__S3MethodsTable__.", envir = defenv) - - getS3Names <- function(row) { - functionName <- row[["functionName"]] - from <- row[["from"]] - envir <- tryCatch(as.environment(from), error = function(e) NULL) - if (startsWith(from, "registered S3method")) { - envir <- s3_table - } - if (is.null(envir)) { - envir <- tryCatch(as.environment(paste0("package:", from)), error = function(e) .GlobalEnv) - } - names(formals(get(functionName, mode = "function", envir = envir))) - } - - s4 <- ignoreErrors(names(formals(getMethod(x)))) - if (utils:::findGeneric(x, parent.frame(), warnS4only = FALSE) == "") { - s3 <- NULL - } - else { - s3Info <- attr(.S3methods(x), "info") - s3Info[, "functionName"] <- rownames(s3Info) - s3 <- ignoreErrors(apply(s3Info[, c("functionName", "from")], 1, getS3Names)) - } - unique(unlist(c(s4, s3, names(formals(x))))) -} - -.jetbrains$printAllPackagesToFile <<- function(repo.urls, output.path) { - remove.newlines <- function(s) { - gsub("\r?\n|\r", " ", s) - } - - old.repos <- getOption("repos") - options(repos = repo.urls) - sink(output.path) - p <- available.packages()[, c("Package", "Repository", "Version", "Depends")] - p <- as.data.frame(p) - p$Depends <- sapply(p$Depends, remove.newlines) - with(p, cat(paste(paste(Package, Repository, Version, sep = " "), Depends, sep = "\t"), sep = "\n")) - sink() - options(repos = old.repos) -} - -.jetbrains$getDefaultRepositories <<- function() { - p <- file.path(Sys.getenv("HOME"), ".R", "repositories") - if (!file.exists(p)) - p <- file.path(R.home("etc"), "repositories") - a <- tools:::.read_repositories(p) - a[, "URL"] -} - -.jetbrains$printCranMirrorsToFile <<- function(output.path) { - sink(output.path) - mirrors <- getCRANmirrors()[, c('Name', 'URL')] - with(mirrors, cat(paste(Name, URL, sep = "\t"), sep = "\n")) - sink() -} - -.jetbrains$printInstalledPackagesToFile <<- function(output.path) { - sink(output.path) - versions <- as.data.frame(installed.packages()[, c("Package", "Version", "Priority", "LibPath")]) - with(versions, cat(paste(LibPath, Package, Version, Priority, sep = "\t"), sep = "\n")) - sink() -} - -.jetbrains$initGraphicsDevice <<- function(width, height, resolution, in.memory) { - path <- .jetbrains$createSnapshotGroup() - .Call(".jetbrains_ther_device_init", path, width, height, resolution, in.memory) - path -} - -.jetbrains$findStoredSnapshot <<- function(directory, number) { - pattern <- paste0("^snapshot_normal_", number, "_") # Note: trailing underscore will cut off remaining digits if any - snapshots <- list.files(directory, pattern = pattern, full.names = FALSE) - if (length(snapshots) > 0) { - return(snapshots[1]) - } else { - return(NULL) - } -} - -.jetbrains$createSnapshotGroup <<- function() { - .jetbrains$createTempDirectory("snapshot_group") -} - -.jetbrains$shutdownGraphicsDevice <<- function() { - path <- .Call(".jetbrains_ther_device_shutdown") - if (!is.null(path)) { - unlink(path, recursive = TRUE) - } - NULL -} - -.jetbrains$saveRecordedPlotToFile <<- function(snapshot, output.path) { - .jetbrains.recorded.snapshot <- snapshot - save(.jetbrains.recorded.snapshot, file = output.path) -} - -.jetbrains$replayPlotFromFile <<- function(input.path) { - load(input.path) - plot <- .jetbrains.recorded.snapshot - - # restore native symbols for R >= 3.0 - rVersion <- getRversion() - if (rVersion >= "3.0") { - for (i in 1:length(plot[[1]])) { - # get the symbol then test if it's a native symbol - symbol <- plot[[1]][[i]][[2]][[1]] - if ("NativeSymbolInfo" %in% class(symbol)) { - # determine the dll that the symbol lives in - name = if (!is.null(symbol$package)) symbol$package[["name"]] else symbol$dll[["name"]] - pkgDLL <- getLoadedDLLs()[[name]] - - # reconstruct the native symbol and assign it into the plot - nativeSymbol <- getNativeSymbolInfo(name = symbol$name, PACKAGE = pkgDLL, withRegistrationInfo = TRUE); - plot[[1]][[i]][[2]][[1]] <- nativeSymbol; - } - } - } - - # Replay obtained plot - suppressWarnings(grDevices::replayPlot(plot, reloadPkgs=TRUE)) -} - -.jetbrains$dropRecordedSnapshots <<- function(device.number, from, to) { - for (i in from:to) { - name <- paste0("recordedSnapshot_", device.number, "_", i) - if (exists(name, envir = .jetbrains)) { - assign(name, NULL, .jetbrains) - } - } -} - -.jetbrains$getLoadedS4ClassInfos <<- function() { - classTable <- methods:::.classTable - infos <- lapply(names(classTable), function(className) { - class <- classTable[[className]] - if (inherits(class, "classRepresentation")) class - }) - Filter(function(x) !is.null(x), infos) -} - -.jetbrains$isObjectFromR6 <<- function(object) { - return(is.R6(object)) -} - -.jetbrains$getR6ClassName <<- function(x) { - return(class(x)[1]) -} - -.jetbrains$getR6ClassInheritanceTree <<- function(x) { - return(head(class(x), -1)[-1]) -} - -.jetbrains$getR6ClassDefMembers <<- function(x) { - return(names(x)[-1]) -} - -.jetbrains$getSysEnv <<- function(env_name, flags) { - s <- Sys.getenv(env_name) - s <- strsplit(s, .Platform$path.sep)[[1]] - if ("--normalize-path" %in% flags) { - s <- sapply(s, function (p) { - normalized <- normalizePath(p) - .jetbrains$toSystemIndependentPath(normalized) - }) - } - s -} - -.jetbrains$loadLibraryPath <<- function() { - res <- NULL - for (path in .libPaths()) { - res <- c(res, list(path, file.access(path, 2) == 0)) - } - res -} - -.jetbrains$loadInstalledPackages <<- function() { - versions <- as.data.frame(installed.packages()[, c("Package", "Version", "Priority", "LibPath")]) - canonicalPackagePaths <- data.frame(CanonicalPath = apply(versions, 1, function(row) { - normalizePath(file.path(row["LibPath"], row["Package"])) - })) - description <- data.frame("Title" = I(lapply(versions[, "Package"], function(x) packageDescription(x, fields = "Title"))), - "URL" = I(lapply(versions[, "Package"], function(x) packageDescription(x, fields = "URL")))) - cbind(versions, canonicalPackagePaths, description) -} - -.jetbrains$unloadLibrary <<- function(package.name, with.dynamic.library) { - resource.name <- paste0("package:", package.name) - detach(resource.name, unload = TRUE, character.only = TRUE) - if (with.dynamic.library) { - .jetbrains$unloadDynamicLibrary(package.name) - } -} - -.jetbrains$unloadDynamicLibrary <<- function(package.name) { - if (.jetbrains$isDynamicLibraryLoaded(package.name)) { - pd.file <- attr(packageDescription(package.name), "file") - lib.path <- sub("/Meta.*", "", pd.file) - library.dynam.unload(package.name, libpath = lib.path) - } -} - -.jetbrains$isDynamicLibraryLoaded <<- function(package.name) { - for (lib in .dynLibs()) { - name <- lib[[1]] - if (name == package.name) { - return(TRUE) - } - } - FALSE -} - -.jetbrains$previewDataImportResult <<- NULL - -.jetbrains$previewDataImport <<- function(path, mode, row.count, importOptions) { - result <- if (mode != "base") { - .jetbrains$previewAdvancedDataImport(path, mode, row.count, importOptions) - } else { - .jetbrains$previewBaseDataImport(path, row.count, importOptions) - } - .jetbrains$previewDataImportResult <<- result - if (is.null(result)) { - return(NULL) - } - result$parsingErrors -} - -.jetbrains$commitDataImport <<- function(path, mode, importOptions) { - .jetbrains$previewDataImport(path, mode, NULL, importOptions) - result <- .jetbrains$previewDataImportResult - .jetbrains$previewDataImportResult <<- NULL - if (is.null(result)) { - return(NULL) - } - result$data -} - -.jetbrains$previewAdvancedDataImport <<- function(path, mode, row.count, importOptions) { - importOptions$openDataViewer <- FALSE - importOptions$importLocation <- path - importOptions$modelLocation <- NULL - importOptions$mode <- mode - if (!is.null(row.count)) { - importOptions$maxRows <- row.count - - # Excel's special - if (is.null(importOptions$nMax) || importOptions$nMax > row.count) { - importOptions$nMax <- row.count - } - } - .rs.previewDataImport(importOptions) -} - -.jetbrains$previewBaseDataImport <<- function(path, row.count, importOptions) { - if (!is.null(row.count)) { - importOptions$nrows <- row.count - } - importOptions$file <- path - tryCatch({ - # try to use read.csv directly if possible (since this is a common case - # and since LibreOffice spreadsheet exports produce files unparsable - # by read.table). check Workspace.makeCommand if we want to deduce - # other more concrete read calls. - data <- if (identical(importOptions$sep, ",") && identical(importOptions$dec, ".") && identical(importOptions$quote, "\"")) { - importOptions$sep <- NULL - importOptions$dec <- NULL - importOptions$quote <- NULL - do.call(read.csv, importOptions) - } else { - do.call(read.table, importOptions) - } - return(list(data = data, parsingErrors = 0)) - }, error=function(e) { - data <- data.frame(Error = e$message) - parsingErrors <- if (!is.null(row.count)) row.count else 0 - return(list(data = data, parsingErrors = parsingErrors)) - }) -} - -.jetbrains$convertRoxygenToHTML <<- function(functionName, text) { - text <- format( - roxygen2:::roclet_process.roclet_rd(, roxygen2:::parse_text(text), base_path = ".")[[paste0(functionName, ".Rd")]]) - links = gsub("^\\.\\./\\.\\./", "/library/", tools::findHTMLlinks()) - text <- utils::capture.output(tools::Rd2HTML(textConnection(text), Links = links)) - return(paste(text, collapse = "\n")) -} - -local({ - handlersEnv <- tools:::.httpd.handlers.env - handlersEnv$jb_get_file <- function(path, query, ...) { - prefix <- "/custom/jb_get_file/" - file <- substr(path, nchar(prefix) + 1, nchar(path)) - if (!file.exists(file)) { - return(list(payload = paste0("No such file ", file))) - } - mimeType <- function(path) { - ext <- strsplit(path, ".", fixed = TRUE)[[1]] - if (n <- length(ext)) ext <- ext[n] else "" - switch(ext, css = "text/css", gif = "image/gif", jpg = "image/jpeg", png = "image/png", svg = "image/svg+xml", - html = "text/html", pdf = "application/pdf", eps = , ps = "application/postscript", - sgml = "text/sgml", xml = "text/xml", "text/plain") - } - return(list(file = file, `content-type` = mimeType(path))) - } -}) - -local({ - env <- as.environment("package:utils") - unlockBinding("View", env) - env$View <- function(x, title = paste(deparse(substitute(x)), collapse = " ")) - invisible(.Call(".jetbrains_View", substitute(x), parent.frame(), title)) - lockBinding("View", env) -}) - -if (.Platform$OS.type == "unix" && !("UTF-8" %in% localeToCharset(Sys.getlocale("LC_CTYPE")))) { - if (grepl("^darwin", R.version$os)) { - Sys.setlocale("LC_CTYPE", "UTF-8") - } else { - Sys.setlocale("LC_CTYPE", "C.UTF-8") - } -} - -options(warn = 1) -options(demo.ask = TRUE); -assign(".Last.sys", function() .Call(".jetbrains_quitRWrapper"), envir = baseenv()) From f44a804427b31691c70c2874b656ec68a4081bd6 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Thu, 6 May 2021 20:46:09 +0300 Subject: [PATCH 48/52] add separate `method()`` and `field` completion --- grammars/library_summary.proto | 12 ++++++-- .../r/editor/RCompletionContributor.kt | 30 +++++++++++++++---- .../completion/RLookupElementFactory.kt | 8 +++++ .../r/roxygen/RoxygenCompletionContributor.kt | 14 +++++++++ 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/grammars/library_summary.proto b/grammars/library_summary.proto index 90a370167..f30b59e3b 100644 --- a/grammars/library_summary.proto +++ b/grammars/library_summary.proto @@ -38,7 +38,12 @@ message RLibrarySymbol { } message R6ClassRepresentation { - message R6ClassMember { + message R6ClassField { + string name = 1; + bool isPublic = 2; + } + + message R6ClassMethod { string name = 1; bool isPublic = 2; } @@ -47,8 +52,9 @@ message RLibrarySymbol { string name = 1; } - repeated string superClasses = 2; - repeated R6ClassMember members = 3; + repeated string superClasses = 1; + repeated R6ClassField fields = 2; + repeated R6ClassMethod methods = 3; repeated R6ClassActiveBinding activeBindings = 4; } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index dc0743f41..24b9e167d 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -113,17 +113,22 @@ class RCompletionContributor : CompletionContributor() { val info = file.runtimeInfo val memberAccess = PsiTreeUtil.getParentOfType(position, RMemberExpression::class.java) ?: return val leftExpr = memberAccess.leftExpr ?: return + + val shownNames = addStaticRuntimeCompletionDependsOfFile(memberAccess, file, result, MemberStaticRuntimeCompletionProvider) + if (info != null) { val noCalls = PsiTreeUtil.processElements(leftExpr) { it !is RCallExpression } if (noCalls) { - info.loadObjectNames(leftExpr.text).forEach { result.consume(rCompletionElementFactory.createNamespaceAccess(it)) } + info.loadObjectNames(leftExpr.text).forEach { + if (!shownNames.contains(it)) { + result.consume(rCompletionElementFactory.createNamespaceAccess(it)) + } + } } } for (extension in RLibrarySupportProvider.EP_NAME.extensions) { extension.completeMembers(leftExpr, rCompletionElementFactory, result) } - - addStaticRuntimeCompletionDependsOfFile(memberAccess, file, result, MemberStaticRuntimeCompletionProvider) } private object MemberStaticRuntimeCompletionProvider : RStaticRuntimeCompletionProvider { @@ -170,9 +175,8 @@ class RCompletionContributor : CompletionContributor() { if (r6Member.name in shownNames) continue when (r6Member){ - // TODO fix specific rCompletionElementFactory is R6ClassField -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) - is R6ClassMethod -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + is R6ClassMethod -> result.consume(rCompletionElementFactory.createFunctionLookupElement(r6Member.name)) } shownNames.add(r6Member.name) @@ -752,6 +756,18 @@ class RCompletionContributor : CompletionContributor() { context.editor.caretModel.moveCaretRelatively(relativeCaretOffset, 0, false, false, false) } } + + override fun getInsertHandlerForLookupString(lookupString: String): InsertHandler { + return InsertHandler { context, _ -> + val document = context.document + val findParentheses = findParentheses(document.text, context.tailOffset) + if (findParentheses == null) { + document.insertString(context.tailOffset, "()") + } + val relativeCaretOffset = 2 + (findParentheses ?: 0) + context.editor.caretModel.moveCaretRelatively(relativeCaretOffset, 0, false, false, false) + } + } } private fun getFileNamePrefix(filepath: String): String? { @@ -797,7 +813,7 @@ class RCompletionContributor : CompletionContributor() { private fun addStaticRuntimeCompletionDependsOfFile(psiElement: T, file: PsiFile, result: CompletionResultSet, - provider: RStaticRuntimeCompletionProvider) { + provider: RStaticRuntimeCompletionProvider) : Set { val runtimeInfo = file.runtimeInfo val shownNames = HashSet() if (file.getUserData(RConsoleView.IS_R_CONSOLE_KEY) == true) { @@ -809,6 +825,8 @@ class RCompletionContributor : CompletionContributor() { runtimeInfo?.let { provider.addCompletionFromRuntime(psiElement, shownNames, result, it) } } } + + return shownNames } private fun addArgumentValueCompletion(position: PsiElement, result: CompletionResultSet) { diff --git a/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt b/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt index c42ebee71..5ca3ae415 100644 --- a/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt +++ b/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt @@ -68,10 +68,12 @@ data class TableManipulationColumnLookup(val column: TableColumnInfo) { interface RLookupElementInsertHandler { fun getInsertHandlerForAssignment(assignment: RAssignmentStatement): InsertHandler + fun getInsertHandlerForLookupString(lookupString: String): InsertHandler } class REmptyLookupElementInsertHandler : RLookupElementInsertHandler { override fun getInsertHandlerForAssignment(assignment: RAssignmentStatement) = BasicInsertHandler() + override fun getInsertHandlerForLookupString(lookupString: String) = BasicInsertHandler() } class RLookupElementFactory(private val functionInsertHandler: RLookupElementInsertHandler = REmptyLookupElementInsertHandler(), @@ -99,6 +101,12 @@ class RLookupElementFactory(private val functionInsertHandler: RLookupElementIns if (isLocal) VARIABLE_GROUPING else GLOBAL_GROUPING) } + fun createFunctionLookupElement(lookupString: String): LookupElement { + val icon = AllIcons.Nodes.Function + return createLookupElementWithGrouping(RLookupElement(lookupString, false, icon), + functionInsertHandler.getInsertHandlerForLookupString(lookupString), + GLOBAL_GROUPING) + } fun createNamespaceAccess(lookupString: String): LookupElement { val insertHandler = InsertHandler { context, _ -> diff --git a/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt b/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt index 261f33429..fc20bb01b 100755 --- a/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt +++ b/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt @@ -109,6 +109,14 @@ class RoxygenCompletionContributor : CompletionContributor() { document.insertString(offset, "()") context.editor.caretModel.moveCaretRelatively(4, 0, false, false, false) } + + override fun getInsertHandlerForLookupString(lookupString: String) = InsertHandler { context, _ -> + val offset = context.tailOffset + val document = context.document + insertSpaceAfterLinkIfNeeded(document, offset) + document.insertString(offset, "()") + context.editor.caretModel.moveCaretRelatively(4, 0, false, false, false) + } } private object RoxygenConstantLinkInsertHandler : RLookupElementInsertHandler { @@ -117,6 +125,12 @@ class RoxygenCompletionContributor : CompletionContributor() { insertSpaceAfterLinkIfNeeded(document, context.tailOffset) context.editor.caretModel.moveCaretRelatively(2, 0, false, false, false) } + + override fun getInsertHandlerForLookupString(lookupString: String) = InsertHandler { context, _ -> + val document = context.document + insertSpaceAfterLinkIfNeeded(document, context.tailOffset) + context.editor.caretModel.moveCaretRelatively(2, 0, false, false, false) + } } private fun insertSpaceAfterLinkIfNeeded(document: Document, tailOffset: Int) { From 2ccb6766b367a1b8e38b17d7294d6926a2f8beea Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 8 May 2021 14:43:19 +0300 Subject: [PATCH 49/52] refactor stub-index classes for library r6 and s4 --- .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 4 +- .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 6 +-- .../R6SetClassMembersContextProvider.kt | 6 +-- .../r/classes/s4/RS4ClassInfoUtil.kt | 5 +- .../r/editor/RCompletionContributor.kt | 15 +++--- .../UnresolvedReferenceInspection.kt | 3 +- .../s4/InstanceOfVirtualS4ClassInspection.kt | 4 +- .../r/psi/RCallExpressionElementType.kt | 7 +-- .../classes/LibraryClassNameIndexBase.kt | 42 ----------------- .../classes/LibraryClassNameIndexProvider.kt | 12 ----- .../classes/LibraryClassNameIndexUtil.kt | 47 +++++++++++++++++++ .../r/psi/stubs/classes/R6ClassNameIndex.kt | 40 ++++++++++++---- .../r/psi/stubs/classes/RS4ClassNameIndex.kt | 40 ++++++++++++---- .../psi/RSkeletonCallExpressionElementType.kt | 5 +- .../classes/R6ClassCompletionTest.kt | 22 ++------- 15 files changed, 139 insertions(+), 119 deletions(-) delete mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexBase.kt delete mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt create mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt index 84ddd4f53..0a77015da 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -14,7 +14,7 @@ import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.impl.RMemberExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex object R6ClassInfoUtil { const val R6PackageName = "R6" @@ -117,7 +117,7 @@ object R6ClassInfoUtil { if (allSuperClasses != null) { return (r6ClassInfo.fields + r6ClassInfo.methods + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.fields + it.methods + it.activeBindings } + R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.fields + it.methods + it.activeBindings } }).distinctBy { it.name } } diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt index bbcbef581..e02273ccb 100644 --- a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -14,7 +14,7 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.isFunctionFromLibrarySoft import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex object R6ClassPsiUtil { @@ -35,7 +35,7 @@ object R6ClassPsiUtil { val r6ClassInfo = run findMemberDefinition@ { (classNamesHierarchy)?.reversed()?.forEach { - val r6ClassInfo = LibraryClassNameIndexProvider.R6ClassNameIndex.findClassInfos(it, project, callSearchScope).firstOrNull() + val r6ClassInfo = R6ClassNameIndex.findClassInfos(it, project, callSearchScope).firstOrNull() if (r6ClassInfo != null) { if (r6ClassInfo.containsMember(dependantIdentifier.name)) return@findMemberDefinition r6ClassInfo @@ -44,7 +44,7 @@ object R6ClassPsiUtil { } as R6ClassInfo? r6ClassInfo ?: return null - val r6ClassDefinitionCall = LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(r6ClassInfo.className, project, callSearchScope).firstOrNull() + val r6ClassDefinitionCall = R6ClassNameIndex.findClassDefinitions(r6ClassInfo.className, project, callSearchScope).firstOrNull() val argumentInfo = getClassDefinitionArgumentInfo(r6ClassDefinitionCall) ?: return null val publicMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPublic) diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt index 48ed88167..055a0dce8 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt @@ -14,7 +14,7 @@ import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.api.RMemberExpression import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex sealed class R6SetClassMembersContext : ILibraryClassContext { override val functionName = R6ClassInfoUtil.functionSet @@ -61,8 +61,8 @@ class R6SetClassMembersContextProvider : R6ContextProvider // Collect slots from super classes and data slot if needed - LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { + RS4ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { val superClassSlots = it.slots when { // methods:::.InhSlotNames @@ -89,7 +88,7 @@ object RS4ClassInfoUtil { return s4ClassInfo.superClasses.flatMap { superClassName -> // R keeps all superclasses together in the list, not as a tree // So, I don't see any reason to do anything else - val parentSuperClasses = LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { + val parentSuperClasses = RS4ClassNameIndex.findClassDefinitions(superClassName, project, callSearchScope).flatMap { getAllAssociatedSuperClasses(it) } listOf(superClassName) + parentSuperClasses diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 24b9e167d..c1683877e 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -37,7 +37,8 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator import org.jetbrains.r.rinterop.RValueFunction import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement @@ -156,8 +157,8 @@ class RCompletionContributor : CompletionContributor() { result: CompletionResultSet): Boolean { val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(psiElement) if (className != null) { - LibraryClassNameIndexProvider.R6ClassNameIndex.findClassDefinitions(className, psiElement.project, - RSearchScopeUtil.getScope(psiElement)).forEach { + R6ClassNameIndex.findClassDefinitions(className, psiElement.project, + RSearchScopeUtil.getScope(psiElement)).forEach { return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it) + R6ClassKeywordsProvider.predefinedClassMethods, shownNames, result) } } @@ -223,8 +224,8 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = RS4ClassInfoUtil.getAssociatedClassName(call) ?: return@forEach - LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, - RSearchScopeUtil.getScope(psiElement)).forEach { + RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, + RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(RS4ClassInfoUtil.getAllAssociatedSlots(it), shownNames, result) } } @@ -534,7 +535,7 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(psiElement: RCallExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope( + RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope( psiElement)).singleOrNull()?.let { definition -> RS4ClassInfoUtil.getAllAssociatedSlots(definition).forEach { result.consume(RLookupElementFactory.createNamedArgumentLookupElement(it.name, it.type, SLOT_NAME_PRIORITY)) @@ -567,7 +568,7 @@ class RCompletionContributor : CompletionContributor() { val runtimeInfo = file.runtimeInfo val loadedPackages = runtimeInfo?.loadedPackages?.keys val shownNames = HashSet() - LibraryClassNameIndexProvider.RS4ClassNameIndex.processAllClassInfos(project, scope, Processor { (declaration, info) -> + RS4ClassNameIndex.processAllS4ClassInfos(project, scope, Processor { (declaration, info) -> if (omitVirtual && info.isVirtual) return@Processor true if (nameToOmit != info.className) { result.addS4ClassName(classNameExpression, declaration, info, shownNames, loadedPackages) diff --git a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt index 29e83e4b2..d78b73d76 100644 --- a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt +++ b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt @@ -20,7 +20,6 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RReferenceBase import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class UnresolvedReferenceInspection : RInspection() { @@ -66,7 +65,7 @@ class UnresolvedReferenceInspection : RInspection() { val classNameExpr = element.argumentList.expressionList.firstOrNull() as? RStringLiteralExpression ?: return val className = classNameExpr.name val packageNames = className?.let { - LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassInfos(it, element.project, RSearchScopeUtil.getScope(element)) + RS4ClassNameIndex.findClassInfos(it, element.project, RSearchScopeUtil.getScope(element)) }?.map { it.packageName }?.filter { it.isNotBlank() } ?: return if (packageNames.isEmpty() || packageNames.any { it in loadedPackages }) return registerMissingPackages(classNameExpr, className, packageNames, runtimeInfo) diff --git a/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt b/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt index d4588e649..b39e9aa39 100644 --- a/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt +++ b/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt @@ -15,7 +15,7 @@ import org.jetbrains.r.psi.api.RStringLiteralExpression import org.jetbrains.r.psi.api.RVisitor import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class InstanceOfVirtualS4ClassInspection : RInspection() { override fun getDisplayName() = RBundle.message("inspection.virtual.s4class.instance.name") @@ -29,7 +29,7 @@ class InstanceOfVirtualS4ClassInspection : RInspection() { if (!call.isFunctionFromLibrary("new", "methods")) return val classNameExpression = call.argumentList.expressionList.firstOrNull() as? RStringLiteralExpression ?: return val className = classNameExpression.name ?: return - val infos = LibraryClassNameIndexProvider.RS4ClassNameIndex.findClassInfos(className, call.project, RSearchScopeUtil.getScope(call)) + val infos = RS4ClassNameIndex.findClassInfos(className, call.project, RSearchScopeUtil.getScope(call)) if (infos.isNotEmpty() && infos.all { it.isVirtual }) { myProblemHolder.registerProblem(classNameExpression, RBundle.message("inspection.virtual.s4class.instance.description", className), diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index f9feda4c6..34ac169d7 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -18,7 +18,8 @@ import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.stubs.RCallExpressionStub import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl import org.jetbrains.r.psi.stubs.RStubElementType -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import java.io.IOException class RCallExpressionElementType(debugName: String) : RStubElementType(debugName) { @@ -50,8 +51,8 @@ class RCallExpressionElementType(debugName: String) : RStubElementType : StringStubIndexExtension() { - abstract val classKey: StubIndexKey - - abstract fun callProcessingForDeclaration(rCallExpression: RCallExpression, processor: Processor>) : Boolean - abstract fun getClassInfoFromRCallExpression(rCallExpression: RCallExpression) : TClassInfo? - - fun processAllClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { - val stubIndex = StubIndex.getInstance() - stubIndex.processAllKeys(classKey, project) { key -> - stubIndex.processElements(classKey, key, project, scope, RCallExpression::class.java) { - declaration -> callProcessingForDeclaration(declaration, processor) - } - } - } - - fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { - return StubIndex.getElements(classKey, name, project, scope, RCallExpression::class.java).mapNotNull { getClassInfoFromRCallExpression(it) } - } - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(classKey, name, project, scope, RCallExpression::class.java) - } - - fun sink(sink: IndexSink, name: String) { - sink.occurrence(classKey, name) - } -} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt deleted file mode 100644 index badb3e005..000000000 --- a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexProvider.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -package org.jetbrains.r.psi.stubs.classes - -class LibraryClassNameIndexProvider { - companion object { - val RS4ClassNameIndex = RS4ClassNameIndex() - val R6ClassNameIndex = R6ClassNameIndex() - } -} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt new file mode 100644 index 000000000..7512c316e --- /dev/null +++ b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.psi.stubs.classes + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndex +import com.intellij.psi.stubs.StubIndexKey +import com.intellij.util.Processor +import org.jetbrains.r.classes.s4.RS4ClassInfo +import org.jetbrains.r.psi.api.RCallExpression + +abstract class LibraryClassNameIndexUtil : StringStubIndexExtension() { + companion object{ + fun processClassInfos(project: Project, + scope: GlobalSearchScope?, + indexKey: StubIndexKey, + indexProcessFunc: RCallExpression.() -> Boolean) { + val stubIndex = StubIndex.getInstance() + stubIndex.processAllKeys(indexKey, project) { key -> + stubIndex.processElements(indexKey, key, project, scope, RCallExpression::class.java) { + declaration -> indexProcessFunc(declaration) + } + } + } + + fun findClassInfos(indexKey: StubIndexKey, + mapClassInfoFunction: RCallExpression.() -> T?, + name: String, + project: Project, + scope: GlobalSearchScope?): List { + return StubIndex.getElements(indexKey, name, project, scope, RCallExpression::class.java).mapNotNull { mapClassInfoFunction(it) } + } + + fun findClassDefinitions(indexKey: StubIndexKey, name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(indexKey, name, project, scope, RCallExpression::class.java) + } + + fun sink(indexKey: StubIndexKey, sink: IndexSink, name: String) { + sink.occurrence(indexKey, name) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt index 65c156163..8f19e3a71 100644 --- a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt @@ -4,24 +4,44 @@ package org.jetbrains.r.psi.stubs.classes +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class R6ClassNameIndex : LibraryClassNameIndexBase() { - override val classKey = StubIndexKey.createIndexKey("R.r6class.shortName") - +class R6ClassNameIndex : StringStubIndexExtension() { override fun getKey(): StubIndexKey { - return classKey + return KEY } - override fun callProcessingForDeclaration(rCallExpression: RCallExpression, - processor: Processor>): Boolean { - return rCallExpression.associatedR6ClassInfo?.let { processor.process(rCallExpression to it) } ?: true - } + companion object { + private val KEY = StubIndexKey.createIndexKey("R.r6class.shortName") + + fun processAllR6ClassInfos(project: Project, + scope: GlobalSearchScope?, + processor: Processor>) { + val processingFunction = fun (declaration: RCallExpression) : Boolean = + declaration.associatedR6ClassInfo?.let { processor.process(declaration to it) } ?: true + + return LibraryClassNameIndexUtil.processClassInfos(project, scope, KEY, processingFunction) + } + + fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { + val mapFunction = fun (declaration: RCallExpression) : R6ClassInfo? = declaration.associatedR6ClassInfo + + return LibraryClassNameIndexUtil.findClassInfos(KEY, mapFunction, name, project, scope) + } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return LibraryClassNameIndexUtil.findClassDefinitions(KEY, name, project, scope) + } - override fun getClassInfoFromRCallExpression(rCallExpression: RCallExpression): R6ClassInfo? { - return rCallExpression.associatedR6ClassInfo + fun sink(sink: IndexSink, name: String) { + return LibraryClassNameIndexUtil.sink(KEY, sink, name) + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt index 6b3559d9a..50ca3d70c 100644 --- a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt +++ b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt @@ -4,24 +4,44 @@ package org.jetbrains.r.psi.stubs.classes +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.Processor import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression -class RS4ClassNameIndex : LibraryClassNameIndexBase() { - override val classKey = StubIndexKey.createIndexKey("R.s4class.shortName") - +class RS4ClassNameIndex : StringStubIndexExtension() { override fun getKey(): StubIndexKey { - return classKey + return KEY } - override fun callProcessingForDeclaration(rCallExpression: RCallExpression, - processor: Processor>): Boolean { - return rCallExpression.associatedS4ClassInfo?.let { processor.process(rCallExpression to it) } ?: true - } + companion object { + private val KEY = StubIndexKey.createIndexKey("R.s4class.shortName") + + fun processAllS4ClassInfos(project: Project, + scope: GlobalSearchScope?, + processor: Processor>) { + val processingFunction = fun (declaration: RCallExpression) : Boolean = + declaration.associatedS4ClassInfo?.let { processor.process(declaration to it) } ?: true + + return LibraryClassNameIndexUtil.processClassInfos(project, scope, KEY, processingFunction) + } + + fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { + val mapFunction = fun (declaration: RCallExpression) : RS4ClassInfo? = declaration.associatedS4ClassInfo + + return LibraryClassNameIndexUtil.findClassInfos(KEY, mapFunction, name, project, scope) + } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return LibraryClassNameIndexUtil.findClassDefinitions(KEY, name, project, scope) + } - override fun getClassInfoFromRCallExpression(rCallExpression: RCallExpression): RS4ClassInfo? { - return rCallExpression.associatedS4ClassInfo + fun sink(sink: IndexSink, name: String) { + return LibraryClassNameIndexUtil.sink(KEY, sink, name) + } } } \ No newline at end of file diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt index 09a086097..4cc91419c 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt @@ -15,7 +15,6 @@ import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.stubs.RStubElementType -import org.jetbrains.r.psi.stubs.classes.LibraryClassNameIndexProvider import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex @@ -50,7 +49,7 @@ class RSkeletonCallExpressionElementType : RStubElementType) - """.trimIndent(), "MyClass" to "string", containedInSuggestions = true) + """.trimIndent(), "MyClass" to "string") } fun testSetMemberVisibilityModifierCompletionToUserClass() { doTest(""" MyClass <- R6Class("MyClass", list(someField = 0)) MyClass${'$'}set() - """.trimIndent(), "\"active\"" to "string", "\"public\"" to "string", "\"private\"" to "string", containedInSuggestions = true) + """.trimIndent(), "\"active\"" to "string", "\"public\"" to "string", "\"private\"" to "string") } fun testConsoleMembersSuggestion() { @@ -84,7 +84,7 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } )) obj <- MyClass${'$'}new() """.trimIndent()) - doTest("obj${'$'}", "clone" to "", "random" to "", "someField" to "", "someMethod" to "", withRuntimeInfo = true, inConsole = true, containedInSuggestions = true) + doTest("obj${'$'}", "clone" to "", "random" to "", "someField" to "", "someMethod" to "", withRuntimeInfo = true, inConsole = true) } private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { @@ -96,8 +96,6 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { private fun doTest(text: String, vararg variants: Pair, // - containedInSuggestions: Boolean = false, - strict: Boolean = true, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { val result = doTestBase(text, withRuntimeInfo, inConsole) @@ -108,18 +106,8 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { elementPresentation.itemText to elementPresentation.typeText } - when { - containedInSuggestions -> { - variants.forEach { expectedSuggestion -> - assert(lookupStrings.any { it.first == expectedSuggestion.first }) - } - } - strict -> { - assertOrderedEquals(lookupStrings, *variants) - } - else -> { - assertContainsOrdered(lookupStrings, *variants) - } + variants.forEach { expectedSuggestion -> + assert(lookupStrings.any { it.first == expectedSuggestion.first }) } } From f918bbc8c1d6e72ef8d37788714dfacc199aec4e Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sat, 8 May 2021 19:29:15 +0300 Subject: [PATCH 50/52] remove r-pluggin extension point definition for context providers for r6 and s4 libraries --- resources/META-INF/rplugin-common.xml | 6 ------ .../r/classes/r6/context/R6ContextProvider.kt | 8 ++++---- .../r6/context/R6CreateClassContextProvider.kt | 6 ++++++ .../r6/context/R6SetClassMembersContextProvider.kt | 5 +++++ .../r/classes/s4/context/RS4ContextProvider.kt | 11 +++++++---- .../classes/s4/context/RS4NewObjectContextProvider.kt | 5 +++++ .../classes/s4/context/RS4SetClassContextProvider.kt | 5 +++++ 7 files changed, 32 insertions(+), 14 deletions(-) diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index 0391a3068..44674d2c2 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -77,12 +77,6 @@ You can find the source code in the following repositories: - - - - messages.RPluginBundle diff --git a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt index bb0d00fba..a7b6870bf 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt @@ -17,10 +17,10 @@ abstract class R6ContextProvider { private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class companion object { - private val EP_NAME: ExtensionPointName> = - ExtensionPointName.create("com.intellij.r6ContextProvider") - - fun getProviders(): List> = EP_NAME.extensionList + fun getProviders(): List> = listOf( + R6CreateClassContextProvider(), + R6SetClassMembersContextProvider() + ) fun getR6Context(element: RPsiElement): ILibraryClassContext? { return getR6Context(element, ILibraryClassContext::class.java) diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt index 6881e1451..5cbd6bed7 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -9,6 +9,7 @@ import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.classes.s4.context.RS4ContextProvider import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.api.RCallExpression @@ -97,4 +98,9 @@ class R6CreateClassContextProvider : R6ContextProvider() { } } } + + override fun equals(other: Any?): Boolean { + if (other == null || other !is R6ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt index 055a0dce8..6b84a1f93 100644 --- a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt +++ b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt @@ -65,4 +65,9 @@ class R6SetClassMembersContextProvider : R6ContextProvider) return false + return this::class.java.name == other::class.java.name + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt index 6b7e65303..ecbaa58be 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt @@ -2,6 +2,9 @@ package org.jetbrains.r.classes.s4.context import com.intellij.openapi.extensions.ExtensionPointName import org.jetbrains.r.classes.common.context.ILibraryClassContext +import org.jetbrains.r.classes.r6.context.R6ContextProvider +import org.jetbrains.r.classes.r6.context.R6CreateClassContextProvider +import org.jetbrains.r.classes.r6.context.R6SetClassMembersContextProvider import org.jetbrains.r.psi.api.RPsiElement import java.lang.reflect.ParameterizedType @@ -13,10 +16,10 @@ abstract class RS4ContextProvider { private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class companion object { - private val EP_NAME: ExtensionPointName> = - ExtensionPointName.create("com.intellij.rS4ContextProvider") - - fun getProviders(): List> = EP_NAME.extensionList + fun getProviders(): List> = listOf( + RS4NewObjectContextProvider(), + RS4SetClassContextProvider() + ) fun getS4Context(element: RPsiElement): ILibraryClassContext? { return getS4Context(element, ILibraryClassContext::class.java) diff --git a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt index 6019a43bf..54d94042d 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt @@ -55,4 +55,9 @@ class RS4NewObjectContextProvider : RS4ContextProvider() { } } } + + override fun equals(other: Any?): Boolean { + if (other == null || other !is RS4ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt index 6f1910201..479b515f2 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt @@ -86,4 +86,9 @@ class RS4SetClassContextProvider : RS4ContextProvider() { } } } + + override fun equals(other: Any?): Boolean { + if (other == null || other !is RS4ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } } \ No newline at end of file From e0b150b0173b0ff812221f5adc10e046996394ab Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 9 May 2021 18:52:59 +0300 Subject: [PATCH 51/52] fix tests and completion for active bindings (and rinterop) --- resources/META-INF/rplugin-common.xml | 8 -------- src/org/jetbrains/r/editor/RCompletionContributor.kt | 7 +++++-- .../r/completion/classes/R6ClassCompletionTest.kt | 8 ++++++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/resources/META-INF/rplugin-common.xml b/resources/META-INF/rplugin-common.xml index 44674d2c2..6f060bfa1 100644 --- a/resources/META-INF/rplugin-common.xml +++ b/resources/META-INF/rplugin-common.xml @@ -731,14 +731,6 @@ You can find the source code in the following repositories: - - - - - - - - diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index c1683877e..8f774afc6 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -176,8 +176,11 @@ class RCompletionContributor : CompletionContributor() { if (r6Member.name in shownNames) continue when (r6Member){ - is R6ClassField -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) - is R6ClassMethod -> result.consume(rCompletionElementFactory.createFunctionLookupElement(r6Member.name)) + is R6ClassField, + is R6ClassActiveBinding + -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + is R6ClassMethod + -> result.consume(rCompletionElementFactory.createFunctionLookupElement(r6Member.name)) } shownNames.add(r6Member.name) diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt index 660cdbd6c..0b673b1cc 100644 --- a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -81,10 +81,14 @@ class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { fun testConsoleMembersSuggestion() { rInterop.executeCode(""" library(R6) - MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } )) + MyClass <- R6Class("MyClass", public = list(someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } ), + private = list(somePrivateField = 0, somePrivateMethod = function(x = 1) { print(x) } ), + active = list(someActiveFunction = function(x) { print(x) } ) + ) obj <- MyClass${'$'}new() """.trimIndent()) - doTest("obj${'$'}", "clone" to "", "random" to "", "someField" to "", "someMethod" to "", withRuntimeInfo = true, inConsole = true) + doTest("obj${'$'}", "clone" to "", "random" to "", "someField" to "", "someMethod" to "", "someActiveFunction" to "", + withRuntimeInfo = true, inConsole = true,) } private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { From 653a2b348542d91184e1710bcdef40a665d0d5d1 Mon Sep 17 00:00:00 2001 From: deaglegross Date: Sun, 7 Feb 2021 04:26:57 +0300 Subject: [PATCH 52/52] Feature request: R6 support --- build.gradle.kts | 1 + .../jetbrains/r/psi/api/RCallExpression.java | 6 + gen/org/jetbrains/r/psi/api/RVisitor.java | 2 +- .../r/psi/impl/RAndOperatorImpl.java | 4 +- .../r/psi/impl/RArgumentListImpl.java | 4 +- .../r/psi/impl/RAssignOperatorImpl.java | 4 +- .../r/psi/impl/RAssignmentStatementImpl.java | 2 +- .../r/psi/impl/RAtExpressionImpl.java | 6 +- .../jetbrains/r/psi/impl/RAtOperatorImpl.java | 4 +- .../r/psi/impl/RBlockExpressionImpl.java | 4 +- .../r/psi/impl/RBooleanLiteralImpl.java | 4 +- .../r/psi/impl/RBoundaryLiteralImpl.java | 4 +- .../r/psi/impl/RBreakStatementImpl.java | 4 +- .../r/psi/impl/RCallExpressionImpl.java | 12 + .../r/psi/impl/RColonOperatorImpl.java | 4 +- .../r/psi/impl/RCompareOperatorImpl.java | 4 +- .../r/psi/impl/REmptyExpressionImpl.java | 4 +- .../r/psi/impl/RExpOperatorImpl.java | 4 +- .../jetbrains/r/psi/impl/RExpressionImpl.java | 4 +- .../r/psi/impl/RForStatementImpl.java | 4 +- .../r/psi/impl/RHelpExpressionImpl.java | 4 +- .../r/psi/impl/RIdentifierExpressionImpl.java | 4 +- .../r/psi/impl/RIfStatementImpl.java | 4 +- .../r/psi/impl/RInfixOperatorImpl.java | 4 +- .../r/psi/impl/RInvalidLiteralImpl.java | 4 +- .../r/psi/impl/RListSubsetOperatorImpl.java | 4 +- .../r/psi/impl/RMemberExpressionImpl.java | 4 +- .../r/psi/impl/RMuldivOperatorImpl.java | 4 +- .../jetbrains/r/psi/impl/RNaLiteralImpl.java | 4 +- .../r/psi/impl/RNamedArgumentImpl.java | 4 +- .../impl/RNamespaceAccessExpressionImpl.java | 4 +- .../r/psi/impl/RNextStatementImpl.java | 4 +- .../r/psi/impl/RNoCommaTailImpl.java | 4 +- .../r/psi/impl/RNotOperatorImpl.java | 4 +- .../r/psi/impl/RNullLiteralImpl.java | 4 +- .../impl/RNumericLiteralExpressionImpl.java | 4 +- .../r/psi/impl/ROperatorExpressionImpl.java | 4 +- .../jetbrains/r/psi/impl/ROperatorImpl.java | 4 +- .../jetbrains/r/psi/impl/ROrOperatorImpl.java | 4 +- .../r/psi/impl/RParameterListImpl.java | 4 +- .../impl/RParenthesizedExpressionImpl.java | 4 +- .../r/psi/impl/RPlusminusOperatorImpl.java | 4 +- .../r/psi/impl/RRepeatStatementImpl.java | 4 +- .../psi/impl/RSubscriptionExpressionImpl.java | 4 +- .../r/psi/impl/RTildeExpressionImpl.java | 4 +- .../r/psi/impl/RTildeOperatorImpl.java | 4 +- .../r/psi/impl/RUnaryNotExpressionImpl.java | 4 +- .../impl/RUnaryPlusminusExpressionImpl.java | 4 +- .../r/psi/impl/RUnaryTildeExpressionImpl.java | 4 +- .../r/psi/impl/RWhileStatementImpl.java | 4 +- grammars/library_summary.proto | 23 ++ grammars/r.bnf | 2 +- resources/META-INF/rplugin-common.xml | 20 +- .../UnmatchingR6ClassNameInspection.html | 6 + resources/messages/RPluginBundle.properties | 2 + .../common/context/ILibraryClassContext.kt | 16 ++ src/org/jetbrains/r/classes/r6/R6ClassInfo.kt | 88 +++++++ .../jetbrains/r/classes/r6/R6ClassInfoUtil.kt | 240 ++++++++++++++++++ .../jetbrains/r/classes/r6/R6ClassPsiUtil.kt | 203 +++++++++++++++ .../r/classes/r6/context/R6ContextProvider.kt | 42 +++ .../context/R6CreateClassContextProvider.kt | 106 ++++++++ .../R6SetClassMembersContextProvider.kt | 73 ++++++ .../jetbrains/r/classes/s4/RS4ClassInfo.kt | 22 ++ .../r/classes/s4/RS4ClassInfoUtil.kt | 6 +- .../r/classes/s4/context/RS4Context.kt | 12 - .../classes/s4/context/RS4ContextProvider.kt | 24 +- .../s4/context/RS4NewObjectContextProvider.kt | 12 +- .../s4/context/RS4SetClassContextProvider.kt | 12 +- .../findUsages/RTargetElementEvaluator.kt | 8 +- .../r/console/RConsoleRuntimeInfo.kt | 11 + .../r/editor/RCompletionContributor.kt | 195 ++++++++++++-- .../completion/RLookupElementFactory.kt | 9 + .../UnresolvedReferenceInspection.kt | 2 +- .../r6/UnmatchingR6ClassNameInspection.kt | 40 +++ .../s4}/DeprecatedSetClassArgsInspection.kt | 4 +- .../s4}/InstanceOfVirtualS4ClassInspection.kt | 4 +- .../r/psi/RCallExpressionElementType.kt | 23 +- src/org/jetbrains/r/psi/RElementFilters.kt | 13 +- src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt | 7 + .../r/psi/references/RReferenceImpl.kt | 24 +- .../r/psi/stubs/RCallExpressionStub.kt | 2 + .../r/psi/stubs/RCallExpressionStubImpl.kt | 6 +- .../r/psi/stubs/RS4ClassNameIndex.kt | 46 ---- .../classes/LibraryClassNameIndexUtil.kt | 47 ++++ .../r/psi/stubs/classes/R6ClassNameIndex.kt | 47 ++++ .../r/psi/stubs/classes/RS4ClassNameIndex.kt | 47 ++++ .../rename/RenameRPsiElementProcessor.kt | 2 +- src/org/jetbrains/r/rinterop/RInterop.kt | 39 +++ .../r/roxygen/RoxygenCompletionContributor.kt | 14 + .../r/skeleton/RSkeletonFileStubBuilder.kt | 59 +++-- .../r/skeleton/psi/RSkeletonCallExpression.kt | 30 +-- .../psi/RSkeletonCallExpressionElementType.kt | 24 +- .../psi/RSkeletonCallExpressionStub.kt | 15 +- test/org/jetbrains/r/RUsefulTestCase.kt | 1 + .../r/classes/RClassesUtilTestsBase.kt | 31 +++ .../r/classes/r6/R6ClassInfoUtilTests.kt | 189 ++++++++++++++ .../r/classes/s4/RS4ClassInfoUtilTests.kt | 46 ++++ .../jetbrains/r/completion/AutoPopupTest.kt | 5 + .../classes/R6ClassCompletionTest.kt | 128 ++++++++++ .../{ => classes}/S4ClassCompletionTest.kt | 4 +- .../r/findUsages/FindUsagesTestBase.kt | 29 +++ .../r/findUsages/R6FindUsagesTest.kt | 132 ++++++++++ ...UsagesTest.kt => RCommonFindUsagesTest.kt} | 51 +--- .../r6/UnmatchingR6ClassNameInspectionTest.kt | 35 +++ .../DeprecatedSetClassArgsInspectionTest.kt | 6 +- .../InstanceOfVirtualS4ClassInspectionTest.kt | 6 +- test/org/jetbrains/r/rename/RRenameTest.kt | 47 +++- .../R6/renameR6ActiveBindingFromUsage.R | 17 ++ .../R6/renameR6ActiveBindingFromUsage.after.R | 17 ++ .../classes/R6/renameR6FieldFromUsage.R | 11 + .../classes/R6/renameR6FieldFromUsage.after.R | 11 + .../classes/R6/renameR6MethodFromUsage.R | 11 + .../R6/renameR6MethodFromUsage.after.R | 11 + 113 files changed, 2279 insertions(+), 341 deletions(-) create mode 100644 resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html create mode 100644 src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt create mode 100644 src/org/jetbrains/r/classes/r6/R6ClassInfo.kt create mode 100644 src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt create mode 100644 src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt create mode 100644 src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt create mode 100644 src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt create mode 100644 src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt delete mode 100644 src/org/jetbrains/r/classes/s4/context/RS4Context.kt create mode 100644 src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt rename src/org/jetbrains/r/inspections/{s4class => classes/s4}/DeprecatedSetClassArgsInspection.kt (94%) rename src/org/jetbrains/r/inspections/{s4class => classes/s4}/InstanceOfVirtualS4ClassInspection.kt (94%) delete mode 100644 src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt create mode 100644 src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt create mode 100644 src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt create mode 100644 src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt create mode 100644 test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt create mode 100644 test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt create mode 100644 test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt create mode 100644 test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt rename test/org/jetbrains/r/completion/{ => classes}/S4ClassCompletionTest.kt (99%) create mode 100644 test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt create mode 100644 test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt rename test/org/jetbrains/r/findUsages/{RFindUsagesTest.kt => RCommonFindUsagesTest.kt} (71%) create mode 100644 test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt rename test/org/jetbrains/r/inspections/{ => classes/s4}/DeprecatedSetClassArgsInspectionTest.kt (91%) rename test/org/jetbrains/r/inspections/{ => classes/s4}/InstanceOfVirtualS4ClassInspectionTest.kt (87%) create mode 100644 testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R create mode 100644 testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R create mode 100644 testData/rename/classes/R6/renameR6FieldFromUsage.R create mode 100644 testData/rename/classes/R6/renameR6FieldFromUsage.after.R create mode 100644 testData/rename/classes/R6/renameR6MethodFromUsage.R create mode 100644 testData/rename/classes/R6/renameR6MethodFromUsage.after.R diff --git a/build.gradle.kts b/build.gradle.kts index 034606cb0..104b65371 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { compile("com.google.protobuf:protobuf-java:$protobufVersion") compile("io.grpc:grpc-stub:$grpcVersion") compile("io.grpc:grpc-protobuf:$grpcVersion") + compile("org.assertj:assertj-core:3.18.1") runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") protobuf(files("protos/", "grammars/")) } diff --git a/gen/org/jetbrains/r/psi/api/RCallExpression.java b/gen/org/jetbrains/r/psi/api/RCallExpression.java index 8490555a1..34f842f78 100644 --- a/gen/org/jetbrains/r/psi/api/RCallExpression.java +++ b/gen/org/jetbrains/r/psi/api/RCallExpression.java @@ -1,9 +1,12 @@ // This is a generated file. Not intended for manual editing. package org.jetbrains.r.psi.api; +import java.util.List; import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; import com.intellij.psi.StubBasedPsiElement; import org.jetbrains.r.psi.stubs.RCallExpressionStub; +import org.jetbrains.r.classes.r6.R6ClassInfo; import org.jetbrains.r.classes.s4.RS4ClassInfo; public interface RCallExpression extends RExpression, StubBasedPsiElement { @@ -17,4 +20,7 @@ public interface RCallExpression extends RExpression, StubBasedPsiElement - - messages.RPluginBundle @@ -432,7 +429,8 @@ You can find the source code in the following repositories: - + + @@ -679,13 +677,19 @@ You can find the source code in the following repositories: bundle="messages.RPluginBundle" key="inspection.deprecated.double.starts.name" implementationClass="org.jetbrains.r.inspections.DeprecatedDoubleStarts"/> + + implementationClass="org.jetbrains.r.inspections.classes.s4.DeprecatedSetClassArgsInspection"/> + implementationClass="org.jetbrains.r.inspections.classes.s4.InstanceOfVirtualS4ClassInspection"/> + + + R @@ -727,10 +731,6 @@ You can find the source code in the following repositories: - - - - diff --git a/resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html b/resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html new file mode 100644 index 000000000..e0656c7ec --- /dev/null +++ b/resources/inspectionDescriptions/UnmatchingR6ClassNameInspection.html @@ -0,0 +1,6 @@ + + +Variable and argument class name in expression 'UserClass <- R6Class("UserClass")' should match. +It’s not strictly needed, but it improves error messages and makes it possible to use R6 objects with S3 generics + + \ No newline at end of file diff --git a/resources/messages/RPluginBundle.properties b/resources/messages/RPluginBundle.properties index ba7bc5088..ffd38803e 100644 --- a/resources/messages/RPluginBundle.properties +++ b/resources/messages/RPluginBundle.properties @@ -381,6 +381,8 @@ inspection.deprecated.setClass.args.description=Argument ''{0}'' is deprecated f inspection.virtual.s4class.instance.name=Trying to generate an object from a virtual class inspection.virtual.s4class.instance.description=Class ''{0}'' is virtual and object of this class cannot be created +inspection.r6class.naming.convention.classname=Classname should match the variable assignee name + install.libraries.fix.name=Install {0} install.libraries.fix.family.name=Install packages diff --git a/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt b/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt new file mode 100644 index 000000000..19efef86c --- /dev/null +++ b/src/org/jetbrains/r/classes/common/context/ILibraryClassContext.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.common.context + +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RPsiElement + +interface ILibraryClassContext { + val functionName: String + val functionCall: RCallExpression + val argumentInfo: RArgumentInfo? + val originalElement: RPsiElement +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt new file mode 100644 index 000000000..2906bc41f --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfo.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.openapi.util.io.DataInputOutputUtilRt +import com.intellij.psi.stubs.StubInputStream +import com.intellij.psi.stubs.StubOutputStream +import com.intellij.util.io.StringRef + +// no need to care about overloads because R6 lib doesn't support it: +// "All items in public, private, and active must have unique names." +interface IR6ClassMember { val name: String } +data class R6ClassField(override val name: String, val isPublic: Boolean = true) : IR6ClassMember +data class R6ClassMethod(override val name: String, val isPublic: Boolean = true) : IR6ClassMember +data class R6ClassActiveBinding(override val name: String) : IR6ClassMember + +data class R6ClassInfo(val className: String, + val superClasses: List, + val fields: List, + val methods: List, + val activeBindings: List) { + + fun containsMember(memberName: String) : Boolean { + return ((fields + methods).map { it.name }.contains(memberName) || + activeBindings.map { it.name }.contains(memberName)) + } + + fun serialize(dataStream: StubOutputStream) { + dataStream.writeName(className) + DataInputOutputUtilRt.writeSeq(dataStream, superClasses) { dataStream.writeName(it) } + + DataInputOutputUtilRt.writeSeq(dataStream, fields) { + dataStream.writeName(it.name); + dataStream.writeBoolean(it.isPublic) + } + + DataInputOutputUtilRt.writeSeq(dataStream, methods) { + dataStream.writeName(it.name); + dataStream.writeBoolean(it.isPublic) + } + + DataInputOutputUtilRt.writeSeq(dataStream, activeBindings) { + dataStream.writeName(it.name) + } + } + + companion object { + fun deserialize(dataStream: StubInputStream): R6ClassInfo { + val className = StringRef.toString(dataStream.readName()) + val superClasses = DataInputOutputUtilRt.readSeq(dataStream) { StringRef.toString(dataStream.readName()) } + + val fields = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + val isPublic = dataStream.readBoolean() + R6ClassField(name, isPublic) + } + + val methods = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + val isPublic = dataStream.readBoolean() + R6ClassMethod(name, isPublic) + } + + val activeBindings = DataInputOutputUtilRt.readSeq(dataStream) { + val name = StringRef.toString(dataStream.readName()) + R6ClassActiveBinding(name) + } + + return R6ClassInfo(className, superClasses, fields, methods, activeBindings) + } + } +} + +class R6ClassKeywordsProvider { + companion object { + val predefinedClassMethods = listOf( + R6ClassMethod("clone", true) + ) + + val visibilityModifiers = listOf( + R6ClassInfoUtil.argumentPrivate, + R6ClassInfoUtil.argumentPublic, + R6ClassInfoUtil.argumentActive, + ) + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt new file mode 100644 index 000000000..0a77015da --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/R6ClassInfoUtil.kt @@ -0,0 +1,240 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.openapi.util.Key +import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.psi.RElementFactory +import org.jetbrains.r.psi.api.* +import org.jetbrains.r.psi.impl.RCallExpressionImpl +import org.jetbrains.r.psi.impl.RMemberExpressionImpl +import org.jetbrains.r.psi.isFunctionFromLibrarySoft +import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex + +object R6ClassInfoUtil { + const val R6PackageName = "R6" + const val R6CreateClassMethod = "R6Class" + + const val R6ClassThisKeyword = "self" + const val functionNew = "new" + const val functionSet = "set" + + const val argumentClassName = "classname" + const val argumentSuperClass = "inherit" + const val argumentPublic = "public" + const val argumentPrivate = "private" + const val argumentActive = "active" + + private val INSTANTIATE_CLASS_DEFINITION_KEY: Key = Key.create("R6_INSTANTIATE_CLASS_DEFINITION") + + private val INSTANTIATE_CLASS_DEFINITION = + """R6Class <- function (classname = NULL, public = list(), private = NULL, + active = NULL, inherit = NULL, lock_objects = TRUE, class = TRUE, + portable = TRUE, lock_class = FALSE, cloneable = TRUE, + parent_env = parent.frame(), lock) {}""".trimIndent() + + /** + * @param call expression `MyClass$new()` + * @return class name which type is instantiated + */ + fun getAssociatedClassNameFromInstantiationCall(call: RCallExpression): String? { + val callExpression = call.expression as? RMemberExpressionImpl ?: return null + if (callExpression.rightExpr?.text != functionNew) return null + return callExpression.leftExpr?.name + } + + /** + * @param rMemberExpression expression `self$someMember` or `obj$someMember` + * @return className of class where `self$...` is used or of which object is called + */ + fun getClassNameFromInternalClassMemberUsageExpression(rMemberExpression: RMemberExpression?): String? { + if (rMemberExpression == null) return null + val classDefinitionCall = R6ClassPsiUtil.getClassDefinitionCallFromMemberUsage(rMemberExpression) ?: return null + return getAssociatedClassNameFromR6ClassCall(classDefinitionCall) + } + + /** + * @param callExpression expression `R6Class("MyClass", ...)` + * @return + */ + fun getAssociatedClassNameFromR6ClassCall(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val rAssignmentStatement = callExpression.parent as? RAssignmentStatement ?: return null + return rAssignmentStatement.assignee?.name + } + + /** + * @param callExpression expression `R6Class("MyClass", ...)` + * @return names of all inherited chain of parents + */ + fun getAssociatedSuperClassesHierarchy(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): MutableList? { + argumentInfo ?: return null + + var classDeclarationExpression = callExpression + val classNamesHierarchy = mutableListOf() + + while (true) { + val directInherit = getAssociatedSuperClassName(classDeclarationExpression) ?: break + classNamesHierarchy.add(directInherit) + classDeclarationExpression = getSuperClassDefinitionCallExpression(classDeclarationExpression) ?: break + } + + return classNamesHierarchy + } + + /** + * @param callExpression expression `R6Class("MyClass", ...)` + * @return direct parent classname + */ + private fun getAssociatedSuperClassName(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): String? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + return (argumentInfo.getArgumentPassedToParameter(argumentSuperClass) as? RIdentifierExpression)?.name + } + + private fun getSuperClassDefinitionCallExpression(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression)): RCallExpression? { + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val inheritClassDefinition = argumentInfo?.getArgumentPassedToParameter(argumentSuperClass) as? RIdentifierExpression + return (inheritClassDefinition?.reference?.resolve() as? RAssignmentStatement)?.assignedValue as? RCallExpression + } + + fun getAllClassMembers(callExpression: RCallExpression): List { + val r6ClassInfo = CachedValuesManager.getProjectPsiDependentCache(callExpression) { callExpression.associatedR6ClassInfo } ?: return emptyList() + val allSuperClasses = getAssociatedSuperClassesHierarchy(callExpression) + + val callSearchScope = RSearchScopeUtil.getScope(callExpression) + val project = callExpression.project + + if (allSuperClasses != null) { + return (r6ClassInfo.fields + r6ClassInfo.methods + r6ClassInfo.activeBindings + allSuperClasses.flatMap { superClassName -> + R6ClassNameIndex.findClassInfos(superClassName, project, callSearchScope).flatMap { it.fields + it.methods + it.activeBindings } + }).distinctBy { it.name } + } + + return emptyList() + } + + fun getAssociatedMembers(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + + val r6ClassFields = getAssociatedFields(callExpression, argumentInfo, onlyPublic) + val r6ClassMethods = getAssociatedMethods(callExpression, argumentInfo, onlyPublic) + + val r6ClassMembers = mutableListOf() + if (r6ClassFields != null) r6ClassMembers.addAll(r6ClassFields) + if (r6ClassMethods != null) r6ClassMembers.addAll(r6ClassMethods) + + return r6ClassMembers + } + + fun getAssociatedFields(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + + val r6ClassFields = mutableListOf() + val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!publicContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, publicContents, true) + + if (!onlyPublic) { + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!privateContents.isNullOrEmpty()) getFieldsFromExpressionList(r6ClassFields, privateContents, false) + } + + return r6ClassFields + } + + fun getAssociatedMethods(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo(callExpression), + onlyPublic: Boolean = false): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + + val r6ClassMethods = mutableListOf() + val publicContents = (argumentInfo.getArgumentPassedToParameter(argumentPublic) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!publicContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, publicContents, true) + + if (!onlyPublic) { + val privateContents = (argumentInfo.getArgumentPassedToParameter(argumentPrivate) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!privateContents.isNullOrEmpty()) getMethodsFromExpressionList(r6ClassMethods, privateContents, false) + } + + return r6ClassMethods + } + + fun getAssociatedActiveBindings(callExpression: RCallExpression, + argumentInfo: RArgumentInfo? = RParameterInfoUtil.getArgumentInfo( + callExpression)): List? { + argumentInfo ?: return null + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val r6ClassActiveBindings = mutableListOf() + + val activeBindings = (argumentInfo.getArgumentPassedToParameter(argumentActive) as? RCallExpressionImpl)?.argumentList?.expressionList + if (!activeBindings.isNullOrEmpty()) getActiveBindingsFromExpressionList(r6ClassActiveBindings, activeBindings) + return r6ClassActiveBindings + } + + fun parseR6ClassInfo(callExpression: RCallExpression): R6ClassInfo? { + if (!callExpression.isFunctionFromLibrarySoft(R6CreateClassMethod, R6PackageName)) return null + val project = callExpression.project + var definition = project.getUserData(INSTANTIATE_CLASS_DEFINITION_KEY) + + if (definition == null || !definition.isValid) { + val instantiateClassDefinition = + RElementFactory.createRPsiElementFromText(callExpression.project, INSTANTIATE_CLASS_DEFINITION) as RAssignmentStatement + definition = instantiateClassDefinition.also { project.putUserData(INSTANTIATE_CLASS_DEFINITION_KEY, it) } + } + + val argumentInfo = RParameterInfoUtil.getArgumentInfo(callExpression, definition) ?: return null + val className = getAssociatedClassNameFromR6ClassCall(callExpression, argumentInfo) ?: return null + val superClassesHierarchy = getAssociatedSuperClassesHierarchy(callExpression, argumentInfo) ?: emptyList() + val fields = getAssociatedFields(callExpression, argumentInfo) ?: emptyList() + val methods = getAssociatedMethods(callExpression, argumentInfo) ?: emptyList() + val activeBindings = getAssociatedActiveBindings(callExpression, argumentInfo) ?: emptyList() + + return R6ClassInfo(className, superClassesHierarchy, fields, methods, activeBindings) + } + + private fun getFieldsFromExpressionList(r6ClassFields: MutableList, + callExpressions: List, + isPublicScope: Boolean) { + callExpressions.forEach { + if (it.lastChild !is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassFields.add(R6ClassField(it.name!!, isPublicScope)) + } + } + } + + private fun getMethodsFromExpressionList(r6ClassMethods: MutableList, + callExpressions: List, + isPublicScope: Boolean) { + callExpressions.forEach { + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassMethods.add(R6ClassMethod(it.name!!, isPublicScope)) + } + } + } + + private fun getActiveBindingsFromExpressionList(r6ClassActiveBindings: MutableList, + callExpressions: List) { + callExpressions.forEach { + if (it.lastChild is RFunctionExpression && !it.name.isNullOrEmpty()) { + r6ClassActiveBindings.add(R6ClassActiveBinding(it.name!!)) + } + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt new file mode 100644 index 000000000..e02273ccb --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/R6ClassPsiUtil.kt @@ -0,0 +1,203 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.elementType +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.parsing.RElementTypes.* +import org.jetbrains.r.psi.api.* +import org.jetbrains.r.psi.impl.RCallExpressionImpl +import org.jetbrains.r.psi.isFunctionFromLibrarySoft +import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex + +object R6ClassPsiUtil { + + /** + * @param dependantIdentifier `someMember` psi-element of expression `obj$someMember` + * @return RPsiElement with name of `someMember` + */ + fun getSearchedIdentifier(dependantIdentifier: RIdentifierExpression?) : RPsiElement? { + if (dependantIdentifier == null) return null + + val classDefinitionCall = getClassDefinitionCallFromMemberUsage(dependantIdentifier) ?: return null + val className = R6ClassInfoUtil.getAssociatedClassNameFromR6ClassCall(classDefinitionCall) ?: return null + val classNamesHierarchy = R6ClassInfoUtil.getAssociatedSuperClassesHierarchy(classDefinitionCall) + classNamesHierarchy?.add(0, className) + + val callSearchScope = RSearchScopeUtil.getScope(classDefinitionCall) + val project = classDefinitionCall.project + + val r6ClassInfo = run findMemberDefinition@ { + (classNamesHierarchy)?.reversed()?.forEach { + val r6ClassInfo = R6ClassNameIndex.findClassInfos(it, project, callSearchScope).firstOrNull() + + if (r6ClassInfo != null) { + if (r6ClassInfo.containsMember(dependantIdentifier.name)) return@findMemberDefinition r6ClassInfo + } + } + } as R6ClassInfo? + + r6ClassInfo ?: return null + val r6ClassDefinitionCall = R6ClassNameIndex.findClassDefinitions(r6ClassInfo.className, project, callSearchScope).firstOrNull() + val argumentInfo = getClassDefinitionArgumentInfo(r6ClassDefinitionCall) ?: return null + + val publicMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPublic) + val privateMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentPrivate) + val activeMembers = getClassMemberExpressionsOfArgument(argumentInfo, R6ClassInfoUtil.argumentActive) + + return extractNamedArgumentByName(dependantIdentifier.name, publicMembers?.mapNotNull { it as? RNamedArgument }) + ?: extractNamedArgumentByName(dependantIdentifier.name, privateMembers?.mapNotNull { it as? RNamedArgument }) + ?: extractNamedArgumentByName(dependantIdentifier.name, activeMembers?.mapNotNull { it as? RNamedArgument }) + } + + /** + * @param dependantIdentifier `someMember` psi-element of expression `obj$someMember` + * @param objectInstantiationCall RAssignmentStatement expression `obj <- MyClass$new()` + * @return class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` + */ + private fun getClassDefinitionExpression(dependantIdentifier: RIdentifierExpression?, objectInstantiationCall: RAssignmentStatement?): RAssignmentStatement? { + if (dependantIdentifier == null) return null + + // handling search request from inside of class usage with `self$field` + if (objectInstantiationCall == null) { + if (dependantIdentifier.parent?.firstChild?.text == R6ClassInfoUtil.R6ClassThisKeyword){ + var currentParent = dependantIdentifier.parent + var currentCall = currentParent as? RCallExpression + + while (dependantIdentifier.parent != null){ + if (currentCall?.isFunctionFromLibrarySoft(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName) == true) break + currentParent = currentParent.parent + currentCall = currentParent as? RCallExpression + } + + return currentCall?.parent as? RAssignmentStatement + } + + return null + } + + // handling search request from classic out-of-class-definition usage + val objectCreationCall = objectInstantiationCall.lastChild // MyClass$new() + val classElement = objectCreationCall?.firstChild?.firstChild // MyClass + + return classElement?.reference?.resolve() as? RAssignmentStatement + } + + /** + * @param rIdentifierExpression `someMember` of expression like `classObject$someMember` or `self$someMember` + * @return `R6Class` function call, which defines class containing `someMember` + */ + fun getClassDefinitionCallFromMemberUsage(rIdentifierExpression: RIdentifierExpression?) : RCallExpression? { + if (rIdentifierExpression == null) return null + val usedClassVariable = getClassIdentifierFromChainedUsages(rIdentifierExpression.parent as? RMemberExpression) + return getClassDefinitionFromClassVariableUsage(usedClassVariable) + } + + /** + * @param rMemberExpression expression like `classObject$someMember` or `self$someMember` + * @return `R6Class` function call, which defines class containing `someMember` + */ + fun getClassDefinitionCallFromMemberUsage(rMemberExpression: RMemberExpression?) : RCallExpression? { + if (rMemberExpression == null) return null + val classObject = getClassIdentifierFromChainedUsages(rMemberExpression) + return getClassDefinitionFromClassVariableUsage(classObject) + } + + private fun getClassDefinitionFromClassVariableUsage(classObject: PsiElement?) : RCallExpression? { + // `self$someMember` + if (classObject?.text == R6ClassInfoUtil.R6ClassThisKeyword) { + val parentFunction = PsiTreeUtil.getStubOrPsiParentOfType(classObject, RCallExpression::class.java) + val r6ClassDefinitionCall = PsiTreeUtil.getStubOrPsiParentOfType(parentFunction, RCallExpression::class.java) + + if (r6ClassDefinitionCall?.isFunctionFromLibrarySoft(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName) == true) { + return r6ClassDefinitionCall + } + } + // `classObject$someMember` + else { + // `classObject <- MyClass$new()` from `classObject$someMember$someMethod()$someMethod2()$someMember` + val r6ObjectCreationExpression = classObject?.reference?.resolve() as? RAssignmentStatement + // `MyClass` from `classObject <- MyClass$new()` + val usedClassVariable = r6ObjectCreationExpression?.assignedValue?.firstChild?.firstChild + // `MyClass <- R6Class(...)` from `MyClass` + val classDefinitionAssignment = usedClassVariable?.reference?.resolve() as? RAssignmentStatement + + return classDefinitionAssignment?.assignedValue as? RCallExpression + } + + return null + } + + /** + * @param classDefinitionAssignment class definition expression `MyClass <- R6Class("MyClass", list( someField = 0))` + * @return argument info containing all internal members of class + */ + fun getClassDefinitionArgumentInfo(classDefinitionAssignment: RAssignmentStatement?) : RArgumentInfo? { + if (classDefinitionAssignment == null) return null + val classDefinitionCall = classDefinitionAssignment.children.last() as? RCallExpression + return getClassDefinitionArgumentInfo(classDefinitionCall) + } + + /** + * @param classDefinitionCall class definition expression `R6Class("MyClass", list( someField = 0))` + * @return argument info containing all internal members of class + */ + fun getClassDefinitionArgumentInfo(classDefinitionCall: RCallExpression?) : RArgumentInfo? { + if (classDefinitionCall == null) return null + return RParameterInfoUtil.getArgumentInfo(classDefinitionCall) + } + + /** + * @param argumentInfo information about arguments of R6 class definition call + * @param argumentName name of argument (i.e. `public`, `private`, `active`) from where to pick class members + */ + private fun getClassMemberExpressionsOfArgument(argumentInfo: RArgumentInfo, argumentName: String) : List? { + val members = argumentInfo.getArgumentPassedToParameter(argumentName) as? RCallExpressionImpl + return members?.argumentList?.expressionList + } + + /** + * @param dependantIdentifier `someMember` psi-element of expression `classObject$someMember$someMethod()$someActive$someMethod2()` + * @return RIdentifier of R6-class object + */ + private fun getR6ObjectIdentifierFromChainedUsage(dependantIdentifier: RIdentifierExpression?): RIdentifierExpression? { + if (dependantIdentifier == null) return null + + val usageExpression = dependantIdentifier.parent + if (usageExpression.elementType != R_MEMBER_EXPRESSION) return null + + var r6Object = usageExpression.firstChild + while (r6Object != null && r6Object.elementType != R_IDENTIFIER_EXPRESSION){ + r6Object = r6Object.firstChild + } + + return r6Object as RIdentifierExpression + } + + /** + * @param rMemberExpression `classObject$someMember$someMethod()$someActive()` + * @return `classObject` identifier as the most left psi-element in chained usage expression + */ + private fun getClassIdentifierFromChainedUsages(rMemberExpression: RMemberExpression?) : RIdentifierExpression? { + if (rMemberExpression == null) return null + val classIdentifier = PsiTreeUtil.firstChild(rMemberExpression).parent as? RIdentifierExpression + if (classIdentifier?.parent.elementType != R_MEMBER_EXPRESSION) return null + return classIdentifier + } + + private fun extractNamedArgumentByName(elementName: String, namedArguments: List?) : RPsiElement? { + namedArguments?.forEach { + if (it != null) { + if (it.name == elementName) return it + } + } + + return null + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt new file mode 100644 index 000000000..a7b6870bf --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6ContextProvider.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.openapi.extensions.ExtensionPointName +import org.jetbrains.r.classes.common.context.ILibraryClassContext +import org.jetbrains.r.psi.api.RPsiElement +import java.lang.reflect.ParameterizedType + +abstract class R6ContextProvider { + abstract fun getR6ContextInner(element: RPsiElement): T? + abstract fun getContext(element: RPsiElement): T? + + @Suppress("UNCHECKED_CAST") + private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class + + companion object { + fun getProviders(): List> = listOf( + R6CreateClassContextProvider(), + R6SetClassMembersContextProvider() + ) + + fun getR6Context(element: RPsiElement): ILibraryClassContext? { + return getR6Context(element, ILibraryClassContext::class.java) + } + + fun getR6Context(element: RPsiElement, vararg searchedContexts: Class): T? { + for (provider in getProviders()) { + if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { + val s4Context = provider.getContext(element) + if (s4Context != null) { + @Suppress("UNCHECKED_CAST") + return s4Context as T? + } + } + } + return null + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt new file mode 100644 index 000000000..5cbd6bed7 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6CreateClassContextProvider.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.ILibraryClassContext +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.classes.s4.context.RS4ContextProvider +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RNamedArgument +import org.jetbrains.r.psi.api.RPsiElement +import org.jetbrains.r.psi.isFunctionFromLibrary + +sealed class R6CreateClassContext : ILibraryClassContext { + override val functionName = R6ClassInfoUtil.R6CreateClassMethod +} + +// R6Class(, ) +// R6Class("", ) +data class R6CreateClassNameContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6CreateClassContext() + +// R6Class("MyClass", inherit = ) +data class R6CreateClassInheritContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6CreateClassContext() + +// R6Class("MyClass", , public = ) +// R6Class("MyClass", , public = list()) +// R6Class("MyClass", , private = ) +// R6Class("MyClass", , private = list()) +// R6Class("MyClass", , active = ) +// R6Class("MyClass", , active = list()) +data class R6CreateClassMembersContext(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6CreateClassContext() + +class R6CreateClassContextProvider : R6ContextProvider() { + override fun getContext(element: RPsiElement): R6CreateClassContext? { + return CachedValuesManager.getCachedValue(element) { + CachedValueProvider.Result.create(getR6ContextInner(element), element) + } + } + + override fun getR6ContextInner(element: RPsiElement): R6CreateClassContext? { + val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null + return if (parentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) { + val parentArgumentInfo = RParameterInfoUtil.getArgumentInfo(parentCall) ?: return null + when (element) { + parentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentClassName) -> { + // R6Class("") + R6CreateClassNameContext(element, parentCall, parentArgumentInfo) + } + + else -> null + } + } else { + val superParentCall = PsiTreeUtil.getParentOfType(parentCall, RCallExpression::class.java) ?: return null + if (!superParentCall.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) return null + val superParentArgumentInfo = RParameterInfoUtil.getArgumentInfo(superParentCall) ?: return null + + return when { + // R6Class("MyClass", inherit = "") + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentSuperClass), element, + false) -> { + val parent = element.parent + if (parent is RNamedArgument && parent.nameIdentifier == element) null + else R6CreateClassInheritContext(element, superParentCall, superParentArgumentInfo) + } + + // R6Class("MyClass", public = "" + // R6Class("MyClass", public = list("") + // R6Class("MyClass", public = list(smt = "") + + // R6Class("MyClass", private = "" + // R6Class("MyClass", private = list("") + // R6Class("MyClass", private = list(smt = "") + + // R6Class("MyClass", active = "" + // R6Class("MyClass", active = list("") + // R6Class("MyClass", active = list(smt = "") + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPublic), element, false) || + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentPrivate), element, false) || + PsiTreeUtil.isAncestor(superParentArgumentInfo.getArgumentPassedToParameter(R6ClassInfoUtil.argumentActive), element, false) -> { + val parent = element.parent + if (parent !is RNamedArgument || parent.assignedValue != element) null + else R6CreateClassMembersContext(element, superParentCall, superParentArgumentInfo) + } + + else -> null + } + } + } + + override fun equals(other: Any?): Boolean { + if (other == null || other !is R6ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt new file mode 100644 index 000000000..6b84a1f93 --- /dev/null +++ b/src/org/jetbrains/r/classes/r6/context/R6SetClassMembersContextProvider.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6.context + +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.ILibraryClassContext +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.hints.parameterInfo.RArgumentInfo +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RMemberExpression +import org.jetbrains.r.psi.api.RPsiElement +import org.jetbrains.r.psi.references.RSearchScopeUtil +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex + +sealed class R6SetClassMembersContext : ILibraryClassContext { + override val functionName = R6ClassInfoUtil.functionSet +} + +// MyClass$set() +// MyClass$set("") +data class R6SetClassMembersContextVisibility(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo?) : R6SetClassMembersContext() + +// MyClass$set(visibility, ) +// MyClass$set("visibility", ) +data class R6SetClassMembersContextName(override val originalElement: RPsiElement, + override val functionCall: RCallExpression, + override val argumentInfo: RArgumentInfo) : R6SetClassMembersContext() + +class R6SetClassMembersContextProvider : R6ContextProvider() { + override fun getContext(element: RPsiElement): R6SetClassMembersContext? { + return CachedValuesManager.getCachedValue(element) { + CachedValueProvider.Result.create(getR6ContextInner(element), element) + } + } + + override fun getR6ContextInner(element: RPsiElement): R6SetClassMembersContext? { + val parentCall = PsiTreeUtil.getParentOfType(element, RCallExpression::class.java) ?: return null + if (!isFromR6Library(parentCall)) return null + if (isElementNthArgumentFromRCallExpression(0, element, parentCall)) { + return R6SetClassMembersContextVisibility(element, parentCall, null) + } + + return null + } + + private fun isElementNthArgumentFromRCallExpression(num: Int, element: RPsiElement, rCallExpression: RCallExpression): Boolean { + val arguments = rCallExpression.lastChild.children + if (arguments.isNullOrEmpty()) return false + + return arguments[num] != null && arguments[num].textMatches(element) + } + + private fun isFromR6Library(rCallExpression: RCallExpression): Boolean { + val memberExpression = rCallExpression.expression as? RMemberExpression ?: return false + if (!memberExpression.lastChild.textMatches(R6ClassInfoUtil.functionSet)) return false + + val r6ClassIdentifier = memberExpression.firstChild + val cachedClasses = R6ClassNameIndex.findClassInfos(r6ClassIdentifier.text, memberExpression.project, + RSearchScopeUtil.getScope(rCallExpression)) + return (!cachedClasses.isNullOrEmpty()) + } + + override fun equals(other: Any?): Boolean { + if (other == null || other !is R6ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt index 56237871f..68afa50bc 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfo.kt @@ -37,4 +37,26 @@ data class RS4ClassInfo(val className: String, return RS4ClassInfo(className, packageName, slots, superClasses, isVirtual) } } + + override fun toString() : String { + return buildString { + append("setClass('").append(className).append("', ") + append("slots = c(") + slots.forEachIndexed { ind, slot -> + if (ind != 0) append(", ") + append(slot.name).append(" = '").append(slot.type).append("'") + } + append("), ") + append("contains = c(") + superClasses.forEachIndexed { ind, superClass -> + if (ind != 0) append(", ") + append("'").append(superClass).append("'") + } + if (isVirtual) { + if (superClasses.isNotEmpty()) append(", ") + append("'VIRTUAL'") + } + append("))") + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt index 57cd09047..e74b5f658 100644 --- a/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt +++ b/src/org/jetbrains/r/classes/s4/RS4ClassInfoUtil.kt @@ -14,7 +14,7 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.isFunctionFromLibrarySoft import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.skeleton.psi.RSkeletonCallExpression object RS4ClassInfoUtil { @@ -35,7 +35,7 @@ object RS4ClassInfoUtil { if (callExpression == null) return emptyList() if (callExpression is RSkeletonCallExpression) { // S4 classes from packages and so contains all slots - return callExpression.associatedS4ClassInfo.slots + return callExpression.associatedS4ClassInfo?.slots ?: emptyList() } if (!callExpression.isFunctionFromLibrary("setClass", "methods")) return emptyList() return CachedValuesManager.getProjectPsiDependentCache(callExpression) { @@ -53,7 +53,7 @@ object RS4ClassInfoUtil { if (callExpression == null) return emptyList() if (callExpression is RSkeletonCallExpression) { // S4 classes from packages and so contains all super classes - return callExpression.associatedS4ClassInfo.superClasses + return callExpression.associatedS4ClassInfo?.superClasses ?: emptyList() } if (!callExpression.isFunctionFromLibrary("setClass", "methods")) return emptyList() return CachedValuesManager.getProjectPsiDependentCache(callExpression) { diff --git a/src/org/jetbrains/r/classes/s4/context/RS4Context.kt b/src/org/jetbrains/r/classes/s4/context/RS4Context.kt deleted file mode 100644 index 6b75e4fbc..000000000 --- a/src/org/jetbrains/r/classes/s4/context/RS4Context.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.r.classes.s4.context - -import org.jetbrains.r.hints.parameterInfo.RArgumentInfo -import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.api.RPsiElement - -interface RS4Context { - val functionName: String - val functionCall: RCallExpression - val argumentInfo: RArgumentInfo - val originalElement: RPsiElement -} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt index 9f02da0f6..ecbaa58be 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4ContextProvider.kt @@ -1,30 +1,34 @@ package org.jetbrains.r.classes.s4.context import com.intellij.openapi.extensions.ExtensionPointName +import org.jetbrains.r.classes.common.context.ILibraryClassContext +import org.jetbrains.r.classes.r6.context.R6ContextProvider +import org.jetbrains.r.classes.r6.context.R6CreateClassContextProvider +import org.jetbrains.r.classes.r6.context.R6SetClassMembersContextProvider import org.jetbrains.r.psi.api.RPsiElement import java.lang.reflect.ParameterizedType -abstract class RS4ContextProvider { +abstract class RS4ContextProvider { - abstract fun getS4Context(element: RPsiElement): T? + abstract fun getContext(element: RPsiElement): T? @Suppress("UNCHECKED_CAST") private val contextClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class companion object { - private val EP_NAME: ExtensionPointName> = - ExtensionPointName.create("com.intellij.rS4ContextProvider") + fun getProviders(): List> = listOf( + RS4NewObjectContextProvider(), + RS4SetClassContextProvider() + ) - fun getProviders(): List> = EP_NAME.extensionList - - fun getS4Context(element: RPsiElement): RS4Context? { - return getS4Context(element, RS4Context::class.java) + fun getS4Context(element: RPsiElement): ILibraryClassContext? { + return getS4Context(element, ILibraryClassContext::class.java) } - fun getS4Context(element: RPsiElement, vararg searchedContexts: Class): T? { + fun getS4Context(element: RPsiElement, vararg searchedContexts: Class): T? { for (provider in getProviders()) { if (searchedContexts.any { it.isAssignableFrom(provider.contextClass) }) { - val s4Context = provider.getS4Context(element) + val s4Context = provider.getContext(element) if (s4Context != null) { @Suppress("UNCHECKED_CAST") return s4Context as T? diff --git a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt index e13847138..54d94042d 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4NewObjectContextProvider.kt @@ -3,6 +3,7 @@ package org.jetbrains.r.classes.s4.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.RPsiUtil @@ -11,7 +12,7 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class RS4NewObjectContext : RS4Context { +sealed class RS4NewObjectContext : ILibraryClassContext { override val functionName = "new" } @@ -27,7 +28,7 @@ data class RS4NewObjectSlotNameContext(override val originalElement: RPsiElement override val argumentInfo: RArgumentInfo) : RS4NewObjectContext() class RS4NewObjectContextProvider : RS4ContextProvider() { - override fun getS4Context(element: RPsiElement): RS4NewObjectContext? { + override fun getContext(element: RPsiElement): RS4NewObjectContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getS4ContextInner(element), element) } @@ -54,4 +55,9 @@ class RS4NewObjectContextProvider : RS4ContextProvider() { } } } -} + + override fun equals(other: Any?): Boolean { + if (other == null || other !is RS4ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt index 188165695..479b515f2 100644 --- a/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt +++ b/src/org/jetbrains/r/classes/s4/context/RS4SetClassContextProvider.kt @@ -3,6 +3,7 @@ package org.jetbrains.r.classes.s4.context import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.r.classes.common.context.ILibraryClassContext import org.jetbrains.r.hints.parameterInfo.RArgumentInfo import org.jetbrains.r.hints.parameterInfo.RParameterInfoUtil import org.jetbrains.r.psi.api.RCallExpression @@ -10,7 +11,7 @@ import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RPsiElement import org.jetbrains.r.psi.isFunctionFromLibrary -sealed class RS4SetClassContext : RS4Context { +sealed class RS4SetClassContext : ILibraryClassContext { override val functionName = "setClass" } @@ -35,7 +36,7 @@ data class RS4SetClassDependencyClassNameContext(override val originalElement: R override val argumentInfo: RArgumentInfo) : RS4SetClassContext() class RS4SetClassContextProvider : RS4ContextProvider() { - override fun getS4Context(element: RPsiElement): RS4SetClassContext? { + override fun getContext(element: RPsiElement): RS4SetClassContext? { return CachedValuesManager.getCachedValue(element) { CachedValueProvider.Result.create(getS4ContextInner(element), element) } @@ -85,4 +86,9 @@ class RS4SetClassContextProvider : RS4ContextProvider() { } } } -} + + override fun equals(other: Any?): Boolean { + if (other == null || other !is RS4ContextProvider<*>) return false + return this::class.java.name == other::class.java.name + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt b/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt index 6c42474a9..67f1f76f4 100644 --- a/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt +++ b/src/org/jetbrains/r/codeInsight/findUsages/RTargetElementEvaluator.kt @@ -3,6 +3,7 @@ package org.jetbrains.r.codeInsight.findUsages import com.intellij.codeInsight.TargetElementEvaluatorEx2 import com.intellij.psi.PsiElement import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RNamedArgument import org.jetbrains.r.psi.api.RParameter class RTargetElementEvaluator: TargetElementEvaluatorEx2() { @@ -12,10 +13,9 @@ class RTargetElementEvaluator: TargetElementEvaluatorEx2() { return grandParent.assignee == parent } - if (grandParent is RParameter) { - return true + return when (grandParent) { + is RParameter, is RNamedArgument -> true + else -> false } - - return false } } \ No newline at end of file diff --git a/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt b/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt index 724dbde7a..878a74730 100644 --- a/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt +++ b/src/org/jetbrains/r/console/RConsoleRuntimeInfo.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.psi.PsiFile import org.jetbrains.annotations.TestOnly +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo import org.jetbrains.r.psi.TableInfo @@ -33,6 +34,7 @@ interface RConsoleRuntimeInfo { fun loadShortS4ClassInfos(): List fun loadS4ClassInfoByObjectName(objectName: String): RS4ClassInfo? fun loadS4ClassInfoByClassName(className: String): RS4ClassInfo? + fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? fun getFormalArguments(expression: String) : List fun loadTableColumns(expression: String): TableInfo val rInterop: RInterop @@ -61,7 +63,10 @@ class RConsoleRuntimeInfoImpl(override val rInterop: RInterop) : RConsoleRuntime private val tableColumnsCache by rInterop.Cached { mutableMapOf() } private val s4ClassInfosByObjectNameCache by rInterop.Cached { mutableMapOf() } private val s4ClassInfosByClassNameCache by rInterop.Cached { mutableMapOf() } + private val r6ClassInfosByObjectNameCache by rInterop.Cached { mutableMapOf() } + private val r6ClassInfosByClassNameCache by rInterop.Cached { mutableMapOf() } private val loadedShortS4ClassInfosCache by rInterop.Cached { AtomicReference?>(null) } + private val loadedShortR6ClassInfosCache by rInterop.Cached { AtomicReference?>(null) } override val rMarkdownChunkOptions by lazy { rInterop.rMarkdownChunkOptions } @@ -133,6 +138,12 @@ class RConsoleRuntimeInfoImpl(override val rInterop: RInterop) : RConsoleRuntime } } + override fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? { + return r6ClassInfosByObjectNameCache.getOrPut(objectName) { + rInterop.getR6ClassInfoByObjectName(RReference.expressionRef(objectName, rInterop)) + } + } + override fun getFormalArguments(expression: String): List { return formalArgumentsCache.getOrPut(expression) { rInterop.getFormalArguments(RReference.expressionRef(expression, rInterop)) } } diff --git a/src/org/jetbrains/r/editor/RCompletionContributor.kt b/src/org/jetbrains/r/editor/RCompletionContributor.kt index 24b55f4de..8f774afc6 100755 --- a/src/org/jetbrains/r/editor/RCompletionContributor.kt +++ b/src/org/jetbrains/r/editor/RCompletionContributor.kt @@ -20,6 +20,8 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.r.RLanguage +import org.jetbrains.r.classes.r6.* +import org.jetbrains.r.classes.r6.context.* import org.jetbrains.r.classes.s4.* import org.jetbrains.r.classes.s4.context.* import org.jetbrains.r.codeInsight.libraries.RLibrarySupportProvider @@ -35,7 +37,8 @@ import org.jetbrains.r.parsing.RElementTypes.* import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import org.jetbrains.r.refactoring.RNamesValidator import org.jetbrains.r.rinterop.RValueFunction import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement @@ -53,6 +56,7 @@ class RCompletionContributor : CompletionContributor() { addMemberAccessCompletion() addAtAccessCompletion() addS4ClassContextCompletion() + addR6ClassContextCompletion() addIdentifierCompletion() } @@ -93,9 +97,14 @@ class RCompletionContributor : CompletionContributor() { .and(RElementFilters.S4_CONTEXT_FILTER), S4ClassContextCompletionProvider()) } + private fun addR6ClassContextCompletion() { + extend(CompletionType.BASIC, psiElement().withLanguage(RLanguage.INSTANCE) + .and(RElementFilters.R6_CONTEXT_FILTER), R6ClassContextCompletionProvider()) + } + private fun addStringLiteralCompletion() { extend(CompletionType.BASIC, psiElement().withLanguage(RLanguage.INSTANCE) - .and(RElementFilters.STRING_EXCEPT_S4_CONTEXT_FILTER), StringLiteralCompletionProvider()) + .and(RElementFilters.STRING_EXCEPT_OTHER_LIBRARIES_CONTEXT_FILTER), StringLiteralCompletionProvider()) } private class MemberAccessCompletionProvider : CompletionProvider() { @@ -105,16 +114,82 @@ class RCompletionContributor : CompletionContributor() { val info = file.runtimeInfo val memberAccess = PsiTreeUtil.getParentOfType(position, RMemberExpression::class.java) ?: return val leftExpr = memberAccess.leftExpr ?: return + + val shownNames = addStaticRuntimeCompletionDependsOfFile(memberAccess, file, result, MemberStaticRuntimeCompletionProvider) + if (info != null) { val noCalls = PsiTreeUtil.processElements(leftExpr) { it !is RCallExpression } if (noCalls) { - info.loadObjectNames(leftExpr.text).forEach { result.consume(rCompletionElementFactory.createNamespaceAccess(it)) } + info.loadObjectNames(leftExpr.text).forEach { + if (!shownNames.contains(it)) { + result.consume(rCompletionElementFactory.createNamespaceAccess(it)) + } + } } } for (extension in RLibrarySupportProvider.EP_NAME.extensions) { extension.completeMembers(leftExpr, rCompletionElementFactory, result) } } + + private object MemberStaticRuntimeCompletionProvider : RStaticRuntimeCompletionProvider { + override fun addCompletionFromRuntime(psiElement: RMemberExpression, + shownNames: MutableSet, + result: CompletionResultSet, + runtimeInfo: RConsoleRuntimeInfo): Boolean { + val obj = psiElement.leftExpr ?: return false + // obj$ + // env$obj$ + if (obj !is RIdentifierExpression && + (obj !is RMemberExpression || obj.rightExpr !is RIdentifierExpression)) { + return false + } + + val text = obj.text + runtimeInfo.loadR6ClassInfoByObjectName(text)?.let { classInfo -> + return addMembersCompletion(classInfo.fields + classInfo.methods + classInfo.activeBindings, shownNames, result) + } + return false + } + + override fun addCompletionStatically(psiElement: RMemberExpression, + shownNames: MutableSet, + result: CompletionResultSet): Boolean { + val className = R6ClassInfoUtil.getClassNameFromInternalClassMemberUsageExpression(psiElement) + if (className != null) { + R6ClassNameIndex.findClassDefinitions(className, psiElement.project, + RSearchScopeUtil.getScope(psiElement)).forEach { + return addMembersCompletion(R6ClassInfoUtil.getAllClassMembers(it) + R6ClassKeywordsProvider.predefinedClassMethods, shownNames, result) + } + } + + return false + } + + private fun addMembersCompletion(r6ClassMembers: List?, + shownNames: MutableSet, + result: CompletionResultSet): Boolean { + var hasNewResults = false + if (r6ClassMembers.isNullOrEmpty()) return hasNewResults + + for (r6Member in r6ClassMembers) { + if (r6Member.name in shownNames) continue + + when (r6Member){ + is R6ClassField, + is R6ClassActiveBinding + -> result.consume(rCompletionElementFactory.createAtAccess(r6Member.name)) + is R6ClassMethod + -> result.consume(rCompletionElementFactory.createFunctionLookupElement(r6Member.name)) + } + + shownNames.add(r6Member.name) + hasNewResults = true + } + + return hasNewResults + } + } } private class AtAccessCompletionProvider : CompletionProvider() { @@ -152,7 +227,8 @@ class RCompletionContributor : CompletionContributor() { val definition = resolveResult.element as? RAssignmentStatement ?: return@forEach (definition.assignedValue as? RCallExpression)?.let { call -> val className = RS4ClassInfoUtil.getAssociatedClassName(call) ?: return@forEach - RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).forEach { + RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, + RSearchScopeUtil.getScope(psiElement)).forEach { return addSlotsCompletion(RS4ClassInfoUtil.getAllAssociatedSlots(it), shownNames, result) } } @@ -201,8 +277,7 @@ class RCompletionContributor : CompletionContributor() { val position = if (probableIdentifier != null) { // operator surrounded by % or identifier PsiTreeUtil.findChildOfType(probableIdentifier, RInfixOperator::class.java) ?: probableIdentifier - } - else { + } else { // operator with parser error PsiTreeUtil.getParentOfType(parameters.position, RPsiElement::class.java, false) ?: return } @@ -255,8 +330,7 @@ class RCompletionContributor : CompletionContributor() { val element = RElementFactory.createRPsiElementFromTextOrNull(originFile.project, code) as? RAssignmentStatement ?: return@forEach result.consume(elementFactory.createFunctionLookupElement(element, isLocal = true)) - } - else { + } else { result.consume(elementFactory.createLocalVariableLookupElement(name, false)) } } @@ -301,8 +375,7 @@ class RCompletionContributor : CompletionContributor() { val parent = it.variableDescription.firstDefinition.parent if (parent is RAssignmentStatement && parent.isFunctionDeclaration) { result.consume(elementFactory.createFunctionLookupElement(parent, true)) - } - else { + } else { result.consume(elementFactory.createLocalVariableLookupElement(name, parent is RParameter)) } } @@ -362,8 +435,7 @@ class RCompletionContributor : CompletionContributor() { arg.parameterList?.parameterList?.map { it.name }?.forEach { consumeParameter(it, shownNames, result) } - } - else { + } else { arg.reference?.multiResolve(false)?.forEach { resolveResult -> (resolveResult.element as? RAssignmentStatement)?.let { assignment -> val inhNamedArgs = info?.loadInheritorNamedArguments(assignment.name) ?: emptyList() @@ -423,9 +495,9 @@ class RCompletionContributor : CompletionContributor() { result.addAllElements(lookupElements.map { val column = it.column if (column.quoteNeeded) { - rCompletionElementFactory.createQuotedLookupElement(column.name, TABLE_MANIPULATION_PRIORITY, true, AllIcons.Nodes.Field, column.type) - } - else { + rCompletionElementFactory.createQuotedLookupElement(column.name, TABLE_MANIPULATION_PRIORITY, true, AllIcons.Nodes.Field, + column.type) + } else { PrioritizedLookupElement.withPriority( RLookupElement(column.name, true, AllIcons.Nodes.Field, packageName = column.type), TABLE_MANIPULATION_PRIORITY @@ -466,7 +538,8 @@ class RCompletionContributor : CompletionContributor() { override fun addCompletionStatically(psiElement: RCallExpression, shownNames: MutableSet, result: CompletionResultSet): Boolean { - RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope(psiElement)).singleOrNull()?.let { definition -> + RS4ClassNameIndex.findClassDefinitions(className, psiElement.project, RSearchScopeUtil.getScope( + psiElement)).singleOrNull()?.let { definition -> RS4ClassInfoUtil.getAllAssociatedSlots(definition).forEach { result.consume(RLookupElementFactory.createNamedArgumentLookupElement(it.name, it.type, SLOT_NAME_PRIORITY)) } @@ -537,19 +610,80 @@ class RCompletionContributor : CompletionContributor() { val projectDir = classDeclaration.project.guessProjectDir() if (virtualFile == null || projectDir == null) "" else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" - } - else packageName + } else packageName if (classNameExpression is RStringLiteralExpression) { addElement(RLookupElementFactory.createLookupElementWithPriority( RLookupElement(escape(className), true, AllIcons.Nodes.Field, packageName = location), STRING_LITERAL_INSERT_HANDLER, priority)) - } - else { + } else { addElement(rCompletionElementFactory.createQuotedLookupElement(className, priority, true, AllIcons.Nodes.Field, location)) } } } + private class R6ClassContextCompletionProvider : CompletionProvider() { + override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { + val expression = PsiTreeUtil.getParentOfType(parameters.position, RExpression::class.java, false) ?: return + val file = parameters.originalFile + addR6ClassNameCompletion(expression, file, result) + addR6AdditionalMembersAfterCreation(expression, file, result) + } + + private fun addR6ClassNameCompletion(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { + val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6CreateClassContext::class.java) ?: return + val shownNames = HashSet() + + when (r6Context) { + is R6CreateClassNameContext -> { // suggestion of name of `<- R6Class("")` + result.addR6ClassNameCompletion(classNameExpression, shownNames) + } + + else -> return + } + } + + private fun addR6AdditionalMembersAfterCreation(classNameExpression: RExpression, file: PsiFile, result: CompletionResultSet) { + val r6Context = R6ContextProvider.getR6Context(classNameExpression, R6SetClassMembersContext::class.java) ?: return + val shownNames = HashSet() + + when (r6Context) { + is R6SetClassMembersContextVisibility -> { // suggestion of name of `classObject$set("")` + result.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression, shownNames) + } + + else -> return + } + } + + private fun CompletionResultSet.addR6SetAdditionalMembersAfterCreationCompletion(classNameExpression: RExpression, shownNames: MutableSet) { + val virtualFile = classNameExpression.containingFile.virtualFile + val projectDir = classNameExpression.project.guessProjectDir() + val location = if (virtualFile == null || projectDir == null) "" + else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" + + R6ClassKeywordsProvider.visibilityModifiers.forEach { visibilityModifier -> + if (visibilityModifier in shownNames) return + shownNames.add(visibilityModifier) + addElement(rCompletionElementFactory.createQuotedLookupElement(visibilityModifier, LANGUAGE_R6_CLASS_NAME, true, AllIcons.Nodes.Field, location)) + } + } + + private fun CompletionResultSet.addR6ClassNameCompletion(classNameExpression: RExpression, + shownNames: MutableSet) { + val classAssignmentExpression = PsiTreeUtil.getParentOfType(classNameExpression, + RAssignmentStatement::class.java) as RAssignmentStatement + val classNameToSuggest = classAssignmentExpression.assignee?.text ?: return + if (classNameToSuggest in shownNames) return + shownNames.add(classNameToSuggest) + + val virtualFile = classNameExpression.containingFile.virtualFile + val projectDir = classNameExpression.project.guessProjectDir() + val location = if (virtualFile == null || projectDir == null) "" + else VfsUtil.getRelativePath(virtualFile, projectDir) ?: "" + + addElement(rCompletionElementFactory.createQuotedLookupElement(classNameToSuggest, LANGUAGE_R6_CLASS_NAME, true, AllIcons.Nodes.Field, location)) + } + } private class StringLiteralCompletionProvider : CompletionProvider() { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { @@ -610,7 +744,7 @@ class RCompletionContributor : CompletionContributor() { private fun findParentheses(text: String, offset: Int): Int? { var whitespaceNo = 0 while (offset + whitespaceNo < text.length && text[offset + whitespaceNo] == ' ') whitespaceNo += 1 - return whitespaceNo.takeIf { (offset + whitespaceNo< text.length && text[offset + whitespaceNo ] == '(') } + return whitespaceNo.takeIf { (offset + whitespaceNo < text.length && text[offset + whitespaceNo] == '(') } } private object RFunctionCompletionInsertHandler : RLookupElementInsertHandler { @@ -626,6 +760,18 @@ class RCompletionContributor : CompletionContributor() { context.editor.caretModel.moveCaretRelatively(relativeCaretOffset, 0, false, false, false) } } + + override fun getInsertHandlerForLookupString(lookupString: String): InsertHandler { + return InsertHandler { context, _ -> + val document = context.document + val findParentheses = findParentheses(document.text, context.tailOffset) + if (findParentheses == null) { + document.insertString(context.tailOffset, "()") + } + val relativeCaretOffset = 2 + (findParentheses ?: 0) + context.editor.caretModel.moveCaretRelatively(relativeCaretOffset, 0, false, false, false) + } + } } private fun getFileNamePrefix(filepath: String): String? { @@ -671,19 +817,20 @@ class RCompletionContributor : CompletionContributor() { private fun addStaticRuntimeCompletionDependsOfFile(psiElement: T, file: PsiFile, result: CompletionResultSet, - provider: RStaticRuntimeCompletionProvider) { + provider: RStaticRuntimeCompletionProvider) : Set { val runtimeInfo = file.runtimeInfo val shownNames = HashSet() if (file.getUserData(RConsoleView.IS_R_CONSOLE_KEY) == true) { if (runtimeInfo == null || !provider.addCompletionFromRuntime(psiElement, shownNames, result, runtimeInfo)) { provider.addCompletionStatically(psiElement, shownNames, result) } - } - else { + } else { if (!provider.addCompletionStatically(psiElement, shownNames, result)) { runtimeInfo?.let { provider.addCompletionFromRuntime(psiElement, shownNames, result, it) } } } + + return shownNames } private fun addArgumentValueCompletion(position: PsiElement, result: CompletionResultSet) { diff --git a/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt b/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt index ab165c48b..5ca3ae415 100644 --- a/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt +++ b/src/org/jetbrains/r/editor/completion/RLookupElementFactory.kt @@ -29,6 +29,7 @@ const val LOADED_S4_CLASS_NAME = 100.0 const val VARIABLE_GROUPING = 90 const val NOT_LOADED_S4_CLASS_NAME = 50.0 const val LANGUAGE_S4_CLASS_NAME = 25.0 +const val LANGUAGE_R6_CLASS_NAME = 25.0 const val PACKAGE_PRIORITY = -1.0 const val GLOBAL_GROUPING = 0 const val NAMESPACE_NAME_GROUPING = -1 @@ -67,10 +68,12 @@ data class TableManipulationColumnLookup(val column: TableColumnInfo) { interface RLookupElementInsertHandler { fun getInsertHandlerForAssignment(assignment: RAssignmentStatement): InsertHandler + fun getInsertHandlerForLookupString(lookupString: String): InsertHandler } class REmptyLookupElementInsertHandler : RLookupElementInsertHandler { override fun getInsertHandlerForAssignment(assignment: RAssignmentStatement) = BasicInsertHandler() + override fun getInsertHandlerForLookupString(lookupString: String) = BasicInsertHandler() } class RLookupElementFactory(private val functionInsertHandler: RLookupElementInsertHandler = REmptyLookupElementInsertHandler(), @@ -98,6 +101,12 @@ class RLookupElementFactory(private val functionInsertHandler: RLookupElementIns if (isLocal) VARIABLE_GROUPING else GLOBAL_GROUPING) } + fun createFunctionLookupElement(lookupString: String): LookupElement { + val icon = AllIcons.Nodes.Function + return createLookupElementWithGrouping(RLookupElement(lookupString, false, icon), + functionInsertHandler.getInsertHandlerForLookupString(lookupString), + GLOBAL_GROUPING) + } fun createNamespaceAccess(lookupString: String): LookupElement { val insertHandler = InsertHandler { context, _ -> diff --git a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt index 1856f4344..d78b73d76 100644 --- a/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt +++ b/src/org/jetbrains/r/inspections/UnresolvedReferenceInspection.kt @@ -20,7 +20,7 @@ import org.jetbrains.r.psi.api.* import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RReferenceBase import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class UnresolvedReferenceInspection : RInspection() { diff --git a/src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt b/src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt new file mode 100644 index 000000000..cfd150483 --- /dev/null +++ b/src/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspection.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.inspections.classes.r6 + +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.r.RBundle +import org.jetbrains.r.classes.r6.R6ClassInfoUtil +import org.jetbrains.r.inspections.RInspection +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RStringLiteralExpression +import org.jetbrains.r.psi.api.RVisitor +import org.jetbrains.r.psi.isFunctionFromLibrary + +class UnmatchingR6ClassNameInspection : RInspection() { + override fun getDisplayName() = RBundle.message("inspection.r6class.naming.convention.classname") + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + return Visitor(holder) + } + + private class Visitor(private val myProblemHolder: ProblemsHolder) : RVisitor() { + override fun visitCallExpression(call: RCallExpression) { + if (!call.isFunctionFromLibrary(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName)) return + val classNameExpression = call.argumentList.expressionList.firstOrNull() as? RStringLiteralExpression ?: return + val className = classNameExpression.name ?: return + val userClassVariableName = R6ClassInfoUtil.getAssociatedClassNameFromR6ClassCall(call) ?: return + + if (className != userClassVariableName) { + myProblemHolder.registerProblem(classNameExpression, + RBundle.message("inspection.r6class.naming.convention.classname", className), + ProblemHighlightType.GENERIC_ERROR) + } + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/inspections/s4class/DeprecatedSetClassArgsInspection.kt b/src/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspection.kt similarity index 94% rename from src/org/jetbrains/r/inspections/s4class/DeprecatedSetClassArgsInspection.kt rename to src/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspection.kt index 964344a8f..0736b64ec 100644 --- a/src/org/jetbrains/r/inspections/s4class/DeprecatedSetClassArgsInspection.kt +++ b/src/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspection.kt @@ -1,8 +1,8 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections.s4class +package org.jetbrains.r.inspections.classes.s4 import com.intellij.codeInspection.LocalInspectionToolSession import com.intellij.codeInspection.ProblemHighlightType diff --git a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt b/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt similarity index 94% rename from src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt rename to src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt index aaaaf36e3..b39e9aa39 100644 --- a/src/org/jetbrains/r/inspections/s4class/InstanceOfVirtualS4ClassInspection.kt +++ b/src/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspection.kt @@ -2,7 +2,7 @@ * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections.s4class +package org.jetbrains.r.inspections.classes.s4 import com.intellij.codeInspection.LocalInspectionToolSession import com.intellij.codeInspection.ProblemHighlightType @@ -15,7 +15,7 @@ import org.jetbrains.r.psi.api.RStringLiteralExpression import org.jetbrains.r.psi.api.RVisitor import org.jetbrains.r.psi.isFunctionFromLibrary import org.jetbrains.r.psi.references.RSearchScopeUtil -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex class InstanceOfVirtualS4ClassInspection : RInspection() { override fun getDisplayName() = RBundle.message("inspection.virtual.s4class.instance.name") diff --git a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt index aec97b347..34ac169d7 100644 --- a/src/org/jetbrains/r/psi/RCallExpressionElementType.kt +++ b/src/org/jetbrains/r/psi/RCallExpressionElementType.kt @@ -9,14 +9,17 @@ import com.intellij.psi.stubs.IndexSink import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubInputStream import com.intellij.psi.stubs.StubOutputStream +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfoUtil import org.jetbrains.r.psi.api.RCallExpression import org.jetbrains.r.psi.impl.RCallExpressionImpl import org.jetbrains.r.psi.stubs.RCallExpressionStub import org.jetbrains.r.psi.stubs.RCallExpressionStubImpl -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex import java.io.IOException class RCallExpressionElementType(debugName: String) : RStubElementType(debugName) { @@ -25,29 +28,35 @@ class RCallExpressionElementType(debugName: String) : RStubElementType): RCallExpressionStub { - return RCallExpressionStubImpl(parentStub, this, RS4ClassInfoUtil.parseS4ClassInfo(psi)) + return RCallExpressionStubImpl(parentStub, this, RS4ClassInfoUtil.parseS4ClassInfo(psi), R6ClassInfoUtil.parseR6ClassInfo(psi)) } @Throws(IOException::class) override fun serialize(stub: RCallExpressionStub, dataStream: StubOutputStream) { dataStream.writeBoolean(stub.s4ClassInfo != null) + dataStream.writeBoolean(stub.r6ClassInfo != null) + stub.s4ClassInfo?.serialize(dataStream) + stub.r6ClassInfo?.serialize(dataStream) } @Throws(IOException::class) override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): RCallExpressionStub { val s4ClassExists = dataStream.readBoolean() + val r6ClassExists = dataStream.readBoolean() + val s4ClassInfo = if (s4ClassExists) RS4ClassInfo.deserialize(dataStream) else null - return RCallExpressionStubImpl(parentStub, this, s4ClassInfo) + val r6ClassInfo = if (r6ClassExists) R6ClassInfo.deserialize(dataStream) else null + return RCallExpressionStubImpl(parentStub, this, s4ClassInfo, r6ClassInfo) } override fun indexStub(stub: RCallExpressionStub, sink: IndexSink) { - stub.s4ClassInfo?.className?.let { - RS4ClassNameIndex.sink(sink, it) - } + stub.s4ClassInfo?.className?.let { RS4ClassNameIndex.sink(sink, it) } + stub.r6ClassInfo?.className?.let { R6ClassNameIndex.sink(sink, it) } } override fun shouldCreateStub(node: ASTNode?): Boolean { - return (node?.psi as? RCallExpression)?.isFunctionFromLibrarySoft("setClass", "methods") == true + return (node?.psi as? RCallExpression)?.isFunctionFromLibrarySoft("setClass", "methods") == true || + (node?.psi as? RCallExpression)?.isFunctionFromLibrarySoft(R6ClassInfoUtil.R6CreateClassMethod, R6ClassInfoUtil.R6PackageName) == true } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/RElementFilters.kt b/src/org/jetbrains/r/psi/RElementFilters.kt index 2205d6ef1..7f748c28e 100644 --- a/src/org/jetbrains/r/psi/RElementFilters.kt +++ b/src/org/jetbrains/r/psi/RElementFilters.kt @@ -11,6 +11,7 @@ import com.intellij.psi.filters.NotFilter import com.intellij.psi.filters.position.FilterPattern import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType +import org.jetbrains.r.classes.r6.context.R6ContextProvider import org.jetbrains.r.classes.s4.context.RS4ContextProvider import org.jetbrains.r.parsing.RElementTypes import org.jetbrains.r.psi.api.* @@ -26,7 +27,8 @@ object RElementFilters { val IDENTIFIER_OR_STRING_FILTER = FilterPattern(IdentifierOrStringFilter()) val STRING_FILTER = FilterPattern(StringFilter()) val S4_CONTEXT_FILTER = FilterPattern(S4ContextFilter()) - val STRING_EXCEPT_S4_CONTEXT_FILTER = FilterPattern(AndFilter(StringFilter(), NotFilter(S4ContextFilter()))) + val R6_CONTEXT_FILTER = FilterPattern(R6ContextFilter()) + val STRING_EXCEPT_OTHER_LIBRARIES_CONTEXT_FILTER = FilterPattern(AndFilter(StringFilter(), NotFilter(S4ContextFilter()))) } class MemberFilter : ElementFilter { @@ -124,3 +126,12 @@ class S4ContextFilter : ElementFilter { override fun isClassAcceptable(hintClass: Class<*>?) = true } + +class R6ContextFilter : ElementFilter { + override fun isAcceptable(element: Any?, context: PsiElement?): Boolean { + val expression = PsiTreeUtil.getParentOfType(context, RExpression::class.java, false) ?: return false + return R6ContextProvider.getR6Context(expression) != null + } + + override fun isClassAcceptable(hintClass: Class<*>?) = true +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt b/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt index 19a1aa601..5d0878a87 100644 --- a/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt +++ b/src/org/jetbrains/r/psi/impl/RPsiImplUtil.kt @@ -21,6 +21,8 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import com.intellij.util.IncorrectOperationException import org.jetbrains.r.RElementGenerator +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.classes.r6.R6ClassInfoUtil import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfoUtil import org.jetbrains.r.parsing.RElementTypes.* @@ -402,6 +404,11 @@ internal object RPsiImplUtil { return callExpression.greenStub?.s4ClassInfo ?: RS4ClassInfoUtil.parseS4ClassInfo(callExpression) } + @JvmStatic + fun getAssociatedR6ClassInfo(callExpression: RCallExpressionImpl): R6ClassInfo? { + return callExpression.greenStub?.r6ClassInfo ?: R6ClassInfoUtil.parseR6ClassInfo(callExpression) + } + private fun getLoopImpl(element: RPsiElement): RLoopStatement? { val loop = PsiTreeUtil.getParentOfType(element, RLoopStatement::class.java, RFunctionExpression::class.java) return if (loop is RLoopStatement) loop else null diff --git a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt index 2377a402b..44aa56fb1 100644 --- a/src/org/jetbrains/r/psi/references/RReferenceImpl.kt +++ b/src/org/jetbrains/r/psi/references/RReferenceImpl.kt @@ -11,11 +11,13 @@ import com.intellij.psi.PsiElementResolveResult import com.intellij.psi.ResolveResult import com.intellij.util.IncorrectOperationException import com.intellij.util.Processor +import org.jetbrains.r.classes.r6.R6ClassPsiUtil import org.jetbrains.r.codeInsight.libraries.RLibrarySupportProvider import org.jetbrains.r.codeInsight.table.RTableContextManager import org.jetbrains.r.psi.* import org.jetbrains.r.psi.api.* import org.jetbrains.r.skeleton.psi.RSkeletonAssignmentStatement +import java.util.ArrayList class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase(element) { @@ -31,12 +33,14 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase() val resolveProcessor = object : Processor { override fun process(it: TableColumnInfo): Boolean { @@ -63,13 +67,11 @@ class RReferenceImpl(element: RIdentifierExpression) : RReferenceBase parameter.name == element.name}?.let { PsiElementResolveResult(it) } + assignment.getParameters().firstOrNull { parameter -> parameter.name == element.name }?.let { PsiElementResolveResult(it) } }.toTypedArray() } + private fun resolveDependantIdentifier(): Array { + val r6SearchedIdentifierDefinition = R6ClassPsiUtil.getSearchedIdentifier(element) ?: return emptyArray() + + val result = ArrayList() + result.add(PsiElementResolveResult(r6SearchedIdentifierDefinition)) + return result.toTypedArray() + } + @Throws(IncorrectOperationException::class) override fun handleElementRename(newElementName: String): PsiElement? { return element.setName(newElementName) diff --git a/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt b/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt index 469214c1f..dcba5410b 100644 --- a/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt +++ b/src/org/jetbrains/r/psi/stubs/RCallExpressionStub.kt @@ -4,9 +4,11 @@ package org.jetbrains.r.psi.stubs import com.intellij.psi.stubs.StubElement +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression interface RCallExpressionStub : StubElement { val s4ClassInfo: RS4ClassInfo? + val r6ClassInfo: R6ClassInfo? } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt b/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt index 69f2ff206..508e4c70d 100644 --- a/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt +++ b/src/org/jetbrains/r/psi/stubs/RCallExpressionStubImpl.kt @@ -6,15 +6,17 @@ package org.jetbrains.r.psi.stubs import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.stubs.StubBase import com.intellij.psi.stubs.StubElement +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression class RCallExpressionStubImpl(parent: StubElement<*>, stubElementType: IStubElementType<*, *>, - override val s4ClassInfo: RS4ClassInfo?) + override val s4ClassInfo: RS4ClassInfo?, + override val r6ClassInfo: R6ClassInfo?) : StubBase(parent, stubElementType), RCallExpressionStub { override fun toString(): String { - return "RCallExpressionStub(${s4ClassInfo?.className})" + return "RCallExpressionStub(${s4ClassInfo?.className};${r6ClassInfo?.className})" } } \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt deleted file mode 100644 index 8712903c2..000000000 --- a/src/org/jetbrains/r/psi/stubs/RS4ClassNameIndex.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -package org.jetbrains.r.psi.stubs - -import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.stubs.IndexSink -import com.intellij.psi.stubs.StringStubIndexExtension -import com.intellij.psi.stubs.StubIndex -import com.intellij.psi.stubs.StubIndexKey -import com.intellij.util.Processor -import org.jetbrains.r.classes.s4.RS4ClassInfo -import org.jetbrains.r.psi.api.RCallExpression - - class RS4ClassNameIndex : StringStubIndexExtension() { - override fun getKey(): StubIndexKey { - return KEY - } - - companion object { - private val KEY = StubIndexKey.createIndexKey("R.s4class.shortName") - - fun processAllS4ClassInfos(project: Project, scope: GlobalSearchScope?, processor: Processor>) { - val stubIndex = StubIndex.getInstance() - stubIndex.processAllKeys(KEY, project) { key -> - stubIndex.processElements(KEY, key, project, scope, RCallExpression::class.java) { declaration -> - declaration.associatedS4ClassInfo?.let { processor.process(declaration to it) } ?: true - } - } - } - - fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java).mapNotNull { it.associatedS4ClassInfo } - } - - fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { - return StubIndex.getElements(KEY, name, project, scope, RCallExpression::class.java) - } - - fun sink(sink: IndexSink, name: String) { - sink.occurrence(KEY, name) - } - } -} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt new file mode 100644 index 000000000..7512c316e --- /dev/null +++ b/src/org/jetbrains/r/psi/stubs/classes/LibraryClassNameIndexUtil.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.psi.stubs.classes + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndex +import com.intellij.psi.stubs.StubIndexKey +import com.intellij.util.Processor +import org.jetbrains.r.classes.s4.RS4ClassInfo +import org.jetbrains.r.psi.api.RCallExpression + +abstract class LibraryClassNameIndexUtil : StringStubIndexExtension() { + companion object{ + fun processClassInfos(project: Project, + scope: GlobalSearchScope?, + indexKey: StubIndexKey, + indexProcessFunc: RCallExpression.() -> Boolean) { + val stubIndex = StubIndex.getInstance() + stubIndex.processAllKeys(indexKey, project) { key -> + stubIndex.processElements(indexKey, key, project, scope, RCallExpression::class.java) { + declaration -> indexProcessFunc(declaration) + } + } + } + + fun findClassInfos(indexKey: StubIndexKey, + mapClassInfoFunction: RCallExpression.() -> T?, + name: String, + project: Project, + scope: GlobalSearchScope?): List { + return StubIndex.getElements(indexKey, name, project, scope, RCallExpression::class.java).mapNotNull { mapClassInfoFunction(it) } + } + + fun findClassDefinitions(indexKey: StubIndexKey, name: String, project: Project, scope: GlobalSearchScope?): Collection { + return StubIndex.getElements(indexKey, name, project, scope, RCallExpression::class.java) + } + + fun sink(indexKey: StubIndexKey, sink: IndexSink, name: String) { + sink.occurrence(indexKey, name) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt new file mode 100644 index 000000000..8f19e3a71 --- /dev/null +++ b/src/org/jetbrains/r/psi/stubs/classes/R6ClassNameIndex.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.psi.stubs.classes + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndexKey +import com.intellij.util.Processor +import org.jetbrains.r.classes.r6.R6ClassInfo +import org.jetbrains.r.psi.api.RCallExpression + +class R6ClassNameIndex : StringStubIndexExtension() { + override fun getKey(): StubIndexKey { + return KEY + } + + companion object { + private val KEY = StubIndexKey.createIndexKey("R.r6class.shortName") + + fun processAllR6ClassInfos(project: Project, + scope: GlobalSearchScope?, + processor: Processor>) { + val processingFunction = fun (declaration: RCallExpression) : Boolean = + declaration.associatedR6ClassInfo?.let { processor.process(declaration to it) } ?: true + + return LibraryClassNameIndexUtil.processClassInfos(project, scope, KEY, processingFunction) + } + + fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { + val mapFunction = fun (declaration: RCallExpression) : R6ClassInfo? = declaration.associatedR6ClassInfo + + return LibraryClassNameIndexUtil.findClassInfos(KEY, mapFunction, name, project, scope) + } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return LibraryClassNameIndexUtil.findClassDefinitions(KEY, name, project, scope) + } + + fun sink(sink: IndexSink, name: String) { + return LibraryClassNameIndexUtil.sink(KEY, sink, name) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt new file mode 100644 index 000000000..50ca3d70c --- /dev/null +++ b/src/org/jetbrains/r/psi/stubs/classes/RS4ClassNameIndex.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.psi.stubs.classes + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StringStubIndexExtension +import com.intellij.psi.stubs.StubIndexKey +import com.intellij.util.Processor +import org.jetbrains.r.classes.s4.RS4ClassInfo +import org.jetbrains.r.psi.api.RCallExpression + +class RS4ClassNameIndex : StringStubIndexExtension() { + override fun getKey(): StubIndexKey { + return KEY + } + + companion object { + private val KEY = StubIndexKey.createIndexKey("R.s4class.shortName") + + fun processAllS4ClassInfos(project: Project, + scope: GlobalSearchScope?, + processor: Processor>) { + val processingFunction = fun (declaration: RCallExpression) : Boolean = + declaration.associatedS4ClassInfo?.let { processor.process(declaration to it) } ?: true + + return LibraryClassNameIndexUtil.processClassInfos(project, scope, KEY, processingFunction) + } + + fun findClassInfos(name: String, project: Project, scope: GlobalSearchScope?): List { + val mapFunction = fun (declaration: RCallExpression) : RS4ClassInfo? = declaration.associatedS4ClassInfo + + return LibraryClassNameIndexUtil.findClassInfos(KEY, mapFunction, name, project, scope) + } + + fun findClassDefinitions(name: String, project: Project, scope: GlobalSearchScope?): Collection { + return LibraryClassNameIndexUtil.findClassDefinitions(KEY, name, project, scope) + } + + fun sink(sink: IndexSink, name: String) { + return LibraryClassNameIndexUtil.sink(KEY, sink, name) + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt b/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt index 60bc54464..158786b91 100644 --- a/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt +++ b/src/org/jetbrains/r/refactoring/rename/RenameRPsiElementProcessor.kt @@ -30,7 +30,7 @@ class RenameRPsiElementProcessor : RenamePsiElementProcessor() { } } is RForStatement -> element.target - is RAssignmentStatement, is RParameter, is RFile -> element + is RAssignmentStatement, is RNamedArgument, is RParameter, is RFile -> element else -> null } } diff --git a/src/org/jetbrains/r/rinterop/RInterop.kt b/src/org/jetbrains/r/rinterop/RInterop.kt index d8c2d8462..2f18a2422 100644 --- a/src/org/jetbrains/r/rinterop/RInterop.kt +++ b/src/org/jetbrains/r/rinterop/RInterop.kt @@ -35,6 +35,8 @@ import io.grpc.stub.StreamObserver import org.jetbrains.annotations.TestOnly import org.jetbrains.concurrency.* import org.jetbrains.r.RBundle +import org.jetbrains.r.classes.r6.* +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassSlot import org.jetbrains.r.debugger.RDebuggerUtil @@ -871,6 +873,43 @@ class RInterop(val interpreter: RInterpreter, val processHandler: ProcessHandler } } + /** + * @return list of [R6ClassInfo] without information about [R6ClassInfo.fields], [R6ClassInfo.methods] and [R6ClassInfo.activeBindings] + */ + fun getLoadedShortR6ClassInfos(): List? { + return try { + executeWithCheckCancel(asyncStub::getLoadedShortR6ClassInfos, Empty.getDefaultInstance()).shortR6ClassInfosList.map { + R6ClassInfo(it.name, emptyList(), emptyList(), emptyList(), emptyList()) + } + } catch (e: RInteropTerminated) { + null + } + } + + fun getR6ClassInfoByObjectName(ref: RReference): R6ClassInfo? { + return try { + val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByObjectName, ref.proto) + R6ClassInfo(res.className, res.superClassesList, + res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, + res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + } catch (e: RInteropTerminated) { + null + } + } + + fun getR6ClassInfoByClassName(className: String): R6ClassInfo? { + return try { + val res = executeWithCheckCancel(asyncStub::getR6ClassInfoByClassName, StringValue.of(className)) + R6ClassInfo(res.className, res.superClassesList, + res.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + res.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, + res.activeBindingsList.map { R6ClassActiveBinding(it.name) }) + } catch (e: RInteropTerminated) { + null + } + } + fun getFormalArguments(function: RReference): List { return try { executeWithCheckCancel(asyncStub::getFormalArguments, function.proto).listList diff --git a/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt b/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt index 261f33429..fc20bb01b 100755 --- a/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt +++ b/src/org/jetbrains/r/roxygen/RoxygenCompletionContributor.kt @@ -109,6 +109,14 @@ class RoxygenCompletionContributor : CompletionContributor() { document.insertString(offset, "()") context.editor.caretModel.moveCaretRelatively(4, 0, false, false, false) } + + override fun getInsertHandlerForLookupString(lookupString: String) = InsertHandler { context, _ -> + val offset = context.tailOffset + val document = context.document + insertSpaceAfterLinkIfNeeded(document, offset) + document.insertString(offset, "()") + context.editor.caretModel.moveCaretRelatively(4, 0, false, false, false) + } } private object RoxygenConstantLinkInsertHandler : RLookupElementInsertHandler { @@ -117,6 +125,12 @@ class RoxygenCompletionContributor : CompletionContributor() { insertSpaceAfterLinkIfNeeded(document, context.tailOffset) context.editor.caretModel.moveCaretRelatively(2, 0, false, false, false) } + + override fun getInsertHandlerForLookupString(lookupString: String) = InsertHandler { context, _ -> + val document = context.document + insertSpaceAfterLinkIfNeeded(document, context.tailOffset) + context.editor.caretModel.moveCaretRelatively(2, 0, false, false, false) + } } private fun insertSpaceAfterLinkIfNeeded(document: Document, tailOffset: Int) { diff --git a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt index 2f96b06de..f02d32581 100644 --- a/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt +++ b/src/org/jetbrains/r/skeleton/RSkeletonFileStubBuilder.kt @@ -8,6 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.stubs.BinaryFileStubBuilder import com.intellij.psi.stubs.Stub import com.intellij.util.indexing.FileContent +import org.jetbrains.r.classes.r6.* import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.classes.s4.RS4ClassSlot import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo @@ -31,27 +32,43 @@ class RSkeletonFileStubBuilder : BinaryFileStubBuilder { LibrarySummary.RLibraryPackage.parseFrom(it) } for (symbol in binPackage.symbolsList) { - if (symbol.representationCase == RepresentationCase.S4CLASSREPRESENTATION) { - val s4ClassRepresentation = symbol.s4ClassRepresentation - RSkeletonCallExpressionStub(skeletonFileStub, - R_SKELETON_CALL_EXPRESSION, - RS4ClassInfo(symbol.name, - s4ClassRepresentation.packageName, - s4ClassRepresentation.slotsList.map { RS4ClassSlot(it.name, it.type) }, - s4ClassRepresentation.superClassesList, - s4ClassRepresentation.isVirtual)) - } - else { - val functionRepresentation = symbol.functionRepresentation - val extraNamedArguments = functionRepresentation.extraNamedArguments - RSkeletonAssignmentStub(skeletonFileStub, - R_SKELETON_ASSIGNMENT_STATEMENT, - symbol.name, - symbol.type, - functionRepresentation.parameters, - symbol.exported, - RExtraNamedArgumentsInfo(extraNamedArguments.argNamesList, - extraNamedArguments.funArgNamesList)) + when (symbol.representationCase) { + RepresentationCase.S4CLASSREPRESENTATION -> { + val s4ClassRepresentation = symbol.s4ClassRepresentation + RSkeletonCallExpressionStub(skeletonFileStub, + R_SKELETON_CALL_EXPRESSION, + RS4ClassInfo(symbol.name, + s4ClassRepresentation.packageName, + s4ClassRepresentation.slotsList.map { RS4ClassSlot(it.name, it.type) }, + s4ClassRepresentation.superClassesList, + s4ClassRepresentation.isVirtual), + null) + } + + RepresentationCase.R6CLASSREPRESENTATION -> { + val r6ClassRepresentation = symbol.r6ClassRepresentation + RSkeletonCallExpressionStub(skeletonFileStub, + R_SKELETON_CALL_EXPRESSION, + null, + R6ClassInfo(symbol.name, + r6ClassRepresentation.superClassesList, + r6ClassRepresentation.fieldsList.map { R6ClassField(it.name, it.isPublic) }, + r6ClassRepresentation.methodsList.map { R6ClassMethod(it.name, it.isPublic) }, + r6ClassRepresentation.activeBindingsList.map { R6ClassActiveBinding(it.name) })) + } + + else -> { + val functionRepresentation = symbol.functionRepresentation + val extraNamedArguments = functionRepresentation.extraNamedArguments + RSkeletonAssignmentStub(skeletonFileStub, + R_SKELETON_ASSIGNMENT_STATEMENT, + symbol.name, + symbol.type, + functionRepresentation.parameters, + symbol.exported, + RExtraNamedArgumentsInfo(extraNamedArguments.argNamesList, + extraNamedArguments.funArgNamesList)) + } } } return skeletonFileStub diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt index b1d39a6b5..fdb47c42d 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpression.kt @@ -8,6 +8,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.stubs.StubElement import com.intellij.util.IncorrectOperationException +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RArgumentList import org.jetbrains.r.psi.api.RCallExpression @@ -27,30 +28,17 @@ class RSkeletonCallExpression(private val myStub: RSkeletonCallExpressionStub) : override fun getElementType(): IStubElementType, *> = stub.stubType - override fun getName(): String = myStub.s4ClassInfo.className + override fun getName(): String = myStub.s4ClassInfo?.className ?: "" override fun canNavigate(): Boolean = false override fun getText(): String { - val info = stub.s4ClassInfo + val s4Info = stub.s4ClassInfo + val r6Info = stub.r6ClassInfo + return buildString { - append("setClass('").append(info.className).append("', ") - append("slots = c(") - info.slots.forEachIndexed { ind, slot -> - if (ind != 0) append(", ") - append(slot.name).append(" = '").append(slot.type).append("'") - } - append("), ") - append("contains = c(") - info.superClasses.forEachIndexed { ind, superClass -> - if (ind != 0) append(", ") - append("'").append(superClass).append("'") - } - if (info.isVirtual) { - if (info.superClasses.isNotEmpty()) append(", ") - append("'VIRTUAL'") - } - append("))") + if (s4Info != null) { append(s4Info.toString()) } + if (r6Info != null) { append(r6Info.toString()) } } } @@ -58,7 +46,9 @@ class RSkeletonCallExpression(private val myStub: RSkeletonCallExpressionStub) : throw IncorrectOperationException("Operation not supported in: $javaClass") } - override fun getAssociatedS4ClassInfo(): RS4ClassInfo = myStub.s4ClassInfo + override fun getAssociatedS4ClassInfo(): RS4ClassInfo? = myStub.s4ClassInfo + + override fun getAssociatedR6ClassInfo(): R6ClassInfo? = myStub.r6ClassInfo override fun getReference(): RReferenceBase<*>? = null diff --git a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt index 85b14e492..4cc91419c 100644 --- a/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt +++ b/src/org/jetbrains/r/skeleton/psi/RSkeletonCallExpressionElementType.kt @@ -11,23 +11,33 @@ import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubInputStream import com.intellij.psi.stubs.StubOutputStream import com.intellij.util.IncorrectOperationException +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.classes.s4.RS4ClassInfo import org.jetbrains.r.psi.api.RCallExpression -import org.jetbrains.r.psi.stubs.RS4ClassNameIndex import org.jetbrains.r.psi.stubs.RStubElementType +import org.jetbrains.r.psi.stubs.classes.R6ClassNameIndex +import org.jetbrains.r.psi.stubs.classes.RS4ClassNameIndex -class RSkeletonCallExpressionElementType : RStubElementType("R bin s4") { +class RSkeletonCallExpressionElementType : RStubElementType("R bin s4 r6") { override fun createPsi(stub: RSkeletonCallExpressionStub): RCallExpression { return RSkeletonCallExpression(stub) } override fun serialize(stub: RSkeletonCallExpressionStub, dataStream: StubOutputStream) { - stub.s4ClassInfo.serialize(dataStream) + dataStream.writeBoolean(stub.s4ClassInfo != null) + dataStream.writeBoolean(stub.r6ClassInfo != null) + + stub.s4ClassInfo?.serialize(dataStream) + stub.r6ClassInfo?.serialize(dataStream) } override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): RSkeletonCallExpressionStub { - val s4ClassInfo = RS4ClassInfo.deserialize(dataStream) - return RSkeletonCallExpressionStub(parentStub, this, s4ClassInfo) + val s4ClassExists = dataStream.readBoolean() + val r6ClassExists = dataStream.readBoolean() + + val s4ClassInfo = if (s4ClassExists) RS4ClassInfo.deserialize(dataStream) else null + val r6ClassInfo = if (r6ClassExists) R6ClassInfo.deserialize(dataStream) else null + return RSkeletonCallExpressionStub(parentStub, this, s4ClassInfo, r6ClassInfo) } override fun createStub(psi: RCallExpression, parentStub: StubElement<*>?): RSkeletonCallExpressionStub { @@ -39,7 +49,7 @@ class RSkeletonCallExpressionElementType : RStubElementType, elementType: RSkeletonCallExpressionElementType, - override val s4ClassInfo: RS4ClassInfo) - : StubBase(parent, elementType), RCallExpressionStub + override val s4ClassInfo: RS4ClassInfo?, + override val r6ClassInfo: R6ClassInfo?) + : StubBase(parent, elementType), RCallExpressionStub { + + override fun toString(): String { + return buildString { + append("RSkeletonCallExpressionStub:") + if (s4ClassInfo != null) { append("s4='${s4ClassInfo.className}'") } + if (r6ClassInfo != null) { append("r6='${r6ClassInfo.className}'") } + } + } +} diff --git a/test/org/jetbrains/r/RUsefulTestCase.kt b/test/org/jetbrains/r/RUsefulTestCase.kt index 5a4d6a5d3..897b5b3f9 100644 --- a/test/org/jetbrains/r/RUsefulTestCase.kt +++ b/test/org/jetbrains/r/RUsefulTestCase.kt @@ -259,6 +259,7 @@ abstract class RUsefulTestCase : BasePlatformTestCase() { grDevices magrittr methods + R6 stats utils roxygen2 diff --git a/test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt b/test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt new file mode 100644 index 000000000..74a6bd30d --- /dev/null +++ b/test/org/jetbrains/r/classes/RClassesUtilTestsBase.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes + +import com.intellij.psi.PsiElement +import org.jetbrains.r.RLanguage +import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RCallExpression +import org.jetbrains.r.psi.api.RFile +import org.jetbrains.r.run.RProcessHandlerBaseTestCase + +abstract class RClassesUtilTestsBase : RProcessHandlerBaseTestCase() { + override fun setUp() { + super.setUp() + addLibraries() + } + + protected fun getRCallExpressionFromAssignment(assignmentStatement: RAssignmentStatement): RCallExpression? { + if (assignmentStatement.children?.size!! < 3) return null + return assignmentStatement.children[2] as RCallExpression; + } + + protected fun getRootElementOfPsi(code: String): PsiElement { + val rFile = myFixture.configureByText("foo.R", code) as RFile + val viewProvider = rFile.getViewProvider(); + val rPsi = viewProvider.getPsi(RLanguage.INSTANCE) + return rPsi.originalElement.firstChild + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt new file mode 100644 index 000000000..0f7d2ba90 --- /dev/null +++ b/test/org/jetbrains/r/classes/r6/R6ClassInfoUtilTests.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.r6 + +import com.intellij.psi.util.PsiTreeUtil +import junit.framework.TestCase +import org.jetbrains.r.classes.RClassesUtilTestsBase +import org.jetbrains.r.psi.api.RAssignmentStatement +import org.jetbrains.r.psi.api.RCallExpression + +class R6ClassInfoUtilTests : RClassesUtilTestsBase() { + private val fullClassCodeDefinition = """ + Car <- R6Class("Car", + inherit = Vehicle, + public = list( + weight = 0, + speed = 0, + accelerate = function(acc = 1){ + self${"$"}speed <- self${"$"}speed + acc + invisible(self) + }, + slowDown = function(neg_acc = 1){ + self${"$"}speed <- self${"$"}speed - acc + invisible(self) + } + ), + private = list( + engine_rpm = 1000, + maximize = function(rmp = 1000) { + self${"$"}engine_rpm <- self${"$"}engine_rpm + rmp + invisible(self) + } + ), + active = list( + asd = 0, + random = function(value) { + if (missing(value)) { + runif(1) + } else { + stop("Can't set `${"$"}random`", call. = FALSE) + } + } + ) + ) + """.trimIndent() + + private val shortedClassCodeDefinition = """ + Car <- R6Class("Car", + inherit = Vehicle, + list( + weight = 0, + speed = 0, + accelerate = function(acc = 1){ + self${"$"}speed <- self${"$"}speed + acc + invisible(self) + }, + slowDown = function(neg_acc = 1){ + self${"$"}speed <- self${"$"}speed - acc + invisible(self) + } + ) + ) + """.trimIndent() + + fun testGetAssociatedClassName(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val className = R6ClassInfoUtil.getAssociatedClassNameFromR6ClassCall(rCallExpression!!) + assertEquals("Car", className) + } + + fun testGetAssociatedClassNameFromInstantiationCall(){ + val rAssignmentStatement = getRootElementOfPsi(""" + obj <- MyClass${'$'}new() + """.trimIndent()) as RAssignmentStatement + + val call = rAssignmentStatement.lastChild as RCallExpression + val className = R6ClassInfoUtil.getAssociatedClassNameFromInstantiationCall(call) + assertEquals("MyClass", className) + } + + fun testGetAssociatedSuperClasses(){ + val psiTree = getRootElementOfPsi(""" + SuperParentClass <- R6Class("SuperParentClass") + ParentClass <- R6Class("ParentClass", inherit = SuperParentClass) + ChildClass <- R6Class("ChildClass", inherit = ParentClass) + """.trimIndent()).parent + + val lastClassAssignment = PsiTreeUtil.getChildrenOfType(psiTree, RAssignmentStatement::class.java).last() as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(lastClassAssignment) + val superClassNames = R6ClassInfoUtil.getAssociatedSuperClassesHierarchy(rCallExpression!!) + + assertNotNull(superClassNames) + assert(superClassNames!!.contains("ParentClass")) + assert(superClassNames.contains("SuperParentClass")) + } + + fun testClassContainsFields(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) + + assertNotNull(classFields) + + val classFieldsNames = classFields!!.map { it.name } + assertContainsElements(classFieldsNames, "weight") + assertEquals(true, classFields[0].isPublic) + + assertContainsElements(classFieldsNames, "speed") + assertEquals(true, classFields[1].isPublic) + + assertContainsElements(classFieldsNames, "engine_rpm") + assertEquals(false, classFields[2].isPublic) + } + + fun testClassContainsMethods(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) + + assertNotNull(classMethods) + + val classMethodsNames = classMethods!!.map { it.name } + assertContainsElements(classMethodsNames, "accelerate") + assertEquals(classMethods[0].isPublic, true) + + assertContainsElements(classMethodsNames, "slowDown") + assertEquals(classMethods[1].isPublic, true) + + assertContainsElements(classMethodsNames, "maximize") + assertEquals(classMethods[2].isPublic, false) + } + + fun testGetAssociatedActiveBindings(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classActiveBindings = R6ClassInfoUtil.getAssociatedActiveBindings(rCallExpression!!) + + assertNotNull(classActiveBindings) + assertEquals(classActiveBindings!!.size, 1) + + val classActiveBindingsNames = classActiveBindings.map { it.name } + assertContainsElements(classActiveBindingsNames, "random") + } + + fun testGetShortenedClassAssociatedFields(){ + val rAssignmentStatement = getRootElementOfPsi(shortedClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classFields = R6ClassInfoUtil.getAssociatedFields(rCallExpression!!) + + assertNotNull(classFields) + + val classFieldsNames = classFields!!.map { it.name } + assertContainsElements(classFieldsNames, "weight") + assertContainsElements(classFieldsNames, "speed") + classFields.forEach { assertEquals(true, it.isPublic) } + } + + fun testGetShortenedClassAssociatedMethods(){ + val rAssignmentStatement = getRootElementOfPsi(shortedClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classMethods = R6ClassInfoUtil.getAssociatedMethods(rCallExpression!!) + + assertNotNull(classMethods) + + val classMethodsNames = classMethods!!.map { it.name } + assertContainsElements(classMethodsNames, "accelerate") + assertContainsElements(classMethodsNames, "slowDown") + classMethods.forEach { assertEquals(true, it.isPublic) } + } + + fun testGetMembers(){ + val rAssignmentStatement = getRootElementOfPsi(fullClassCodeDefinition) as RAssignmentStatement + val rCallExpression = getRCallExpressionFromAssignment(rAssignmentStatement) + val classMethods = R6ClassInfoUtil.getAssociatedMembers(rCallExpression!!) + + assertNotNull(classMethods) + + val classMethodsNames = classMethods!!.map { it.name } + assertContainsElements(classMethodsNames, "weight") + assertContainsElements(classMethodsNames, "speed") + assertContainsElements(classMethodsNames, "engine_rpm") + assertContainsElements(classMethodsNames, "maximize") + assertContainsElements(classMethodsNames, "accelerate") + assertContainsElements(classMethodsNames, "slowDown") + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt b/test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt new file mode 100644 index 000000000..15fdb6eb4 --- /dev/null +++ b/test/org/jetbrains/r/classes/s4/RS4ClassInfoUtilTests.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.classes.s4 + +import org.jetbrains.r.classes.RClassesUtilTestsBase +import org.jetbrains.r.psi.api.RCallExpression + +class RS4ClassInfoUtilTests : RClassesUtilTestsBase() { + private val setClassCode = """ + setClass("Person", + contains = "Being", + slots = c( + name = "character", + age = "numeric" + ) + ) + """.trimIndent() + + fun testGetAssociatedClassName(){ + val setClassExpression = getRootElementOfPsi(setClassCode) as RCallExpression + val className = RS4ClassInfoUtil.getAssociatedClassName(setClassExpression) + + assertEquals("Person", className) + } + + fun testGetAllAssociatedSlots(){ + val setClassExpression = getRootElementOfPsi(setClassCode) as RCallExpression + val classSlots = RS4ClassInfoUtil.getAllAssociatedSlots(setClassExpression) + + assertEquals(2, classSlots.size) + assertEquals(classSlots[0].name, "name") + assertEquals(classSlots[0].type, "character") + assertEquals(classSlots[1].name, "age") + assertEquals(classSlots[1].type, "numeric") + } + + fun testGetAllAssociatedSuperClasses(){ + val setClassExpression = getRootElementOfPsi(setClassCode) as RCallExpression + val superClasses = RS4ClassInfoUtil.getAllAssociatedSuperClasses(setClassExpression) + + assertEquals(1, superClasses.size) + assertEquals(superClasses[0], "Being") + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/completion/AutoPopupTest.kt b/test/org/jetbrains/r/completion/AutoPopupTest.kt index df57a400e..8d8fe0f3d 100644 --- a/test/org/jetbrains/r/completion/AutoPopupTest.kt +++ b/test/org/jetbrains/r/completion/AutoPopupTest.kt @@ -10,6 +10,7 @@ import com.intellij.util.ThrowableRunnable import org.jetbrains.r.RFileType import org.jetbrains.r.RLightCodeInsightFixtureTestCase import org.jetbrains.r.classes.s4.RS4ClassInfo +import org.jetbrains.r.classes.r6.R6ClassInfo import org.jetbrains.r.console.RConsoleRuntimeInfo import org.jetbrains.r.console.addRuntimeInfo import org.jetbrains.r.hints.parameterInfo.RExtraNamedArgumentsInfo @@ -198,6 +199,10 @@ class AutoPopupTest : RLightCodeInsightFixtureTestCase() { throw NotImplementedError() } + override fun loadR6ClassInfoByObjectName(objectName: String): R6ClassInfo? { + throw NotImplementedError() + } + override fun loadExtraNamedArguments(functionName: String, functionExpression: RFunctionExpression): RExtraNamedArgumentsInfo { throw NotImplementedError() } diff --git a/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt new file mode 100644 index 000000000..0b673b1cc --- /dev/null +++ b/test/org/jetbrains/r/completion/classes/R6ClassCompletionTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.completion.classes + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementPresentation +import org.jetbrains.r.console.RConsoleRuntimeInfoImpl +import org.jetbrains.r.console.RConsoleView +import org.jetbrains.r.console.addRuntimeInfo +import org.jetbrains.r.run.RProcessHandlerBaseTestCase + +class R6ClassCompletionTest : RProcessHandlerBaseTestCase() { + + override fun setUp() { + super.setUp() + addLibraries() + } + + fun testUserClassWithSingleField() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0 )) + obj <- MyClass${"$"}new() + obj$ + """.trimIndent(), "clone" to "", "someField" to "") + } + + fun testUserClassWithSingleFieldChainedUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0 )) + obj <- MyClass${"$"}new() + obj${'$'}someField${'$'} + """.trimIndent(), "clone" to "", "someField" to "") + } + + fun testUserClassForInternalUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, add = function(x = 1) { self$ } )) + """.trimIndent(), "add" to "", "clone" to "", "someField" to "") + } + + fun testUserClassWithSeveralMembers() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) } )) + obj <- MyClass${"$"}new() + obj$ + """.trimIndent(), "clone" to "", "someField" to "", "someMethod" to "") + } + + fun testUserClassWithFieldMethodActiveBinding() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } )) + obj <- MyClass${"$"}new() + obj$ + """.trimIndent(), "clone" to "", "random" to "", "someField" to "", "someMethod" to "") + } + + fun testInheritedUserClassFieldsVisibility() { + doTest(""" + ParentClass <- R6Class("ParentClass", list( someField = 0 )) + ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) + obj <- ChildClass${"$"}new() + obj${'$'}someField${'$'} + """.trimIndent(), "add" to "", "clone" to "", "someField" to "") + } + + fun testUserClassNameSuggestion() { + doTest(""" + MyClass <- R6Class() + """.trimIndent(), "MyClass" to "string") + } + + fun testSetMemberVisibilityModifierCompletionToUserClass() { + doTest(""" + MyClass <- R6Class("MyClass", list(someField = 0)) + MyClass${'$'}set() + """.trimIndent(), "\"active\"" to "string", "\"public\"" to "string", "\"private\"" to "string") + } + + fun testConsoleMembersSuggestion() { + rInterop.executeCode(""" + library(R6) + MyClass <- R6Class("MyClass", public = list(someField = 0, someMethod = function (x = 1) { print(x) }, random = function() { print('it is a random active binding') } ), + private = list(somePrivateField = 0, somePrivateMethod = function(x = 1) { print(x) } ), + active = list(someActiveFunction = function(x) { print(x) } ) + ) + obj <- MyClass${'$'}new() + """.trimIndent()) + doTest("obj${'$'}", "clone" to "", "random" to "", "someField" to "", "someMethod" to "", "someActiveFunction" to "", + withRuntimeInfo = true, inConsole = true,) + } + + private fun doWrongVariantsTest(text: String, vararg variants: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false) { + val result = doTestBase(text, withRuntimeInfo, inConsole) + assertNotNull(result) + val lookupStrings = result.map { it.lookupString } + assertDoesntContain(lookupStrings, *variants) + } + + private fun doTest(text: String, + vararg variants: Pair, // + withRuntimeInfo: Boolean = false, + inConsole: Boolean = false) { + val result = doTestBase(text, withRuntimeInfo, inConsole) + assertNotNull(result) + val lookupStrings = result.map { + val elementPresentation = LookupElementPresentation() + it.renderElement(elementPresentation) + elementPresentation.itemText to elementPresentation.typeText + } + + variants.forEach { expectedSuggestion -> + assert(lookupStrings.any { it.first == expectedSuggestion.first }) + } + } + + private fun doTestBase(text: String, withRuntimeInfo: Boolean = false, inConsole: Boolean = false): Array { + myFixture.configureByText("foo.R", text) + if (inConsole) { + myFixture.file.putUserData(RConsoleView.IS_R_CONSOLE_KEY, true) + } + if (withRuntimeInfo) { + myFixture.file.addRuntimeInfo(RConsoleRuntimeInfoImpl(rInterop)) + } + return myFixture.completeBasic() + } +} diff --git a/test/org/jetbrains/r/completion/S4ClassCompletionTest.kt b/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt similarity index 99% rename from test/org/jetbrains/r/completion/S4ClassCompletionTest.kt rename to test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt index e8efaca00..cd78df20c 100644 --- a/test/org/jetbrains/r/completion/S4ClassCompletionTest.kt +++ b/test/org/jetbrains/r/completion/classes/S4ClassCompletionTest.kt @@ -1,8 +1,8 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.completion +package org.jetbrains.r.completion.classes import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementPresentation diff --git a/test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt b/test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt new file mode 100644 index 000000000..1d56e43b1 --- /dev/null +++ b/test/org/jetbrains/r/findUsages/FindUsagesTestBase.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.findUsages + +import com.intellij.testFramework.UsefulTestCase +import com.intellij.usages.PsiElementUsageTarget +import com.intellij.usages.UsageTargetUtil +import org.intellij.lang.annotations.Language +import org.jetbrains.r.run.RProcessHandlerBaseTestCase + +abstract class FindUsagesTestBase : RProcessHandlerBaseTestCase() { + override fun setUp() { + super.setUp() + addLibraries() + } + + fun doTest(@Language("R") code: String, expected: String) { + myFixture.configureByText("test.R", code.trimIndent()) + val element = myFixture.elementAtCaret + val targets = UsageTargetUtil.findUsageTargets(element) + assertNotNull(targets) + assertTrue(targets.size > 0) + val target = (targets[0] as PsiElementUsageTarget).element + val actual = myFixture.getUsageViewTreeTextRepresentation(target) + UsefulTestCase.assertSameLines(expected.trimIndent(), actual) + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt new file mode 100644 index 000000000..d5a457d02 --- /dev/null +++ b/test/org/jetbrains/r/findUsages/R6FindUsagesTest.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.findUsages + +class R6FindUsagesTest : FindUsagesTestBase() { + override fun setUp() { + super.setUp() + addLibraries() + } + + fun testR6ClassFieldOutOfClassUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someField + obj${'$'}someField + """, """ + Usage (2 usages) + Variable + someField = 0 + Found usages (2 usages) + Unclassified (2 usages) + light_idea_test_case (2 usages) + (2 usages) + 3obj${"$"}someField + 4obj${"$"}someField + """) + } + + fun testR6ClassFunctionOutOfClassUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someMethod() + obj${'$'}someField + """, """ + Usage (1 usage) + Variable + someMethod = function(x = 1) { print(x) } + Found usages (1 usage) + Unclassified (1 usage) + light_idea_test_case (1 usage) + (1 usage) + 3obj${"$"}someMethod() + """) + } + + fun testR6ClassFieldOutOfClassChainedUsage() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someField + obj${'$'}someMethod()${'$'}someField + """, """ + Usage (2 usages) + Variable + someField = 0 + Found usages (2 usages) + Unclassified (2 usages) + light_idea_test_case (2 usages) + (2 usages) + 3obj${'$'}someField + 4obj${'$'}someMethod()${'$'}someField + """) + } + + fun testR6ClassInsideMethodFieldUsage(){ + doTest(""" + Accumulator <- R6Class("Accumulator", list( + sum = 0, + add = function(x = 1) { + self${"$"}sum <- self${"$"}sum + x + invisible(self) + }) + ) + + x <- Accumulator${"$"}new() + x${"$"}add(4)${"$"}sum + x${"$"}sum + """, """ + Usage (3 usages) + Variable + sum = 0 + Found usages (3 usages) + Unclassified (3 usages) + light_idea_test_case (3 usages) + (3 usages) + 4self${'$'}sum <- self${'$'}sum + x + 10x${'$'}add(4)${'$'}sum + 11x${'$'}sum + """) + } + + fun testR6ClassFieldFromDefinition() { + doTest(""" + MyClass <- R6Class("MyClass", list( someField = 0, someMethod = function(x = 1) { print(x) } )) + obj <- MyClass${'$'}new() + obj${'$'}someField + obj${'$'}someField + """, """ + Usage (2 usages) + Variable + someField + Found usages (2 usages) + Unclassified (2 usages) + light_idea_test_case (2 usages) + (2 usages) + 3obj${"$"}someField + 4obj${"$"}someField + """) + } + + fun testR6ClassFieldWithSuperClass() { + doTest(""" + ParentClass <- R6Class("ParentClass", list( someField = 0 )) + ChildClass <- R6Class("ChildClass", inherit = ParentClass, list( add = function(x = 1) { print(x) } )) + obj <- ChildClass${'$'}new() + obj${'$'}someField + """, """ + Usage (1 usage) + Variable + someField = 0 + Found usages (1 usage) + Unclassified (1 usage) + light_idea_test_case (1 usage) + (1 usage) + 4obj${'$'}someField + """) + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/findUsages/RFindUsagesTest.kt b/test/org/jetbrains/r/findUsages/RCommonFindUsagesTest.kt similarity index 71% rename from test/org/jetbrains/r/findUsages/RFindUsagesTest.kt rename to test/org/jetbrains/r/findUsages/RCommonFindUsagesTest.kt index 5447c3733..4d6ed63e5 100644 --- a/test/org/jetbrains/r/findUsages/RFindUsagesTest.kt +++ b/test/org/jetbrains/r/findUsages/RCommonFindUsagesTest.kt @@ -10,13 +10,7 @@ import com.intellij.usages.UsageTargetUtil import org.intellij.lang.annotations.Language import org.jetbrains.r.run.RProcessHandlerBaseTestCase -class RFindUsagesTest : RProcessHandlerBaseTestCase() { - - override fun setUp() { - super.setUp() - addLibraries() - } - +class RCommonFindUsagesTest : FindUsagesTestBase() { fun testLocalVariable() { doTest(""" my.local.variable <- 10 @@ -33,9 +27,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 2print(my.local.variable) - 5print(my.local.variable + 20) + 2print(my.local.variable) + 5print(my.local.variable + 20) """) } @@ -56,9 +49,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 2print(my.local.function(2, 3)) - 5print(my.local.function(4, 5)) + 2print(my.local.function(2, 3)) + 5print(my.local.function(4, 5)) """) } @@ -74,9 +66,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 1base.package <- packageDescription("base") - 2dplyr.package <- packageDescription("dplyr") + 1base.package <- packageDescription("base") + 2dplyr.package <- packageDescription("dplyr") """) } @@ -97,9 +88,8 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (2 usages) light_idea_test_case (2 usages) (2 usages) - test.R (2 usages) - 2x + y + z - 7func(x = p) + 2x + y + z + 7func(x = p) """) } @@ -124,13 +114,11 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 10x + y + z + 10x + y + z Usage in roxygen2 documentation (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 5#' @param x, y X and y + 5#' @param x, y X and y """) } @@ -157,24 +145,11 @@ class RFindUsagesTest : RProcessHandlerBaseTestCase() { Unclassified (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 10bar(x) + y + z + 10bar(x) + y + z Usage in roxygen2 documentation (1 usage) light_idea_test_case (1 usage) (1 usage) - test.R (1 usage) - 5#' @see [bar] + 5#' @see [bar] """) } - - private fun doTest(@Language("R") code: String, expected: String) { - myFixture.configureByText("test.R", code.trimIndent()) - val element = myFixture.elementAtCaret - val targets = UsageTargetUtil.findUsageTargets(element) - assertNotNull(targets) - assertTrue(targets.size > 0) - val target = (targets[0] as PsiElementUsageTarget).element - val actual = myFixture.getUsageViewTreeTextRepresentation(target) - UsefulTestCase.assertSameLines(expected.trimIndent(), actual) - } } \ No newline at end of file diff --git a/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt new file mode 100644 index 000000000..2d2fc3701 --- /dev/null +++ b/test/org/jetbrains/r/inspections/classes/r6/UnmatchingR6ClassNameInspectionTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package org.jetbrains.r.inspections.classes.r6 + +import org.jetbrains.r.RBundle +import org.jetbrains.r.inspections.RInspectionTest + +class UnmatchingR6ClassNameInspectionTest : RInspectionTest() { + override fun setUp() { + super.setUp() + addLibraries() + } + + fun testClassNameInspection() { + doExprTest(""" + UserClass <- R6Class("UserClass") + """.trimIndent()) + + doExprTest(""" + UserClass <- R6Class(${makeError("MyClass")}) + """.trimIndent()) + } + + override val inspection = UnmatchingR6ClassNameInspection::class.java + + companion object { + private fun msg(argumentName: String) = RBundle.message("inspection.r6class.naming.convention.classname", argumentName) + + private fun makeError(className: String): String { + return "'$className'" + } + } +} \ No newline at end of file diff --git a/test/org/jetbrains/r/inspections/DeprecatedSetClassArgsInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspectionTest.kt similarity index 91% rename from test/org/jetbrains/r/inspections/DeprecatedSetClassArgsInspectionTest.kt rename to test/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspectionTest.kt index 40100f427..9bd2875fa 100644 --- a/test/org/jetbrains/r/inspections/DeprecatedSetClassArgsInspectionTest.kt +++ b/test/org/jetbrains/r/inspections/classes/s4/DeprecatedSetClassArgsInspectionTest.kt @@ -1,11 +1,11 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections +package org.jetbrains.r.inspections.classes.s4 import org.jetbrains.r.RBundle -import org.jetbrains.r.inspections.s4class.DeprecatedSetClassArgsInspection +import org.jetbrains.r.inspections.RInspectionTest class DeprecatedSetClassArgsInspectionTest : RInspectionTest() { diff --git a/test/org/jetbrains/r/inspections/InstanceOfVirtualS4ClassInspectionTest.kt b/test/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspectionTest.kt similarity index 87% rename from test/org/jetbrains/r/inspections/InstanceOfVirtualS4ClassInspectionTest.kt rename to test/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspectionTest.kt index a84dc8884..e56e07dab 100644 --- a/test/org/jetbrains/r/inspections/InstanceOfVirtualS4ClassInspectionTest.kt +++ b/test/org/jetbrains/r/inspections/classes/s4/InstanceOfVirtualS4ClassInspectionTest.kt @@ -1,11 +1,11 @@ /* - * Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + * Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ -package org.jetbrains.r.inspections +package org.jetbrains.r.inspections.classes.s4 import org.jetbrains.r.RBundle -import org.jetbrains.r.inspections.s4class.InstanceOfVirtualS4ClassInspection +import org.jetbrains.r.inspections.RInspectionTest class InstanceOfVirtualS4ClassInspectionTest : RInspectionTest() { diff --git a/test/org/jetbrains/r/rename/RRenameTest.kt b/test/org/jetbrains/r/rename/RRenameTest.kt index c27eda4c8..7ca6419b3 100644 --- a/test/org/jetbrains/r/rename/RRenameTest.kt +++ b/test/org/jetbrains/r/rename/RRenameTest.kt @@ -91,22 +91,41 @@ class RRenameTest : RLightCodeInsightFixtureTestCase() { fun testRenameDocumentationFunctionLink() = doTestWithProject("baz") - private fun doTestWithProject(newName: String, isInlineAvailable: Boolean = true, isRmd: Boolean = false, isSourceTest: Boolean = false) { + fun testRenameR6FieldFromUsage() = doTestWithProject("summary", isInOtherDirectory = true) + + fun testRenameR6MethodFromUsage() = doTestWithProject("additiveOperator", isInOtherDirectory = true) + + fun testRenameR6ActiveBindingFromUsage() = doTestWithProject("rnd", isInOtherDirectory = true) + + private fun doTestWithProject(newName: String, isInlineAvailable: Boolean = true, isRmd: Boolean = false, isSourceTest: Boolean = false, isInOtherDirectory: Boolean = false) { val dotFileExtension = getDotExtension(isRmd) lateinit var startFiles: List lateinit var endFiles: List - if (isSourceTest) { - addLibraries() - val files = File(myFixture.testDataPath + "/rename/" + getTestName(true)) - .listFiles() - ?.map { it.absolutePath.replace(myFixture.testDataPath, "") } - ?.sortedByDescending { it.contains("main") } ?: error("Cannot find root test directory") - startFiles = files.filter { !it.contains(".after.") } - endFiles = files - startFiles - myFixture.configureByFiles(*startFiles.toTypedArray()) - } - else { - myFixture.configureByFile("rename/" + getTestName(true) + dotFileExtension) + + when { + isInOtherDirectory -> { + addLibraries() + val files = File(myFixture.testDataPath + "/rename/").walkBottomUp() + .filter { it -> it.name.contains(getTestName(true)) } + .map { it.absolutePath.replace(myFixture.testDataPath, "") } + .sortedByDescending { it.contains("main") }.toList() ?: error("Cannot find root test directory") + startFiles = files.filter { !it.contains(".after.") } + endFiles = files - startFiles + myFixture.configureByFiles(*startFiles.toTypedArray()) + } + isSourceTest -> { + addLibraries() + val files = File(myFixture.testDataPath + "/rename/" + getTestName(true)) + .listFiles() + ?.map { it.absolutePath.replace(myFixture.testDataPath, "") } + ?.sortedByDescending { it.contains("main") } ?: error("Cannot find root test directory") + startFiles = files.filter { !it.contains(".after.") } + endFiles = files - startFiles + myFixture.configureByFiles(*startFiles.toTypedArray()) + } + else -> { + myFixture.configureByFile("rename/" + getTestName(true) + dotFileExtension) + } } val variableHandler = RVariableInplaceRenameHandler() val memberHandler = RMemberInplaceRenameHandler() @@ -125,7 +144,7 @@ class RRenameTest : RLightCodeInsightFixtureTestCase() { } CodeInsightTestUtil.doInlineRename(handler, newName, myFixture) - if (isSourceTest) { + if (isSourceTest || isInOtherDirectory) { if (endFiles.size != startFiles.size) error("Different number of start and end files") for (i in startFiles.indices) { myFixture.checkResultByFile(startFiles[i], endFiles[i], false) diff --git a/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R new file mode 100644 index 000000000..cc9cc9c09 --- /dev/null +++ b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.R @@ -0,0 +1,17 @@ +Accumulator <- R6Class("Accumulator", list( + summary = 0, + add = function(x = 1) { + self$summary <- self$summary + x + invisible(self) + }, + random = function(value) { + if (missing(value)) { + runif(1) + } else { + stop("Can't set `$random`", call. = FALSE) + } + }) +) + +x <- Accumulator$new() +x$random \ No newline at end of file diff --git a/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R new file mode 100644 index 000000000..368f402c8 --- /dev/null +++ b/testData/rename/classes/R6/renameR6ActiveBindingFromUsage.after.R @@ -0,0 +1,17 @@ +Accumulator <- R6Class("Accumulator", list( + summary = 0, + add = function(x = 1) { + self$summary <- self$summary + x + invisible(self) + }, + rnd = function(value) { + if (missing(value)) { + runif(1) + } else { + stop("Can't set `$random`", call. = FALSE) + } + }) +) + +x <- Accumulator$new() +x$rnd \ No newline at end of file diff --git a/testData/rename/classes/R6/renameR6FieldFromUsage.R b/testData/rename/classes/R6/renameR6FieldFromUsage.R new file mode 100644 index 000000000..5ed7f7f47 --- /dev/null +++ b/testData/rename/classes/R6/renameR6FieldFromUsage.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + sum = 0, + add = function(x = 1) { + self$sum <- self$sum + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$sum$add(4)$sum$add(4)$sum +x$sum diff --git a/testData/rename/classes/R6/renameR6FieldFromUsage.after.R b/testData/rename/classes/R6/renameR6FieldFromUsage.after.R new file mode 100644 index 000000000..a480bb3f3 --- /dev/null +++ b/testData/rename/classes/R6/renameR6FieldFromUsage.after.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + summary = 0, + add = function(x = 1) { + self$summary <- self$summary + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$summary$add(4)$summary$add(4)$summary +x$summary diff --git a/testData/rename/classes/R6/renameR6MethodFromUsage.R b/testData/rename/classes/R6/renameR6MethodFromUsage.R new file mode 100644 index 000000000..a457981fd --- /dev/null +++ b/testData/rename/classes/R6/renameR6MethodFromUsage.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + sum = 0, + add = function(x = 1) { + self$sum <- self$sum + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$add(4)$sum +x$sum diff --git a/testData/rename/classes/R6/renameR6MethodFromUsage.after.R b/testData/rename/classes/R6/renameR6MethodFromUsage.after.R new file mode 100644 index 000000000..eae41bb57 --- /dev/null +++ b/testData/rename/classes/R6/renameR6MethodFromUsage.after.R @@ -0,0 +1,11 @@ +Accumulator <- R6Class("Accumulator", list( + sum = 0, + additiveOperator = function(x = 1) { + self$sum <- self$sum + x + invisible(self) + }) +) + +x <- Accumulator$new() +x$additiveOperator(4)$sum +x$sum