Skip to content

Commit

Permalink
Features/#360 PEPs configuration support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kirill Golovin authored and Kirill Golovin committed Aug 16, 2021
1 parent 698a405 commit 4911dcf
Show file tree
Hide file tree
Showing 18 changed files with 405 additions and 181 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pluginVerifierIdeVersions = 2021.1.1, 2021.1.2, 2021.1.3, 2021.2
# !!!! Uncomment one of the following settings: either pycharmPath or pythonPlugin !!!!
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

platformPlugins =
platformPlugins = org.jetbrains.plugins.yaml

# -- PyCharm: --
# * Run Snakemake plugin inside PyCharm. The IDE will be downloaded automatically
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ class ImplicitPySymbolsProvider(
)
progressIndicator?.checkCanceled()

//collect pep methods
collectClassInstanceMethods(
listOf(
"peppy.project" to "Project"
), SmkCodeInsightScope.PEP_SECTION, usedFiles, sdk, elementsCache
)
progressIndicator?.checkCanceled()

///////////////////////////////////////
// Collect hardcoded classes

Expand Down Expand Up @@ -211,11 +219,11 @@ class ImplicitPySymbolsProvider(

val packages = PyPackageManager.getInstance(sdk).packages

val pkg1 = packages?.firstOrNull() { it.name == SnakemakeAPI.SMK_API_PKG_NAME_SMK}
val pkg1 = packages?.firstOrNull() { it.name == SnakemakeAPI.SMK_API_PKG_NAME_SMK }
if (pkg1 != null) {
return pkg1.matches(requirement1)
}
val pkg2 = packages?.firstOrNull() { it.name == SnakemakeAPI.SMK_API_PKG_NAME_SMK_MINIMAL}
val pkg2 = packages?.firstOrNull() { it.name == SnakemakeAPI.SMK_API_PKG_NAME_SMK_MINIMAL }
if (pkg2 != null) {
return pkg2.matches(requirement2)
}
Expand Down Expand Up @@ -356,29 +364,23 @@ class ImplicitPySymbolsProvider(

private fun collectClassInstanceMethods(
moduleAndClass: List<Pair<String, String>>,
methods: List<String>,
scope: SmkCodeInsightScope,
usedFiles: MutableSet<VirtualFile>,
sdk: Sdk,
elementsCache: MutableList<ImplicitPySymbol>,
checkInheritedMethods: Boolean = false
) {
processClasses(moduleAndClass, usedFiles, sdk) { pyClass ->
methods.forEach { methodName ->
//val ctx = TypeEvalContext.userInitiated(
// pyClass.project,
// pyClass.originalElement.containingFile
//)
val ctx = null
// val ctx = TypeEvalContext.codeInsightFallback(pyClass.project)
val method = pyClass.findMethodByName(
methodName, checkInheritedMethods, ctx
)

//val ctx = TypeEvalContext.userInitiated(
// pyClass.project,
// pyClass.originalElement.containingFile
//)
val ctx = null
// val ctx = TypeEvalContext.codeInsightFallback(pyClass.project)
pyClass.methods.forEach { method ->
if (method != null) {
elementsCache.add(ImplicitPySymbol(method.name!!, method, scope, ImplicitPySymbolUsageType.METHOD))
}

}
}
}
Expand All @@ -400,7 +402,14 @@ class ImplicitPySymbolsProvider(
// val ctx = TypeEvalContext.codeInsightFallback(pyClass.project)
val constructor = pyClass.findInitOrNew(false, ctx)
if (constructor != null) {
elementsCache.add(ImplicitPySymbol(pyClass.name!!, constructor, scope, ImplicitPySymbolUsageType.METHOD))
elementsCache.add(
ImplicitPySymbol(
pyClass.name!!,
constructor,
scope,
ImplicitPySymbolUsageType.METHOD
)
)
}
}
}
Expand All @@ -424,19 +433,19 @@ class ImplicitPySymbolsProvider(
sdk: Sdk,
processor: (PyClass) -> Unit
) {
moduleAndClass.forEach { (pyModuleFqn, className) ->
val pyFiles = collectPyFiles(pyModuleFqn, usedFiles, sdk)

pyFiles
.asSequence()
.filter { it.isValid }
.mapNotNull { it.findTopLevelClass(className) }
.filter { it.name != null }
.forEach { pyClass ->
processor(pyClass)
}
}
}
moduleAndClass.forEach { (pyModuleFqn, className) ->
val pyFiles = collectPyFiles(pyModuleFqn, usedFiles, sdk)

pyFiles
.asSequence()
.filter { it.isValid }
.mapNotNull { it.findTopLevelClass(className) }
.filter { it.name != null }
.forEach { pyClass ->
processor(pyClass)
}
}
}

private fun collectTopLevelClassesInheretedFrom(
pyModuleFqn: String,
Expand Down Expand Up @@ -509,11 +518,14 @@ class ImplicitPySymbolsProvider(
val resolveContext = fromSdk(project, sdk)
////////////////


val pyFiles = resolveQualifiedName(
QualifiedName.fromDottedString(pyModuleFqn),
resolveContext
).filterIsInstance<PyFile>()

pyFiles.forEach {
print(it)
}
pyFiles.forEach { usedFiles.add(it.virtualFile) }
return pyFiles
}
Expand Down Expand Up @@ -606,7 +618,7 @@ class ImplicitPySymbolsProvider(
usedFiles.add(workflowFile.virtualFile)
}
globals["workflow"] = workflowFile?.findTopLevelClass("Workflow")

var pepFile = collectPyFiles("peppy.project", usedFiles, sdk).firstOrNull()
// Snakemake >= 6.5
var commonFile = collectPyFiles("snakemake.common.__init__", usedFiles, sdk).firstOrNull()
if (commonFile == null) {
Expand All @@ -622,6 +634,7 @@ class ImplicitPySymbolsProvider(
globals[SnakemakeAPI.SMK_VARS_SCATTER] = commonFile?.findTopLevelClass("Scatter")
globals[SnakemakeAPI.SMK_VARS_GATHER] = commonFile?.findTopLevelClass("Gather")
globals[SnakemakeAPI.SMK_VARS_CONFIG] = null
globals[SnakemakeAPI.SMK_VARS_PEP] = pepFile?.findTopLevelClass("Project")

val checkpointsFile = collectPyFiles("snakemake.checkpoints", usedFiles, sdk).firstOrNull()
if (checkpointsFile != null) {
Expand Down Expand Up @@ -668,8 +681,8 @@ private class ImplicitPySymbolsCacheImpl(
override val contentVersion: Int = 0
) : ImplicitPySymbolsCache {

private val scope2Symbols = symbols.groupBy { it.scope}
private val scope2SyntheticSymbols = syntheticSymbols.groupBy ({ it.first }, { it.second })
private val scope2Symbols = symbols.groupBy { it.scope }
private val scope2SyntheticSymbols = syntheticSymbols.groupBy({ it.first }, { it.second })

override operator fun get(scope: SmkCodeInsightScope) = validElements(scope2Symbols[scope] ?: emptyList())
override fun getSynthetic(scope: SmkCodeInsightScope) = scope2SyntheticSymbols[scope] ?: emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ import com.jetbrains.snakecharm.lang.psi.SmkRunSection

enum class SmkCodeInsightScope {
TOP_LEVEL,
RULELIKE_RUN_SECTION;
RULELIKE_RUN_SECTION,
PEP_SECTION;

fun includes(second: SmkCodeInsightScope) = when (this) {
TOP_LEVEL -> second == TOP_LEVEL
RULELIKE_RUN_SECTION -> second == TOP_LEVEL || second == RULELIKE_RUN_SECTION
TOP_LEVEL -> second == TOP_LEVEL
RULELIKE_RUN_SECTION -> second == TOP_LEVEL || second == RULELIKE_RUN_SECTION
PEP_SECTION -> second == PEP_SECTION
}

companion object {

private fun isPep(anchor: PsiElement): Boolean =
anchor.text == "pep"

operator fun get(anchor: PsiElement) = when {
isPep(anchor) -> PEP_SECTION
PsiTreeUtil.getParentOfType(anchor, SmkRunSection::class.java) != null -> RULELIKE_RUN_SECTION
else -> TOP_LEVEL
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.ALLOWED_LAMBDA_OR_CALLABLE_ARGS
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SECTION_ACCESSOR_CLASSES
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SMK_VARS_CHECKPOINTS
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SMK_VARS_CONFIG
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SMK_VARS_PEP
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SMK_VARS_RULES
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SMK_VARS_WILDCARDS
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.WILDCARDS_ACCESSOR_CLASS
Expand All @@ -20,10 +22,7 @@ import com.jetbrains.snakecharm.lang.SnakemakeNames.SECTION_OUTPUT
import com.jetbrains.snakecharm.lang.SnakemakeNames.SECTION_RESOURCES
import com.jetbrains.snakecharm.lang.psi.*
import com.jetbrains.snakecharm.lang.psi.impl.SmkPsiUtil
import com.jetbrains.snakecharm.lang.psi.types.SmkCheckpointType
import com.jetbrains.snakecharm.lang.psi.types.SmkRuleLikeSectionArgsType
import com.jetbrains.snakecharm.lang.psi.types.SmkRulesType
import com.jetbrains.snakecharm.lang.psi.types.SmkWildcardsType
import com.jetbrains.snakecharm.lang.psi.types.*
import com.jetbrains.snakecharm.stringLanguage.SmkSLanguage
import com.jetbrains.snakecharm.stringLanguage.lang.psi.SmkSLReferenceExpressionImpl

Expand All @@ -40,9 +39,9 @@ class SmkTypeProvider : PyTypeProviderBase() {
// getCallableType(callable, context) // e.g. method calls

override fun getReferenceType(
referenceTarget: PsiElement,
context: TypeEvalContext,
anchor: PsiElement?
referenceTarget: PsiElement,
context: TypeEvalContext,
anchor: PsiElement?
): Ref<PyType>? {
if (!SmkPsiUtil.isInsideSnakemakeOrSmkSLFile(anchor)) {
return null
Expand All @@ -66,9 +65,9 @@ class SmkTypeProvider : PyTypeProviderBase() {
}

private fun getSectionAccessorInRunSection(
referenceTarget: PyClass,
anchor: PyReferenceExpression,
context: TypeEvalContext
referenceTarget: PyClass,
anchor: PyReferenceExpression,
context: TypeEvalContext
): Ref<PyType>? {

val fqn = referenceTarget.qualifiedName
Expand All @@ -78,15 +77,15 @@ class SmkTypeProvider : PyTypeProviderBase() {
refTargetSection != null -> {
// check if in run section & rule
val (_, ruleLike) = getParentSectionAndRuleLike(
anchor, SmkRunSection::class.java
anchor, SmkRunSection::class.java
) ?: return null

ruleLike.getSectionByName(refTargetSection)?.let { SmkRuleLikeSectionArgsType(it) }

}
fqn == WILDCARDS_ACCESSOR_CLASS -> {
val ruleLike = PsiTreeUtil.getParentOfType(
anchor, SmkRuleOrCheckpoint::class.java
anchor, SmkRuleOrCheckpoint::class.java
) ?: return null

context.getType(ruleLike.wildcardsElement)
Expand All @@ -99,12 +98,12 @@ class SmkTypeProvider : PyTypeProviderBase() {
private fun getLambdaParamType(referenceTarget: PyNamedParameter): Ref<PyType>? {
// in a lambda
val lambda = PsiTreeUtil.getParentOfType(
referenceTarget, PyLambdaExpression::class.java
referenceTarget, PyLambdaExpression::class.java
) ?: return null

// in a section, lambda not in call
val (parentSection, ruleLike) = getParentSectionAndRuleLike(
lambda, SmkRuleOrCheckpointArgsSection::class.java, PyCallExpression::class.java
lambda, SmkRuleOrCheckpointArgsSection::class.java, PyCallExpression::class.java
) ?: return null

val allowedArgs = ALLOWED_LAMBDA_OR_CALLABLE_ARGS[parentSection.sectionKeyword] ?: emptyArray()
Expand All @@ -128,25 +127,25 @@ class SmkTypeProvider : PyTypeProviderBase() {
return null
}

private fun <T: SmkSection> getParentSectionAndRuleLike(
element: PsiElement,
sectionClass: Class<T>,
vararg sectionStopAt: Class<out PsiElement>
private fun <T : SmkSection> getParentSectionAndRuleLike(
element: PsiElement,
sectionClass: Class<T>,
vararg sectionStopAt: Class<out PsiElement>
): Pair<T, SmkRuleOrCheckpoint>? {
val section = PsiTreeUtil.getParentOfType(
element, sectionClass, true, *sectionStopAt
element, sectionClass, true, *sectionStopAt
) ?: return null

val ruleLike = PsiTreeUtil.getParentOfType(
section, SmkRuleOrCheckpoint::class.java
section, SmkRuleOrCheckpoint::class.java
) ?: return null

return section to ruleLike
}

override fun getReferenceExpressionType(
referenceExpression: PyReferenceExpression,
context: TypeEvalContext
referenceExpression: PyReferenceExpression,
context: TypeEvalContext
): PyType? {
val smkExpression = when {
SnakemakeLanguageDialect.isInsideSmkFile(referenceExpression) -> referenceExpression
Expand All @@ -159,11 +158,15 @@ class SmkTypeProvider : PyTypeProviderBase() {

val psiFile = smkExpression?.containingFile
val parentDeclaration =
PsiTreeUtil.getParentOfType(smkExpression, SmkRuleOrCheckpoint::class.java)
PsiTreeUtil.getParentOfType(smkExpression, SmkRuleOrCheckpoint::class.java)

if (referenceExpression.asQualifiedName()?.matches(SMK_VARS_PEP, SMK_VARS_CONFIG) == true) {
return SmkPepConfigType(psiFile as SmkFile)
}
if (referenceExpression.children.isNotEmpty() ||
psiFile == null ||
psiFile !is SmkFile) {
psiFile == null ||
psiFile !is SmkFile
) {
return null
}

Expand All @@ -176,12 +179,12 @@ class SmkTypeProvider : PyTypeProviderBase() {
// affect only "rules" which is resolved to appropriate place
return when (referenceExpression.referencedName) {
SMK_VARS_RULES -> SmkRulesType(
parentDeclaration as? SmkRule,
psiFile
parentDeclaration as? SmkRule,
psiFile
)
SMK_VARS_CHECKPOINTS -> SmkCheckpointType(
parentDeclaration as? SmkCheckPoint,
psiFile
parentDeclaration as? SmkCheckPoint,
psiFile
)

SMK_VARS_WILDCARDS -> parentDeclaration?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ object SnakemakeAPI {
const val UNPACK_FUNCTION = "unpack"

const val SMK_VARS_CONFIG = "config"
const val SMK_VARS_PEP = "pep"
const val SMK_VARS_RULES = "rules"
const val SMK_VARS_CHECKPOINTS = "checkpoints"
const val SMK_VARS_SCATTER = "scatter"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ class SMKImplicitPySymbolsCompletionContributor : CompletionContributor() {
val IN_PY_REF = psiElement().inside(PyReferenceExpression::class.java)

private val REF_CAPTURE = psiElement()
.inFile(SmkKeywordCompletionContributor.IN_SNAKEMAKE)
.and(IN_PY_REF)
.with(object : PatternCondition<PsiElement>("isFirstChild") {
override fun accepts(element: PsiElement, context: ProcessingContext): Boolean {
// check that this element is "first" in parent PyReferenceExpression, e.g. that
// we don't have some prefix with '.', e.g. element is 'exp<caret>', not 'foo.exp<caret>'
val refExpression = PsiTreeUtil.getParentOfType(element, PyReferenceExpression::class.java)
return !(refExpression?.isQualified ?: true)
}
})
.inFile(SmkKeywordCompletionContributor.IN_SNAKEMAKE)
.and(IN_PY_REF)
.with(object : PatternCondition<PsiElement>("isFirstChild") {
override fun accepts(element: PsiElement, context: ProcessingContext): Boolean {
// check that this element is "first" in parent PyReferenceExpression, e.g. that
// we don't have some prefix with '.', e.g. element is 'exp<caret>', not 'foo.exp<caret>'

val refExpression = PsiTreeUtil.getParentOfType(element, PyReferenceExpression::class.java)
return !(refExpression?.isQualified ?: true)
}
})
}

init {
Expand All @@ -46,9 +46,11 @@ class SMKImplicitPySymbolsCompletionContributor : CompletionContributor() {

class SMKImplicitPySymbolsCompletionProvider : CompletionProvider<CompletionParameters>() {

override fun addCompletions(parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet) {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {

val contextElement = parameters.position
val contextScope = SmkCodeInsightScope[contextElement]
Expand Down
Loading

0 comments on commit 4911dcf

Please sign in to comment.