diff --git a/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkCompletionUtil.kt b/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkCompletionUtil.kt new file mode 100644 index 000000000..7bf23a673 --- /dev/null +++ b/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkCompletionUtil.kt @@ -0,0 +1,8 @@ +package com.jetbrains.snakecharm.codeInsight.completion + +class SmkCompletionUtil { + companion object { + val RULES_AND_CHECKPOINTS_PRIORITY = Double.MAX_VALUE + const val WILDCARDS_LAMBDA_PARAMETER_PRIORITY = 1.0 + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkLambdaParameterInSectionCompletionContributor.kt b/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkLambdaParameterInSectionCompletionContributor.kt index fd0182511..712838d83 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkLambdaParameterInSectionCompletionContributor.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/completion/SmkLambdaParameterInSectionCompletionContributor.kt @@ -50,7 +50,7 @@ object SMKLambdaParameterInSectionCompletionProvider : CompletionProvider() { + companion object { + val IN_SMK_LOCALRULES_OR_RULEORDER_RULE_NAME_REFERENCE = + psiElement() + .andOr( + psiElement().inside(psiElement(SmkWorkflowLocalrulesSection::class.java)), + psiElement().inside(psiElement(SmkWorkflowRuleorderSection::class.java)) + ) + .withParent(SmkReferenceExpression::class.java)!! + } + + override fun addCompletions( + parameters: CompletionParameters, + context: ProcessingContext, + result: CompletionResultSet + ) { + val variants = collectVariantsForElement(parameters.position) + val references = + (PsiTreeUtil.getParentOfType(parameters.position, SmkWorkflowRuleorderSection::class.java) + ?: PsiTreeUtil.getParentOfType(parameters.position, SmkWorkflowLocalrulesSection::class.java)) + ?.argumentList + ?.arguments + ?.filterIsInstance() + ?.map { it.name } + if (references != null) { + variants.removeAll { it.first in references } + } + addVariantsToCompletionResultSet(variants, parameters, result) + } +} + +private class SmkRulesAndCheckpointsObjectsCompletionProvider : CompletionProvider() { + companion object { + val IN_SMK_RULES_OR_CHECKPOINTS_OBJECT = + psiElement() + .withParent( + psiElement(PyReferenceExpression::class.java) + .withChild(psiElement().andOr( + psiElement().withText(SnakemakeNames.SMK_VARS_CHECKPOINTS), + psiElement().withText(SnakemakeNames.SMK_VARS_RULES) + )) + ) + } + + override fun addCompletions( + parameters: CompletionParameters, + context: ProcessingContext, + result: CompletionResultSet + ) { + val variants = collectVariantsForElement(parameters.position) + + // we need to obtain containing rule/checkpoint from the original file + // which is why we obtain it from an element present both in copy and original file + val parentElement = parameters.position.parent + val originalContainingRuleOrCheckpoint = + PsiTreeUtil.getParentOfType( + parameters.withPosition(parentElement, parentElement.textOffset).originalPosition, + SmkRuleOrCheckpoint::class.java + ) + variants.removeAll { it.second == originalContainingRuleOrCheckpoint } + when (originalContainingRuleOrCheckpoint) { + is SmkRule -> variants.removeAll { it.second is SmkCheckPoint } + is SmkCheckPoint -> variants.removeAll { it.second is SmkRule } + } + + addVariantsToCompletionResultSet(variants, parameters, result) + } +} + + +// the following functions can be used to implement completion for any section/object referring to rule names + +private fun collectVariantsForElement(element: PsiElement): MutableList> { + val variants = mutableListOf>() + + val module = ModuleUtilCore.findModuleForPsiElement(element) + if (module != null) { + val ruleResults = mutableListOf() + val checkpointResults = mutableListOf() + AbstractSmkRuleOrCheckpointType.addVariantFromIndex( + SmkRuleNameIndex.KEY, + module, + ruleResults, + SmkRule::class.java + ) + AbstractSmkRuleOrCheckpointType.addVariantFromIndex( + SmkCheckpointNameIndex.KEY, + module, + checkpointResults, + SmkCheckPoint::class.java + ) + variants.addAll( + (ruleResults + checkpointResults) + .filter { (it as SmkRuleOrCheckpoint).name != null } + .map { (it as SmkRuleOrCheckpoint).name!! to it }) + } else { + variants.addAll( + (element.containingFile.originalFile as SmkFile).collectRules() + + (element.containingFile.originalFile as SmkFile).collectCheckPoints() + ) + } + + return variants +} + +private fun addVariantsToCompletionResultSet( + variants: MutableList>, + parameters: CompletionParameters, + result: CompletionResultSet +) { + /* + auto popup invocation count: 0 + auto popup, then a manual invocation: 2 + manual invocation once: 1 + manual invocation twice: 2 + */ + if (parameters.invocationCount <= 1) { + val includedFiles = SmkResolveUtil.getIncludedFiles(parameters.originalFile as SmkFile) + variants.retainAll { + it.second.containingFile.originalFile == parameters.originalFile || + it.second.containingFile.originalFile in includedFiles + } + } + + result.addAllElements( + variants.map { (name, elem) -> + AbstractSmkRuleOrCheckpointType.createRuleLikeLookupItem(name, elem) + } + ) + + result.runRemainingContributors(parameters, false) + + val shortcut = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance() + .getAction(IdeActions.ACTION_CODE_COMPLETION)) + if (StringUtil.isNotEmpty(shortcut)) { + result.addLookupAdvertisement("Pressing $shortcut twice would show all rules and checkpoints in the module.") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/resolve/SmkResolveUtil.kt b/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/resolve/SmkResolveUtil.kt new file mode 100644 index 000000000..ceb8b19fc --- /dev/null +++ b/src/main/kotlin/com/jetbrains/snakecharm/codeInsight/resolve/SmkResolveUtil.kt @@ -0,0 +1,31 @@ +package com.jetbrains.snakecharm.codeInsight.resolve + +import com.jetbrains.snakecharm.lang.psi.SmkFile + +class SmkResolveUtil { + companion object { + fun getIncludedFiles(file: SmkFile): List { + val includedFiles = mutableListOf() + getIncludedFilesForFile(file, includedFiles, mutableSetOf()) + return includedFiles + } + + private fun getIncludedFilesForFile( + file: SmkFile, + includedFiles: MutableList, + visitedFiles: MutableSet + ) { + visitedFiles.add(file) + val currentIncludes = file.collectIncludes() + .flatMap { it.references.toList() } + .map { it.resolve() } + .filterIsInstance() + includedFiles.addAll(currentIncludes) + currentIncludes.forEach { + if (it !in visitedFiles) { + getIncludedFilesForFile(it, includedFiles, visitedFiles) + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFile.kt b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFile.kt index bac51f9ed..fbb96058c 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFile.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFile.kt @@ -6,6 +6,7 @@ import com.jetbrains.python.psi.PyElementVisitor import com.jetbrains.python.psi.impl.PyFileImpl import com.jetbrains.snakecharm.SmkFileType import com.jetbrains.snakecharm.lang.SnakemakeLanguageDialect +import com.jetbrains.snakecharm.lang.SnakemakeNames /** * @author Roman.Chernyatchik @@ -66,4 +67,20 @@ class SmkFile(viewProvider: FileViewProvider) : PyFileImpl(viewProvider, Snakema }) return ruleNameAndPsi } + + fun collectIncludes(): List { + val includeStatements = mutableListOf() + + acceptChildren(object : PyElementVisitor(), SmkElementVisitor { + override val pyElementVisitor: PyElementVisitor = this + + override fun visitSmkWorkflowArgsSection(st: SmkWorkflowArgsSection) { + if (st.sectionKeyword == SnakemakeNames.WORKFLOW_INCLUDE_KEYWORD) { + includeStatements.add(st) + } + } + }) + + return includeStatements + } } \ No newline at end of file diff --git a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFileReference.kt b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFileReference.kt index 90fbab05e..a25599c63 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFileReference.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFileReference.kt @@ -60,7 +60,7 @@ open class SmkFileReference( it is PyStringLiteralExpression && it.stringValue == path } ?: return null - val parentFolder = element.containingFile.virtualFile?.parent ?: return null + val parentFolder = element.containingFile.originalFile.virtualFile?.parent ?: return null val filePath = (stringLiteral as? PyStringLiteralExpression)?.stringValue val vfm = VirtualFileManager.getInstance() diff --git a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/impl/SmkReferenceExpressionImpl.kt b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/impl/SmkReferenceExpressionImpl.kt index 53f255c50..b372519a7 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/impl/SmkReferenceExpressionImpl.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/impl/SmkReferenceExpressionImpl.kt @@ -1,6 +1,7 @@ package com.jetbrains.snakecharm.lang.psi.impl import com.intellij.lang.ASTNode +import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.util.TextRange import com.intellij.psi.* import com.intellij.psi.util.PsiTreeUtil @@ -9,12 +10,13 @@ import com.jetbrains.python.psi.impl.PyElementImpl import com.jetbrains.python.psi.resolve.RatedResolveResult import com.jetbrains.python.psi.types.PyType import com.jetbrains.python.psi.types.TypeEvalContext -import com.jetbrains.snakecharm.lang.psi.SmkFile -import com.jetbrains.snakecharm.lang.psi.SmkReferenceExpression -import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpoint +import com.jetbrains.snakecharm.lang.psi.* import com.jetbrains.snakecharm.lang.psi.impl.SmkPsiUtil.getIdentifierNode +import com.jetbrains.snakecharm.lang.psi.stubs.SmkCheckpointNameIndex +import com.jetbrains.snakecharm.lang.psi.stubs.SmkRuleNameIndex import com.jetbrains.snakecharm.lang.psi.types.AbstractSmkRuleOrCheckpointType + class SmkReferenceExpressionImpl(node: ASTNode): PyElementImpl(node), SmkReferenceExpression { override fun getName() = getNameNode()?.text @@ -33,28 +35,24 @@ class SmkReferenceExpressionImpl(node: ASTNode): PyElementImpl(node), SmkReferen private fun getNameNode() = getIdentifierNode(node) - private class SmkRuleOrCheckpointNameReference( + class SmkRuleOrCheckpointNameReference( element: PsiNamedElement, textRange: TextRange - ) : PsiReferenceBase(element, textRange), PsiPolyVariantReference { + ) : PsiReferenceBase.Poly(element, textRange, true) { private val key: String = element.text - override fun resolve(): PsiElement? = - multiResolve(false).firstOrNull()?.element - - override fun multiResolve(incompleteCode: Boolean): Array = - (getRules() + getCheckpoints()) - .filter { (name, _) -> name == key } - .map { (_, psi) -> RatedResolveResult(RatedResolveResult.RATE_NORMAL, psi) } - .toTypedArray() + override fun multiResolve(incompleteCode: Boolean): Array { + val rules = AbstractSmkRuleOrCheckpointType + .findAvailableRuleLikeElementByName(element, key, SmkRuleNameIndex.KEY, SmkRule::class.java) + { getRules().map{ (_, psi) -> psi }} + val checkpoints = AbstractSmkRuleOrCheckpointType + .findAvailableRuleLikeElementByName(element, key, SmkCheckpointNameIndex.KEY, SmkCheckPoint::class.java) + { getCheckpoints().map{ (_, psi) -> psi }} - override fun getVariants(): Array = - (getRules() + getCheckpoints()).map { (name, elem) -> - AbstractSmkRuleOrCheckpointType.createRuleLikeLookupItem( - name, - elem as SmkRuleOrCheckpoint - ) - }.toTypedArray() + return (rules + checkpoints) + .map { RatedResolveResult(RatedResolveResult.RATE_NORMAL, it) } + .toTypedArray() + } override fun handleElementRename(newElementName: String): PsiElement = element.setName(newElementName) diff --git a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/types/AbstractSmkRuleOrCheckpointType.kt b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/types/AbstractSmkRuleOrCheckpointType.kt index 829a17e24..292c87893 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/types/AbstractSmkRuleOrCheckpointType.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/types/AbstractSmkRuleOrCheckpointType.kt @@ -14,16 +14,14 @@ import com.intellij.psi.stubs.StubIndexKey import com.intellij.util.ProcessingContext import com.intellij.util.Processors import com.intellij.util.containers.ContainerUtil -import com.jetbrains.python.codeInsight.completion.PythonCompletionWeigher import com.jetbrains.python.psi.AccessDirection import com.jetbrains.python.psi.PyExpression import com.jetbrains.python.psi.resolve.PyResolveContext import com.jetbrains.python.psi.resolve.RatedResolveResult import com.jetbrains.python.psi.types.PyType +import com.jetbrains.snakecharm.codeInsight.completion.SmkCompletionUtil import com.jetbrains.snakecharm.lang.SnakemakeLanguageDialect import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpoint -import java.io.File -import java.lang.IllegalArgumentException abstract class AbstractSmkRuleOrCheckpointType( @@ -41,63 +39,7 @@ abstract class AbstractSmkRuleOrCheckpointType( completionPrefix: String?, location: PsiElement, context: ProcessingContext? - ): Array { - if (!SnakemakeLanguageDialect.isInsideSmkFile(location)) { - return emptyArray() - } - - val results = ArrayList() - - val module = ModuleUtilCore.findModuleForPsiElement(location.originalElement) - if (module != null) { - addVariantFromIndex(indexKey, module, results, clazz, searchScope(module)) - } - - if (results.isEmpty()) { - // show at list current file: - results.addAll(currentFileDeclarations) - } - - return results.stream() - .filter { it.name != null && it != containingRule } - .map { createRuleLikeLookupItem(it.name!!, it) } - .toArray() - } - - fun addVariantFromIndex( - indexKey: StubIndexKey, - module: Module, - results: MutableList, - clazz: Class, - scope: GlobalSearchScope - ) { - val project = module.project - val stubIndex = StubIndex.getInstance() - val allKeys = ContainerUtil.newTroveSet() - stubIndex.processAllKeys( - indexKey, Processors.cancelableCollectProcessor(allKeys), scope, null - ) - - for (key in allKeys) { - results.addAll(StubIndex.getElements(indexKey, key, project, scope, clazz)) - } - } - - private fun searchScope(module: Module): GlobalSearchScope { - // module content root and all dependent modules: - val scope = GlobalSearchScope.moduleWithDependentsScope(module) - - // all project w/o tests from project sdk roots - // PyProjectScopeBuilder.excludeSdkTestsScope(targetFile); - - //Returns module scope including sources, tests, and dependencies, excluding libraries: - // * if files in content root, not in source root - not visible in scope - // GlobalSearchScope.moduleWithDependenciesScope(module) - - // project with all modules and libs: - // GlobalSearchScope.allScope(project) - return scope - } + ): Array = emptyArray() override fun assertValid(message: String?) { // [romeo] Not sure is our type always valid or check whether any element is invalid @@ -109,20 +51,11 @@ abstract class AbstractSmkRuleOrCheckpointType( direction: AccessDirection, resolveContext: PyResolveContext ): List { - if (!SnakemakeLanguageDialect.isInsideSmkFile(location)) { + if (!SnakemakeLanguageDialect.isInsideSmkFile(location) || location == null) { return emptyList() } - val module = location?.let { ModuleUtilCore.findModuleForPsiElement(it.originalElement) } - val results = if (module != null) { - val scope = searchScope(module) - StubIndex.getElements(indexKey, name, module.project, scope, clazz) - } else { - // try local resolve - currentFileDeclarations.filter { elem -> - elem.name == name - } - } + val results = findAvailableRuleLikeElementByName(location.originalElement, name, indexKey, clazz) { currentFileDeclarations } if (results.isEmpty()) { return emptyList() @@ -156,14 +89,68 @@ abstract class AbstractSmkRuleOrCheckpointType( VfsUtil.getRelativePath(virtualFile, fileContentRootDirectory) ?: virtualFile.presentableUrl } } + return PrioritizedLookupElement.withPriority( LookupElementBuilder .createWithSmartPointer(name, elem) .withTypeText(displayPath) - .withIcon(elem.getIcon(0)) - , - PythonCompletionWeigher.WEIGHT_DELTA.toDouble() + .withIcon(elem.getIcon(0)), + SmkCompletionUtil.RULES_AND_CHECKPOINTS_PRIORITY ) } + + fun findAvailableRuleLikeElementByName( + location: PsiElement, + name: String, + indexKey: StubIndexKey, + clazz: Class, + getCurrentFileDeclarationsFunction: () -> List + ): Collection { + val module = location.let { ModuleUtilCore.findModuleForPsiElement(it.originalElement) } + return if (module != null) { + val scope = searchScope(module) + StubIndex.getElements(indexKey, name, module.project, scope, clazz) + } else { + // try local resolve + getCurrentFileDeclarationsFunction().filter { elem -> + elem.name == name + } + } + } + + fun addVariantFromIndex( + indexKey: StubIndexKey, + module: Module, + results: MutableList, + clazz: Class, + scope: GlobalSearchScope = searchScope(module) + ) { + val project = module.project + val stubIndex = StubIndex.getInstance() + val allKeys = ContainerUtil.newTroveSet() + stubIndex.processAllKeys( + indexKey, Processors.cancelableCollectProcessor(allKeys), scope, null + ) + + for (key in allKeys) { + results.addAll(StubIndex.getElements(indexKey, key, project, scope, clazz)) + } + } + + private fun searchScope(module: Module): GlobalSearchScope { + // module content root and all dependent modules: + val scope = GlobalSearchScope.moduleWithDependentsScope(module) + + // all project w/o tests from project sdk roots + // PyProjectScopeBuilder.excludeSdkTestsScope(targetFile); + + //Returns module scope including sources, tests, and dependencies, excluding libraries: + // * if files in content root, not in source root - not visible in scope + // GlobalSearchScope.moduleWithDependenciesScope(module) + + // project with all modules and libs: + // GlobalSearchScope.allScope(project) + return scope + } } } \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 74e101edd..9fd35eca4 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -71,6 +71,11 @@ language="Python" implementationClass="com.jetbrains.snakecharm.codeInsight.completion.SmkLambdaParameterInSectionCompletionContributor" /> + -Only last subworkfow with the same name will be executed +Only last subworkflow with the same name will be executed \ No newline at end of file diff --git a/src/test/kotlin/com/jetbrains/snakecharm/cucumber/CompletionResolveSteps.kt b/src/test/kotlin/com/jetbrains/snakecharm/cucumber/CompletionResolveSteps.kt index 9ab0d48b7..dfae9dce2 100644 --- a/src/test/kotlin/com/jetbrains/snakecharm/cucumber/CompletionResolveSteps.kt +++ b/src/test/kotlin/com/jetbrains/snakecharm/cucumber/CompletionResolveSteps.kt @@ -200,6 +200,12 @@ class CompletionResolveSteps { doComplete() } + @When("^I invoke autocompletion popup (\\d+) times$") + fun iInvokeAutocompletionPopupNTimes(invocationCount: String) { + Registry.get("ide.completion.variant.limit").setValue(10000) + doComplete(invocationCount.toInt()) + } + @Then("^completion list should contain:$") fun completionListShouldContain(table: DataTable) { completionListShouldContainMethods(table.asList(String::class.java)) @@ -334,9 +340,9 @@ class CompletionResolveSteps { else -> listOf(ref.resolve()) } - private fun doComplete() { + private fun doComplete(invocationCount: Int = 1) { val fixture = SnakemakeWorld.fixture() - fixture.complete(CompletionType.BASIC) + fixture.complete(CompletionType.BASIC, invocationCount) SnakemakeWorld.myCompletionList = fixture.lookupElementStrings } diff --git a/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/localrules_and_ruleorder.feature b/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/localrules_and_ruleorder.feature index e24407989..57afff9e5 100644 --- a/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/localrules_and_ruleorder.feature +++ b/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/localrules_and_ruleorder.feature @@ -8,8 +8,7 @@ Feature: Completion for rule names in localrules and ruleorder sections input: "input.txt" rule rule2: - output: touch("output.txt") - + output: touch("output.txt"mamm rule rule3: output: touch("_output.txt") @@ -17,9 +16,9 @@ Feature: Completion for rule names in localrules and ruleorder sections """ When I put the caret after rule1, And I invoke autocompletion popup - Then completion list should contain: - | rule2 | - | rule3 | + Then completion list should only contain: + | rule2 | + | rule3 | Scenario: Complete in ruleorder section Given a snakemake project @@ -38,6 +37,89 @@ Feature: Completion for rule names in localrules and ruleorder sections """ When I put the caret after rule3 > And I invoke autocompletion popup - Then completion list should contain: - | rule1 | - | rule2 | \ No newline at end of file + Then completion list should only contain: + | rule1 | + | rule2 | + + Scenario Outline: Complete in localrules/ruleorder section from included files + Given a snakemake project + Given a file "boo.smk" with text + """ + rule rule4: + input: "path/to/input" + """ + Given a file "soo.smk" with text + """ + include: "boo.smk" + rule rule5: + shell: "echo hello" + """ + Given a file "goo.smk" with text + """ + include: "boo.smk" + rule rule6: + shell: "echo hello" + """ + Given I open a file "foo.smk" with text + """ + rule rule1: + input: "input.txt" + + rule rule2: + output: touch("output.txt") + + rule rule3: + output: touch("_output.txt") + +
: rule3 + + include: "soo.smk" + """ + When I put the caret after rule3 + And I invoke autocompletion popup + Then completion list should only contain: + | rule1 | + | rule2 | + | rule4 | + | rule5 | + Examples: + | section | separator | + | localrules | , | + | ruleorder | > | + + Scenario Outline: second completion popup invocation produces all indexed rules + Given a snakemake project + Given a file "boo.smk" with text + """ + rule boo1: input: "file.txt" + + rule boo2: input: "file1.txt" + """ + Given a file "soo.smk" with text + """ + rule soo: input: "soo.txt" + """ + Given I open a file "foo.smk" with text + """ + include: "soo.smk" + + rule foo1: + input: "file.txt" + + rule foo2: + input: "file.txt" + +
: foo1 + """ + When I put the caret after foo1 + And I invoke autocompletion popup 2 times + Then completion list should only contain: + | foo2 | + | soo | + | boo1 | + | boo2 | + Examples: + | section | separator | + | localrules | , | + | ruleorder | > | + diff --git a/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/rules_and_checkpoints_names.feature b/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/rules_and_checkpoints_names.feature index 518853c7c..af3035fc2 100644 --- a/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/rules_and_checkpoints_names.feature +++ b/src/test/resources/com/jetbrains/snakecharm/cucumber/completion/rules_and_checkpoints_names.feature @@ -122,7 +122,7 @@ Feature: Rule and Checkpoints names completion after 'rules.' and 'checkpoints.' input: s. """ When I put the caret after input: s. - And I invoke autocompletion popup + And I invoke autocompletion popup 2 times Then completion list should contain: | aaaa | | bbbb | @@ -179,7 +179,7 @@ Feature: Rule and Checkpoints names completion after 'rules.' and 'checkpoints.' input: s. """ When I put the caret after s. - And I invoke autocompletion popup + And I invoke autocompletion popup 2 times Then completion list should contain these items with type text: | boo1 | boo.smk | | boo2 | boo.smk | @@ -221,7 +221,7 @@ Feature: Rule and Checkpoints names completion after 'rules.' and 'checkpoints.' input: s. """ When I put the caret after s. - And I invoke autocompletion popup + And I invoke autocompletion popup 2 times Then completion list should contain these items with type text: | boo | dir1/dir2/boo.smk | | doo | dir1/dir3/dir4/doo.smk | diff --git a/src/test/resources/com/jetbrains/snakecharm/cucumber/resolve/localrules_and_ruleorder.feature b/src/test/resources/com/jetbrains/snakecharm/cucumber/resolve/localrules_and_ruleorder.feature index 379edc749..ad98fbe2b 100644 --- a/src/test/resources/com/jetbrains/snakecharm/cucumber/resolve/localrules_and_ruleorder.feature +++ b/src/test/resources/com/jetbrains/snakecharm/cucumber/resolve/localrules_and_ruleorder.feature @@ -153,5 +153,107 @@ Feature: Resolve for rules in localrules and ruleorder | localrules: aa | | localrules: aa | + Scenario Outline: Resolve even for declarations from files present but not included for localrules/ruleorder section + Given a snakemake project + Given a file "boo.smk" with text + """ + rule dddd: + input: "path/to/input" + """ + Given I open a file "foo.smk" with text + """ +
: aaaa + + rule aaaa: + input: "input.txt" + + rule bbbb: + output: touch("output.txt") + + checkpoint cccc: + output: touch("_output.txt") + """ + When I put the caret after + Then reference should resolve to "" in "" + Examples: + | ptn | text | section | separator | symbol_name | file | + | ddd | dddd | localrules | , | dddd | boo.smk | + | ddd | dddd | ruleorder | > | dddd | boo.smk | + + Scenario Outline: Resolve in localrules/ruleorder section for rules from included files + Given a snakemake project + Given a file "boo.smk" with text + """ + rule dddd: + input: "path/to/input" + + checkpoint eeee: + input: "path/to/input/2" + """ + Given I open a file "foo.smk" with text + """ +
: aaaa + + include: "boo.smk" + + rule aaaa: + input: "input.txt" + + rule bbbb: + output: touch("output.txt") + + checkpoint cccc: + output: touch("_output.txt") + """ + When I put the caret after + Then reference should resolve to "" in "" + Examples: + | ptn | text | symbol_name | file | section | separator | + | ddd | dddd | dddd | boo.smk | localrules | , | + | ddd | dddd | dddd | boo.smk | ruleorder | > | + | eee | eeee | eeee | boo.smk | localrules | , | + | eee | eeee | eeee | boo.smk | ruleorder | > | + + Scenario Outline: Resolve in localrules/ruleorder section for rules from files included in other files + Given a snakemake project + Given a file "boo.smk" with text + """ + rule dddd: + input: "path/to/input" + """ + Given a file "soo.smk" with text + """ + include: "boo.smk" + checkpoint eeee: + input: "path/to/input/2" + """ + Given a file "goo.smk" with text + """ + include: "soo.smk" + """ + Given I open a file "foo.smk" with text + """ +
: aaaa + + include: "goo.smk" + + rule aaaa: + input: "input.txt" + + rule bbbb: + output: touch("output.txt") + + checkpoint cccc: + output: touch("_output.txt") + """ + When I put the caret after + Then reference should resolve to "" in "" + Examples: + | ptn | text | symbol_name | file | section | separator | + | ddd | dddd | dddd | boo.smk | localrules | , | + | ddd | dddd | dddd | boo.smk | ruleorder | > | + | eee | eeee | eeee | soo.smk | localrules | , | + | eee | eeee | eeee | soo.smk | ruleorder | > | +