diff --git a/src/main/kotlin/com/jetbrains/snakecharm/inspections/SmkUnresolvedReferenceInspectionExtension.kt b/src/main/kotlin/com/jetbrains/snakecharm/inspections/SmkUnresolvedReferenceInspectionExtension.kt index 1a49269c..6cfb7d00 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/inspections/SmkUnresolvedReferenceInspectionExtension.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/inspections/SmkUnresolvedReferenceInspectionExtension.kt @@ -18,7 +18,7 @@ class SmkUnresolvedReferenceInspectionExtension : PyUnresolvedReferenceQuickFixP return } val name = fileReference.path - existing.add(CreateMissedFile(section, name, sectionName)) + existing.add(CreateMissedFile(section, name, sectionName, fileReference.searchRelativelyToCurrentFolder)) } } } \ No newline at end of file diff --git a/src/main/kotlin/com/jetbrains/snakecharm/inspections/quickfix/CreateMissedFile.kt b/src/main/kotlin/com/jetbrains/snakecharm/inspections/quickfix/CreateMissedFile.kt index 60a28b8d..c6cac780 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/inspections/quickfix/CreateMissedFile.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/inspections/quickfix/CreateMissedFile.kt @@ -6,6 +6,7 @@ import com.intellij.openapi.application.ModalityState import com.intellij.openapi.command.undo.* import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFileManager @@ -26,6 +27,7 @@ class CreateMissedFile( element: PsiElement, private val fileName: String, private val sectionName: String, + private val searchRelativelyToCurrentFolder: Boolean ) : LocalQuickFixAndIntentionActionOnPsiElement(element) { companion object { private val condaDefaultContext = """ @@ -57,7 +59,11 @@ class CreateMissedFile( startElement: PsiElement, endElement: PsiElement ) { - val targetFilePath = Paths.get(file.virtualFile.parent.path, fileName) + val dir = + if (searchRelativelyToCurrentFolder) file.virtualFile.parent else ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile( + file.virtualFile + ) ?: return + val targetFilePath = Paths.get(dir.path, fileName) var firstAffectedFile = targetFilePath while (firstAffectedFile.parent.notExists()) { firstAffectedFile = firstAffectedFile.parent ?: break @@ -70,7 +76,7 @@ class CreateMissedFile( } else { Files.delete(firstAffectedFile) } - VirtualFileManager.getInstance().syncRefresh() + VirtualFileManager.getInstance().asyncRefresh { } } val redo = Runnable { if (!supportedSections.containsKey(sectionName)) { @@ -80,7 +86,7 @@ class CreateMissedFile( val directoryPath = Files.createDirectories(targetFilePath.parent) val directoryVirtualFile = VfsUtil.findFile(directoryPath, true) ?: return@Runnable LocalFileSystem.getInstance().createChildFile(this, directoryVirtualFile, targetFilePath.name) - VirtualFileManager.getInstance().syncRefresh() + VirtualFileManager.getInstance().asyncRefresh { } val context = supportedSections[sectionName] if (context != null) { // We don't use the result of 'createChildFile()' because it has inappropriate type (and throw UnsupportedOperationException) 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 69beffcf..7aedff47 100644 --- a/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFileReference.kt +++ b/src/main/kotlin/com/jetbrains/snakecharm/lang/psi/SmkFileReference.kt @@ -25,7 +25,7 @@ open class SmkFileReference( private val textRange: TextRange, private val stringLiteralExpression: PyStringLiteralExpression, val path: String, - private val searchRelativelyToCurrentFolder: Boolean = true, + val searchRelativelyToCurrentFolder: Boolean = true, ) : PsiReferenceBase(element, textRange), PsiReferenceEx { // Reference caching can be implemented with the 'ResolveCache' class if needed @@ -159,7 +159,7 @@ open class SmkFileReference( override fun getUnresolvedDescription(): String? = null - open fun hasAppropriateSuffix():Boolean = false + open fun hasAppropriateSuffix(): Boolean = false } /** @@ -176,7 +176,8 @@ class SmkIncludeReference( it is SmkFile && it.originalFile != element.containingFile.originalFile } - override fun hasAppropriateSuffix() = (path.endsWith(".smk") || path == "Snakemake") && element.containingFile.virtualFile.path != path + override fun hasAppropriateSuffix() = + (path.endsWith(".smk") || path == "Snakemake") && element.containingFile.virtualFile.path != path } /** @@ -277,6 +278,8 @@ class SmkNotebookReference( val name = it.name.lowercase() name.endsWith(".ipynb") } + + override fun hasAppropriateSuffix() = path.endsWith(".ipynb") } /** @@ -291,10 +294,13 @@ class SmkScriptReference( ) : SmkFileReference(element, textRange, stringLiteralExpression, path) { override fun getVariants() = collectFileSystemItemLike { val name = it.name.lowercase() - name.endsWith(".py") or name.endsWith(".r") or name.endsWith(".rmd") or name.endsWith(".jl") or name.endsWith(".rs") + hasCorrectEnding(name) } - override fun hasAppropriateSuffix() = path.endsWith(".ipynb") + override fun hasAppropriateSuffix() = hasCorrectEnding(path.lowercase()) + + private fun hasCorrectEnding(name: String) = + name.endsWith(".py") or name.endsWith(".r") or name.endsWith(".rmd") or name.endsWith(".jl") or name.endsWith(".rs") } /** diff --git a/src/test/resources/features/highlighting/inspections/unresolved_reference_extension.feature b/src/test/resources/features/highlighting/inspections/unresolved_reference_extension.feature index d25d82ea..743c48e5 100644 --- a/src/test/resources/features/highlighting/inspections/unresolved_reference_extension.feature +++ b/src/test/resources/features/highlighting/inspections/unresolved_reference_extension.feature @@ -1,38 +1,6 @@ Feature: Inspection: SmkUnresolvedReferenceInspectionExtension - Checks, that extension + inspection work - Scenario Outline: Unresolved conda file - Given a snakemake project - Given I open a file "foo.smk" with text - """ - rule NAME: - conda: - "" - """ - And PyUnresolvedReferencesInspection inspection is enabled - Then I expect inspection error on <> with message - """ - Unresolved reference '' - """ - When I check highlighting warnings - And I invoke quick fix Create '' and see text: - """ - rule NAME: - conda: - "" - """ - Then the file "" should have text - """ - channels: - dependencies: - """ - Examples: - | path | - | NAME.yaml | - | envs/NAME.yaml | - | ../envs/NAME.yaml | - - Scenario Outline: other unresolved sections + Scenario Outline: Quick fix fot missed files Given a snakemake project Given I open a file "foo.smk" with text """ @@ -49,10 +17,18 @@ Feature: Inspection: SmkUnresolvedReferenceInspectionExtension
: "" """ Examples: - | path | section | - | NAME.py.ipynb | rule NAME: notebook | - | NAME.py | rule NAME: script | - | boo.smk | module NAME: snakefile | - | NAME.yaml | configfile | - | NAME.yaml | pepfile | - | NAME.yml | pepschema | + | path | section | + | NAME.yaml | rule NAME: conda | + | envs/NAME.yaml | rule NAME: conda | + | ../envs/NAME.yaml | rule NAME: conda | + | NAME.py.ipynb | rule NAME: notebook | + | NAME.py | rule NAME: script | + | boo.smk | module NAME: snakefile | + | NAME.yaml | configfile | + | NAME.yaml | pepfile | + | NAME.yml | pepschema | + + # Impossible to check whether the file has been created because: + # 1) It is being creating asynchronously + # 2) So, we may need async refresh() (see LightTempDirTestFixtureImpl.java:137) + # It leads to Exception: "Do not perform a synchronous refresh under read lock ..." \ No newline at end of file