Skip to content

Commit

Permalink
Tests were added. Minor fixes in marker provider and smk use implemen…
Browse files Browse the repository at this point in the history
…tation.

Resolves: #429
  • Loading branch information
Dmitry committed Jan 13, 2022
1 parent 49fd4c6 commit b50ba5f
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ class SnakemakeRuleInheritanceMarkerProvider : RelatedItemLineMarkerProvider() {
use: SmkUse,
result: MutableCollection<in RelatedItemLineMarkerInfo<*>>
) {
val name = use.getIdentifierLeaf() ?: return
val name = (if (use.nameIdentifierIsWildcard()) use.nameIdentifier?.firstChild else use.nameIdentifier) ?: return

val results =
use.getImportedRuleNames()?.mapNotNull { it.reference.resolve() } // Inherited rules are declared in list
use.getDefinedReferencesOfImportedRuleNames()
?.mapNotNull { it.reference.resolve() } // Inherited rules are declared in list
?: ((use.getModuleName()?.reference?.resolve() as? SmkModule)?.getPsiFile() as? SmkFile)?.advancedCollectRules(
mutableSetOf() // Inherited rules are declared by '*' pattern
)?.map { it.second } ?: return
Expand All @@ -52,17 +54,14 @@ class SnakemakeRuleInheritanceMarkerProvider : RelatedItemLineMarkerProvider() {
result: MutableCollection<in RelatedItemLineMarkerInfo<*>>
) {
val currentFile = element.containingFile
val identifier = (if (element is SmkUse) element.getIdentifierLeaf() else element.nameIdentifier) ?: return
val names = if (element is SmkUse) element.getProducedRulesNames().map { it.first } else listOf(
element.name ?: return
)
val e = StubIndex.getInstance().getAllKeys(KEY, element.project)
val identifier = (if (element is SmkUse && element.nameIdentifierIsWildcard()) element.nameIdentifier?.firstChild else element.nameIdentifier) ?: return
val modulesFromStub = StubIndex.getInstance().getAllKeys(KEY, element.project)
val module = element.let { ModuleUtilCore.findModuleForPsiElement(it.originalElement) } ?: return
val files = mutableSetOf<SmkFile>()
for (key in e) {
for (smkModule in modulesFromStub) {
files.addAll(StubIndex.getElements(
KEY,
key,
smkModule,
module.project,
GlobalSearchScope.moduleWithDependentsScope(module),
SmkModule::class.java
Expand All @@ -76,12 +75,7 @@ class SnakemakeRuleInheritanceMarkerProvider : RelatedItemLineMarkerProvider() {
val overrides = mutableListOf<SmkRuleOrCheckpoint>()
files.forEach { file ->
file.collectUses().forEach { pair ->
val containsReferenceToTargetRule = pair.second.getImportedRuleNames()?.any { it.text in names }
?: pair.second.hasPatternInDefinitionOfInheritedRules()
val moduleReference = pair.second.getModuleName()
val moduleReferToCurrentFileIfExists =
if (moduleReference != null) (moduleReference.reference?.resolve() as? SmkModule)?.getPsiFile() == currentFile else true
if (containsReferenceToTargetRule && moduleReferToCurrentFileIfExists) {
if(pair.second.getImportedRules()?.firstOrNull{it == element} != null){
overrides.add(pair.second)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,15 @@ class SmkUseImpl : SmkRuleLikeImpl<SmkUseStub, SmkUse, SmkRuleOrCheckpointArgsSe
override fun getModuleName() =
(findChildByType(SmkTokenTypes.SMK_FROM_KEYWORD) as? PsiElement)?.nextSibling?.nextSibling

override fun getDefinedReferencesOfImportedRuleNames(): Array<SmkReferenceExpression>? = PsiTreeUtil.getChildrenOfType(
findChildByType(USE_IMPORTED_RULES_NAMES),
SmkReferenceExpression::class.java
)
override fun getDefinedReferencesOfImportedRuleNames(): Array<SmkReferenceExpression>? =
PsiTreeUtil.getChildrenOfType(
findChildByType(USE_IMPORTED_RULES_NAMES),
SmkReferenceExpression::class.java
)

override fun getImportedRules(): List<SmkRuleOrCheckpoint>? =
getPairsOfImportedRulesAndNames(mutableSetOf())?.map { it.second }
getDefinedReferencesOfImportedRuleNames()?.mapNotNull { it.reference.resolve() as? SmkRuleOrCheckpoint }
?: getPairsOfImportedRulesAndNames(mutableSetOf())?.map { it.second }

override fun nameIdentifierIsWildcard() = nameIdentifier?.let {
it is SmkUseNameIdentifier && it.textContains('*')
Expand Down
69 changes: 69 additions & 0 deletions src/test/kotlin/features/glue/ActionsSteps.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package features.glue

import com.google.common.collect.ImmutableMap
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.documentation.DocumentationManager
import com.intellij.codeInsight.highlighting.BraceMatchingUtil
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.navigation.NavigationGutterIconRenderer
import com.intellij.codeInspection.LocalInspectionEP
import com.intellij.icons.AllIcons
import com.intellij.ide.util.gotoByName.GotoSymbolModel2
import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.openapi.application.ApplicationManager
Expand All @@ -27,9 +30,11 @@ import com.jetbrains.snakecharm.codeInsight.completion.wrapper.SmkWrapperCrawler
import com.jetbrains.snakecharm.inspections.SmkUnrecognizedSectionInspection
import com.jetbrains.snakecharm.lang.highlighter.SmkColorSettingsPage
import com.jetbrains.snakecharm.lang.highlighter.SnakemakeSyntaxHighlighterFactory
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpoint
import com.jetbrains.snakecharm.stringLanguage.lang.highlighter.SmkSLSyntaxHighlighter
import features.glue.SnakemakeWorld.findPsiElementUnderCaret
import features.glue.SnakemakeWorld.fixture
import features.glue.SnakemakeWorld.getOffsetUnderCaret
import features.glue.SnakemakeWorld.myFixture
import features.glue.SnakemakeWorld.myGeneratedDocPopupText
import io.cucumber.datatable.DataTable
Expand All @@ -39,6 +44,7 @@ import io.cucumber.java.en.When
import org.junit.Assert
import java.io.File.separator
import java.util.regex.Pattern
import javax.swing.Icon
import kotlin.test.*


Expand Down Expand Up @@ -117,6 +123,69 @@ class ActionsSteps {
}
}

@Then("^I expect marker of overriding section with references:\$")
fun iExpectMarkerOfOverridingSectionWithReferences(table: DataTable) {
// val gutters = fixture().findAllGutters()
// for(gutter in gutters){
// println(gutter.tooltipText)
// }
checkLineMarkersWithIconOnTheElement(AllIcons.Gutter.OverridingMethod, table)
}

@Then("^I expect marker of overridden section with references:\$")
fun iExpectMarkerOfOverriddenSectionWithReferences(table: DataTable) {
checkLineMarkersWithIconOnTheElement(AllIcons.Gutter.OverridenMethod, table)
}

@Then("^I expect no markers")
fun iExpectNoMarkers() {
checkLineMarkersWithIconOnTheElement(null, DataTable.emptyDataTable(), true)
}

private fun checkLineMarkersWithIconOnTheElement(icon: Icon?, table: DataTable, expectNoGutters: Boolean = false) {
fixture().doHighlighting()
var currentElement: PsiElement? = null
val application = ApplicationManager.getApplication()
val targetRulesOrCheckpoints = mutableListOf<SmkRuleOrCheckpoint>()
application.invokeAndWait({
application.runReadAction {
// Firstly, we're reading current rule or checkpoint name
// And all its gutter icons
currentElement = fixture().file.findElementAt(getOffsetUnderCaret())
targetRulesOrCheckpoints.addAll(
((fixture().findGuttersAtCaret()
.firstOrNull { it.icon == icon } as? LineMarkerInfo.LineMarkerGutterIconRenderer<*>)?.lineMarkerInfo?.navigationHandler as? NavigationGutterIconRenderer)?.targetElements?.map { it as SmkRuleOrCheckpoint }
?: emptyList())
}
}, ModalityState.NON_MODAL)
// Checks, that there are an appropriate number of markers
if (expectNoGutters) {
assertTrue(targetRulesOrCheckpoints.isEmpty(), "${currentElement?.text} should not have markers.")
return
} else {
assertTrue(targetRulesOrCheckpoints.isNotEmpty(), "${currentElement?.text} has no appropriate markers.")
}
// Finally, we check if all defined targets are in the marker list
// And if there are no targets missed in the test
application.invokeAndWait({
application.runWriteAction {
for (marker in table.asLists()) {
val appropriateTargetRule =
targetRulesOrCheckpoints.find { rule -> rule.name == marker.first() && rule.containingFile.name == marker.last() }
assertNotNull(
appropriateTargetRule,
"Missed target in ${currentElement?.text}: ${marker.first()} in the file ${marker.last()}}"
)
targetRulesOrCheckpoints.remove(appropriateTargetRule)
}
assertTrue(
targetRulesOrCheckpoints.isEmpty(),
"Extras: ${targetRulesOrCheckpoints.joinToString { "${it.name} in file ${it.containingFile.name}" }}"
)
}
}, ModalityState.NON_MODAL)
}

@Then("^I expect the tag highlighting to be the same as the annotator highlighting$")
fun iExpectTheTagHighlightingToBeTheSameAsTheAnnotatorHighlighting() {
val level = "info"
Expand Down
10 changes: 10 additions & 0 deletions src/test/kotlin/features/glue/CompletionResolveSteps.kt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ class CompletionResolveSteps {
}, ModalityState.NON_MODAL)
}

@When("^I change current file to <([^>]+)>\$")
fun iChangeCurrentFileTo(file: String){
val application = ApplicationManager.getApplication()
application.invokeAndWait({
application.runWriteAction {
SnakemakeWorld.fixture().openFileInEditor(SnakemakeWorld.fixture().findFileInTempDir(file))
}
}, ModalityState.NON_MODAL)
}


@Then("^reference should resolve to \"(.+)\" directory")
fun referenceShouldResolveToDirectory(name: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
Feature: tests line marker provider in case of rule inheritance

Scenario Outline: Rule inheritance in the one file
Given a snakemake project
Given I open a file "foo.smk" with text
"""
<rule_like> NAME:
input: "name_inp"
<rule_like> NAME_2:
input: "name_2_inp"
use rule NAME as NAME_3 with:
threads: 1
use rule NAME as NAME_4 with:
threads: 2
"""
When I put the caret at NAME:
Then I expect marker of overridden section with references:
| NAME_3 | foo.smk |
| NAME_4 | foo.smk |
When I put the caret at NAME_2:
Then I expect no markers
When I put the caret at NAME_3
Then I expect marker of overriding section with references:
| NAME | foo.smk |
When I put the caret at NAME_4
Then I expect marker of overriding section with references:
| NAME | foo.smk |
Examples:
| rule_like |
| rule |
| checkpoint |

Scenario Outline: Rule inheritance in case of module usage
Given a snakemake project
And a file "boo.smk" with text
"""
<rule_like> NAME:
input: "name_inp"
<rule_like> NAME_2:
input: "name_2_inp"
"""
Given I open a file "foo.smk" with text
"""
module MODULE:
snakefile: "boo.smk"
use rule NAME from MODULE as NAME_3 with:
threads: 1
use rule NAME from MODULE as NAME_4 with:
threads: 2
use rule NAME_2 as INCORRECT_USE_CASE with:
thread: 3
use rule NAME, NAME_2 from MODULE as EXPLICIT_DEFINITION_OF_*
use rule * from MODULE as IMPLICIT_DEFINITION_OF_*
"""
When I change current file to <boo.smk>
And I put the caret at NAME:
Then I expect marker of overridden section with references:
| NAME_3 | foo.smk |
| NAME_4 | foo.smk |
| EXPLICIT_DEFINITION_OF_* | foo.smk |
| IMPLICIT_DEFINITION_OF_* | foo.smk |
When I change current file to <boo.smk>
And I put the caret at NAME_2:
Then I expect marker of overridden section with references:
| EXPLICIT_DEFINITION_OF_* | foo.smk |
| IMPLICIT_DEFINITION_OF_* | foo.smk |
When I change current file to <foo.smk>
And I put the caret at NAME_3
Then I expect marker of overriding section with references:
| NAME | boo.smk |
When I put the caret at NAME_4
Then I expect marker of overriding section with references:
| NAME | boo.smk |
When I put the caret at INCORRECT_USE_CASE
Then I expect no markers
When I put the caret at EXPLICIT_DEFINITION_OF_*
Then I expect marker of overriding section with references:
| NAME | boo.smk |
| NAME_2 | boo.smk |
When I put the caret at IMPLICIT_DEFINITION_OF_*
Then I expect marker of overriding section with references:
| NAME | boo.smk |
| NAME_2 | boo.smk |
Examples:
| rule_like |
| rule |
| checkpoint |

0 comments on commit b50ba5f

Please sign in to comment.