Skip to content

Commit

Permalink
Bug fixes, new tests
Browse files Browse the repository at this point in the history
Resolves: #433
  • Loading branch information
Dmitry committed Mar 24, 2022
1 parent 2b1b793 commit dd18ecf
Show file tree
Hide file tree
Showing 10 changed files with 407 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private void initJPanel() {
filesChooserDescriptor.setTitle(SnakemakeBundle.message("smk.framework.configurable.panel.configuration.files.file.chooser"));
filesChooserDescriptor.withFileFilter(file -> {
String path = file.getCanonicalPath();
return path != null && path.endsWith(".yaml");
return path != null && (path.endsWith(".yaml") || path.endsWith(".yml"));
});

var yamlFilesPanel = ToolbarDecorator.createDecorator(yamlFilesTable)
Expand Down Expand Up @@ -141,7 +141,7 @@ public boolean isModified() {
@NotNull
private SmkSupportProjectSettings.State getUIState() {
SmkSupportProjectSettings.State state = SmkSupportProjectSettings.Companion.getInstance(project).stateSnapshot();
state.setConfigurationFiles(yamlFilesPaths.stream().distinct().collect(Collectors.toList()));
state.setConfigurationFiles(yamlFilesPaths);
state.setExplicitlyDefinedKeyValuePairs(keyValuePairs.stream().distinct().collect(Collectors.toList()));
return state;
}
Expand All @@ -154,12 +154,17 @@ public void apply() throws ConfigurationException {
@Override
public void reset() {
final SmkSupportProjectSettings conf = SmkSupportProjectSettings.Companion.getInstance(project);

// Here we create a new instances for FilePathState and KeyValuePairState
// Because we want to know, when settings state was changed
// Otherwise, if we use provided objects
// Old settings state and the new one will be the same
yamlFilesPaths.clear();
yamlFilesPaths.addAll(conf.getConfigurationFiles());
yamlFilesPaths.addAll(conf.getConfigurationFiles().stream()
.map(it -> new SmkSupportProjectSettings.FilePathState(Objects.requireNonNull(it.getPath()), it.getEnabled())).collect(Collectors.toList()));
((AbstractTableModel) yamlFilesTable.getModel()).fireTableRowsInserted(0, yamlFilesPaths.size() - 1);
keyValuePairs.clear();
keyValuePairs.addAll(conf.getExplicitlyDefinedKeyValuePairs());
keyValuePairs.addAll(conf.getExplicitlyDefinedKeyValuePairs().stream()
.map(it -> new SmkSupportProjectSettings.KeyValuePairState(Objects.requireNonNull(it.getKey()), Objects.requireNonNull(it.getValue()))).collect(Collectors.toList()));
((AbstractTableModel) keyValuePairsTable.getModel()).fireTableRowsInserted(0, keyValuePairs.size() - 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import org.jetbrains.yaml.psi.*
import java.io.File

// TODO: Support JSON
// TODO: disable unresolved reference inspection for predefined key-value pairs
class SmkYAMLKeysStorage(val project: Project) : Disposable {

private var reversedYAMLFiles = mutableListOf<SmartPsiElementPointer<YAMLFile>>()
private var reversedYAMLFiles = mutableListOf<Pair<SmartPsiElementPointer<YAMLFile>, Boolean>>()
private var definedPairs = mutableMapOf<String, String>()

fun initOnStartup() {
Expand Down Expand Up @@ -56,6 +55,8 @@ class SmkYAMLKeysStorage(val project: Project) : Disposable {
})
}

fun keyWasDefinedInYAMLKeyValuePairs(key: String) = definedPairs.containsKey(key)

/**
* Returns top-level [YAMLKeyValue] by its name or null
*/
Expand All @@ -68,10 +69,10 @@ class SmkYAMLKeysStorage(val project: Project) : Disposable {
*/
fun getTopLevelKeysVariants(file: SmkFile): List<String> {
val result = mutableListOf<String>()
val allYamlFiles = reversedYAMLFiles.mapNotNull { it.element } + file.advancedCollectConfigFiles()
.mapNotNull { (it.reference?.resolve() as? YAMLFile) }
result.addAll(definedPairs.keys)
result.addAll(allYamlFiles.map { YAMLUtil.getTopLevelKeys(it).map { yamlKeyValue -> yamlKeyValue.keyText } }
result.addAll(getYamlFilesForSmkFile(file).map {
YAMLUtil.getTopLevelKeys(it).map { yamlKeyValue -> yamlKeyValue.keyText }
}
.flatten())
return result
}
Expand All @@ -85,7 +86,7 @@ class SmkYAMLKeysStorage(val project: Project) : Disposable {
val targetPsi = resolveToOperandOrItsChild(file, path) ?: return emptyArray()
// We don't provide completion for YAMLSequence
// Because it receives indexes, not keys
return when (val variantsProvider = (targetPsi as? YAMLKeyValue)?.value ?: targetPsi) {
return when (val variantsProvider = SmkYAMLUtil.getValueOrParam(targetPsi)) {
is YAMLMapping -> variantsProvider.keyValues.mapNotNull { it.keyText }.toTypedArray()
else -> return emptyArray()
}
Expand Down Expand Up @@ -179,7 +180,7 @@ class SmkYAMLKeysStorage(val project: Project) : Disposable {
if (file.enabled && path != null) {
val yamlFile = fileSystem.findFileByIoFile(File(path))?.toPsi(project) as? YAMLFile
if (yamlFile != null) {
reversedYAMLFiles.add(pointerManager.createSmartPsiElementPointer(yamlFile))
reversedYAMLFiles.add(pointerManager.createSmartPsiElementPointer(yamlFile) to file.enabled)
}
}
}
Expand All @@ -206,7 +207,13 @@ class SmkYAMLKeysStorage(val project: Project) : Disposable {
* Collects '.yaml' files that produce keys for specific [file]
*/
private fun getYamlFilesForSmkFile(file: SmkFile) =
reversedYAMLFiles.mapNotNull { it.element } + file.advancedCollectConfigFiles()
reversedYAMLFiles.mapNotNull {
if (it.second) {
it.first.element
} else {
null
}
} + file.advancedCollectConfigFiles()
.mapNotNull { it.reference?.resolve() as? YAMLFile }

private fun subscribeOnEvents() {
Expand Down Expand Up @@ -237,6 +244,19 @@ class SmkYAMLKeysStorage(val project: Project) : Disposable {
})
}

/**
* Adds YAML file into storage if project runs in unit test mode
*/
fun addFilesInTestMode(file: YAMLFile, enabled: Boolean) {
if (!ApplicationManager.getApplication().isUnitTestMode) {
throw IllegalStateException("Method 'addFilesInTestMode' is available only in unit test mode")
}

val pointerManager = SmartPointerManager.getInstance(project)

reversedYAMLFiles.add(pointerManager.createSmartPsiElementPointer(file) to enabled)
}

override fun dispose() {
// Same as in SmkWrapperStorage
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class SmkYAMLUtil {
for (node in path) {
// We're getting value here instead of getting it in 'is YAMLMapping'
// In order to return YAMLKeyValue and produce correct resolve
yamlPsiElement = (yamlPsiElement as? YAMLKeyValue)?.value ?: yamlPsiElement
yamlPsiElement = getValueOrParam(yamlPsiElement)
yamlPsiElement = when (yamlPsiElement) {
is YAMLMapping -> yamlPsiElement.keyValues.firstOrNull { it.key?.text == node }
is YAMLSequence -> {
Expand All @@ -24,5 +24,10 @@ class SmkYAMLUtil {
}
return yamlPsiElement
}

fun getValueOrParam(yamlPsiElement: YAMLPsiElement?): YAMLPsiElement? {
val result = (yamlPsiElement as? YAMLKeyValue)?.value ?: yamlPsiElement
return (result as? YAMLAlias)?.reference?.resolve()?.markedValue ?: result
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,6 @@ class SmkSupportProjectSettings(val project: Project) : PersistentStateComponent
this.path = path
this.enabled = enabled
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as FilePathState

return path == other.path
}

override fun hashCode() = path?.hashCode() ?: 0
}

class KeyValuePairState() : BaseState() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jetbrains.snakecharm.inspections

import com.intellij.openapi.components.service
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
Expand All @@ -10,11 +11,15 @@ import com.jetbrains.python.psi.PyQualifiedExpression
import com.jetbrains.python.psi.types.PyType
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI
import com.jetbrains.snakecharm.codeInsight.completion.yamlKeys.SmkYAMLKeysStorage
import com.jetbrains.snakecharm.lang.psi.SmkModule
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpointArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkUse
import com.jetbrains.snakecharm.lang.psi.impl.SmkPsiUtil
import com.jetbrains.snakecharm.lang.psi.references.SmkSectionNameArgInPySubscriptionLikeReference
import com.jetbrains.snakecharm.lang.psi.types.SmkAvailableForSubscriptionType
import com.jetbrains.snakecharm.lang.psi.types.SmkConfigType
import com.jetbrains.snakecharm.stringLanguage.lang.psi.references.SmkSLSubscriptionKeyReference

/**
* See also: [com.jetbrains.snakecharm.lang.highlighter.SnakemakeVisitorFilter]
Expand All @@ -38,6 +43,10 @@ class SmkIgnorePyInspectionExtension : PyInspectionExtension() {
return false
}

if(isDefinedYAMLKeyValuePair(reference)) {
return true
}

val refElement = reference.element

val use = node.parentOfType<SmkUse>()
Expand All @@ -62,6 +71,15 @@ class SmkIgnorePyInspectionExtension : PyInspectionExtension() {
return false
}

private fun isDefinedYAMLKeyValuePair(reference: PsiReference) : Boolean{
val type = (reference as? SmkSectionNameArgInPySubscriptionLikeReference)?.type ?: (reference as? SmkSLSubscriptionKeyReference)?.type
if (type !is SmkConfigType){
return false
}
val storage = reference.element.project.service<SmkYAMLKeysStorage>()
return storage.keyWasDefinedInYAMLKeyValuePairs(reference.canonicalText)
}

// ignoreMissingDocstring
// ignoreMethodParameters
// getFunctionParametersFromUsage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ class SmkSLSubscriptionIndexKeyExpressionImpl(node: ASTNode) : PyReferenceExpres

override fun toString() = "SmkSLSubscriptionKeyExpression: [${this.referencedName}]"

override fun hasEmptyIndex() = text == "IntellijIdeaRulezzz "
override fun hasEmptyIndex() = text.endsWith("IntellijIdeaRulezzz ")
}
41 changes: 40 additions & 1 deletion src/test/kotlin/features/glue/StepDefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ import com.intellij.testFramework.UsefulTestCase
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
import com.intellij.testFramework.fixtures.InjectionTestFixture
import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl
import com.jetbrains.extensions.python.toPsi
import com.jetbrains.python.PythonMockSdk
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache
import com.jetbrains.python.fixtures.PyLightProjectDescriptor
import com.jetbrains.python.psi.PyFile
import com.jetbrains.snakecharm.SnakemakeTestCase
import com.jetbrains.snakecharm.SnakemakeTestUtil
import com.jetbrains.snakecharm.codeInsight.completion.yamlKeys.SmkYAMLKeysStorage
import com.jetbrains.snakecharm.framework.SmkSupportProjectSettings
import com.jetbrains.snakecharm.inspections.SmkUnrecognizedSectionInspection
import io.cucumber.datatable.DataTable
import io.cucumber.java.en.Given
import org.jetbrains.yaml.psi.YAMLFile
import javax.swing.SwingUtilities
import kotlin.test.fail

Expand Down Expand Up @@ -172,6 +175,42 @@ class StepDefs {
waitEDTEventsDispatching()
}

@Given("^add file \"(.+)\" as configuration file to settings panel and (enable|disable) file")
fun addConfigFile(file: String, mode: String) {
if (!file.endsWith(".yaml") && file.endsWith(".yml")) {
fail("File ends with '.yaml' or '.yml' was expected, but received: $file")
}

val enabled = when(mode) {
"enable" -> true
"disable" -> false
else -> fail("Unexpected configuration file mode: $mode")
}

ApplicationManager.getApplication().runReadAction {
val yamlFile = SnakemakeWorld.fixture().findFileInTempDir(file).toPsi(SnakemakeWorld.fixture().project) as YAMLFile
val storage = SnakemakeWorld.fixture().project.getService(SmkYAMLKeysStorage::class.java)
storage.addFilesInTestMode(yamlFile, enabled)
}
}

@Given("^add key-value pairs to settings panel")
fun addYAMLKeyValuePairs(table: DataTable) {
if (table.width() != 2) {
fail("Data table with two columns was expected, but received: ${table.width()}")
}
val project = SnakemakeWorld.fixture().project
val pairs = table.asLists().map { row -> SmkSupportProjectSettings.KeyValuePairState(row.component1(), row.component2()) }
val state = SmkSupportProjectSettings.getInstance(project).stateSnapshot()

state.explicitlyDefinedKeyValuePairs.addAll(pairs)
ApplicationManager.getApplication().invokeAndWait {
SmkSupportProjectSettings.updateStateAndFireEvent(project, state)
}

waitEDTEventsDispatching()
}

@Given("^add snakemake framework support (with|without) wrappers loaded")
fun withSnakemakeFacet(withWrappersStr: String) {
val project = SnakemakeWorld.fixture().project
Expand Down
Loading

0 comments on commit dd18ecf

Please sign in to comment.