-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into features/#452-unused-log-file
- Loading branch information
Showing
26 changed files
with
600 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/main/kotlin/com/jetbrains/snakecharm/inspections/SmkDocstringsWillBeIgnoredInspection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.jetbrains.snakecharm.inspections | ||
|
||
import com.intellij.codeInspection.LocalInspectionToolSession | ||
import com.intellij.codeInspection.ProblemsHolder | ||
import com.jetbrains.snakecharm.SnakemakeBundle | ||
import com.jetbrains.snakecharm.lang.psi.* | ||
|
||
class SmkDocstringsWillBeIgnoredInspection : SnakemakeInspection() { | ||
override fun buildVisitor( | ||
holder: ProblemsHolder, | ||
isOnTheFly: Boolean, | ||
session: LocalInspectionToolSession | ||
) = object : SnakemakeInspectionVisitor(holder, getContext(session)) { | ||
|
||
override fun visitSmkRule(rule: SmkRule) { | ||
visitSMKRuleLike(rule) | ||
} | ||
|
||
override fun visitSmkCheckPoint(checkPoint: SmkCheckPoint) { | ||
visitSMKRuleLike(checkPoint) | ||
} | ||
|
||
fun visitSMKRuleLike(rule: SmkRuleLike<SmkArgsSection>) { | ||
val docstrings = rule.getStringLiteralExpressions().drop(1) | ||
if (docstrings.isEmpty()) { | ||
return | ||
} | ||
for (docstring in docstrings) { | ||
registerProblem(docstring, SnakemakeBundle.message("INSP.NAME.docstrings.will.be.ignored")) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
.../kotlin/com/jetbrains/snakecharm/inspections/SmkUnresolvedReferenceInspectionExtension.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.jetbrains.snakecharm.inspections | ||
|
||
import com.intellij.codeInspection.LocalQuickFix | ||
import com.intellij.psi.PsiReference | ||
import com.jetbrains.python.inspections.PyUnresolvedReferenceQuickFixProvider | ||
import com.jetbrains.snakecharm.inspections.quickfix.CreateMissedFileQuickFix | ||
import com.jetbrains.snakecharm.inspections.quickfix.CreateMissedFileUndoableAction | ||
import com.jetbrains.snakecharm.lang.psi.SmkArgsSection | ||
import com.jetbrains.snakecharm.lang.psi.SmkFileReference | ||
|
||
class SmkUnresolvedReferenceInspectionExtension : PyUnresolvedReferenceQuickFixProvider { | ||
|
||
override fun registerQuickFixes(reference: PsiReference, existing: MutableList<LocalQuickFix>) { | ||
val section = reference.element as? SmkArgsSection ?: return | ||
val sectionName = section.sectionKeyword ?: return | ||
if (CreateMissedFileUndoableAction.sectionToDefaultFileContent.containsKey(sectionName)) { | ||
val fileReference = (section.reference as? SmkFileReference) ?: return | ||
if (!fileReference.hasAppropriateSuffix()) { | ||
return | ||
} | ||
val name = fileReference.path | ||
existing.add(CreateMissedFileQuickFix(section, | ||
name, | ||
sectionName, | ||
fileReference.searchRelativelyToCurrentFolder)) | ||
} | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
src/main/kotlin/com/jetbrains/snakecharm/inspections/quickfix/CreateMissedFileQuickFix.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.jetbrains.snakecharm.inspections.quickfix | ||
|
||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement | ||
import com.intellij.notification.NotificationType | ||
import com.intellij.openapi.command.undo.UndoManager | ||
import com.intellij.openapi.diagnostic.Logger | ||
import com.intellij.openapi.editor.Editor | ||
import com.intellij.openapi.project.Project | ||
import com.intellij.openapi.roots.ProjectRootManager | ||
import com.intellij.psi.PsiElement | ||
import com.intellij.psi.PsiFile | ||
import com.jetbrains.snakecharm.SmkNotifier | ||
import com.jetbrains.snakecharm.SnakemakeBundle | ||
import java.nio.file.InvalidPathException | ||
import java.nio.file.Paths | ||
import kotlin.io.path.Path | ||
|
||
class CreateMissedFileQuickFix( | ||
element: PsiElement, | ||
private val fileName: String, | ||
private val sectionName: String, | ||
private val searchRelativelyToCurrentFolder: Boolean, | ||
) : LocalQuickFixAndIntentionActionOnPsiElement(element) { | ||
companion object { | ||
private val LOGGER = Logger.getInstance(CreateMissedFileQuickFix::class.java) | ||
} | ||
|
||
override fun getFamilyName() = SnakemakeBundle.message("INSP.NAME.conda.env.missing.fix", fileName) | ||
|
||
override fun getText() = familyName | ||
|
||
override fun invoke( | ||
project: Project, | ||
file: PsiFile, | ||
editor: Editor?, | ||
startElement: PsiElement, | ||
endElement: PsiElement, | ||
) { | ||
val vFile = file.virtualFile ?: return | ||
|
||
val fileIndex = ProjectRootManager.getInstance(project).fileIndex | ||
val relativeDirectory = when { | ||
searchRelativelyToCurrentFolder -> vFile.parent | ||
else -> fileIndex.getContentRootForFile(vFile) | ||
} ?: return | ||
|
||
val fileToCreatePath = try { | ||
when { | ||
Path(fileName).isAbsolute -> Path(fileName) | ||
else -> Paths.get(relativeDirectory.path, fileName) | ||
} | ||
} catch (e: InvalidPathException) { | ||
LOGGER.error(e) | ||
SmkNotifier.notify( | ||
title = SnakemakeBundle.message("notifier.msg.create.env.file.title"), | ||
content = SnakemakeBundle.message("notifier.msg.create.env.file.name.invalid.file.exception", fileName), | ||
type = NotificationType.ERROR, | ||
project = project | ||
) | ||
return | ||
} | ||
val action = CreateMissedFileUndoableAction(file, fileToCreatePath, sectionName) | ||
action.redo() | ||
UndoManager.getInstance(project).undoableActionPerformed(action) | ||
} | ||
} |
187 changes: 187 additions & 0 deletions
187
...in/kotlin/com/jetbrains/snakecharm/inspections/quickfix/CreateMissedFileUndoableAction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package com.jetbrains.snakecharm.inspections.quickfix | ||
|
||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer | ||
import com.intellij.notification.NotificationType | ||
import com.intellij.openapi.command.undo.DocumentReference | ||
import com.intellij.openapi.command.undo.DocumentReferenceManager | ||
import com.intellij.openapi.command.undo.UndoableAction | ||
import com.intellij.openapi.diagnostic.Logger | ||
import com.intellij.openapi.progress.ProgressIndicator | ||
import com.intellij.openapi.progress.ProgressManager | ||
import com.intellij.openapi.progress.Task | ||
import com.intellij.openapi.project.Project | ||
import com.intellij.openapi.vfs.VirtualFile | ||
import com.intellij.openapi.vfs.VirtualFileManager | ||
import com.intellij.psi.PsiFile | ||
import com.jetbrains.snakecharm.SmkNotifier | ||
import com.jetbrains.snakecharm.SnakemakeBundle | ||
import com.jetbrains.snakecharm.lang.SnakemakeNames | ||
import org.apache.commons.io.FileUtils | ||
import java.io.IOException | ||
import java.nio.file.Files | ||
import java.nio.file.Path | ||
import kotlin.io.path.appendText | ||
import kotlin.io.path.isDirectory | ||
import kotlin.io.path.name | ||
import kotlin.io.path.notExists | ||
|
||
class CreateMissedFileUndoableAction( | ||
private val actionInvocationTarget: PsiFile, | ||
private val fileToCreatePath: Path, | ||
private val sectionName: String, | ||
) : UndoableAction { | ||
|
||
companion object { | ||
private val LOGGER = Logger.getInstance(CreateMissedFileUndoableAction::class.java) | ||
private val condaDefaultContent = """ | ||
channels: | ||
dependencies: | ||
""".trimIndent() | ||
|
||
// Update it if wee need default text | ||
val sectionToDefaultFileContent = mapOf( | ||
SnakemakeNames.SECTION_CONDA to condaDefaultContent, | ||
SnakemakeNames.SECTION_NOTEBOOK to null, | ||
SnakemakeNames.SECTION_SCRIPT to null, | ||
SnakemakeNames.MODULE_SNAKEFILE_KEYWORD to null, | ||
|
||
SnakemakeNames.WORKFLOW_CONFIGFILE_KEYWORD to null, | ||
SnakemakeNames.WORKFLOW_PEPFILE_KEYWORD to null, | ||
SnakemakeNames.WORKFLOW_PEPSCHEMA_KEYWORD to null | ||
) | ||
} | ||
|
||
private val actionInvocationTargetVFile = actionInvocationTarget.virtualFile!! | ||
private val project = actionInvocationTarget.project | ||
private val firstCreatedFileOrDir: Path | ||
private val firstCreatedFileOrDirParent: VirtualFile? | ||
|
||
init { | ||
// Memorize first directory that will be created & delete it on "undo" step | ||
var fileOrDirToCreate = fileToCreatePath | ||
while (fileOrDirToCreate.parent.notExists()) { | ||
fileOrDirToCreate = fileOrDirToCreate.parent ?: break | ||
} | ||
firstCreatedFileOrDir = fileOrDirToCreate | ||
|
||
val parent = fileOrDirToCreate.parent | ||
firstCreatedFileOrDirParent = when (parent) { | ||
null -> null | ||
else -> VirtualFileManager.getInstance().findFileByNioPath(parent) | ||
} | ||
} | ||
|
||
override fun undo() { | ||
// invoke in such way because: | ||
// (1) changing document inside undo/redo is not allowed (see ChangeFileEncodingAction) | ||
// (2) we don't want to use UI thread, e.g. if disk IO operation will be slow due to NFS, or etc. | ||
ProgressManager.getInstance().run(object : Task.Backgroundable( | ||
project, | ||
SnakemakeBundle.message("notifier.msg.deleting.env.file", fileToCreatePath.name), | ||
false | ||
) { | ||
override fun run(indicator: ProgressIndicator) { | ||
doUndo() | ||
// XXX: Sometimes VFS refresh in onSuccess() called to early and doesn't see FS changes | ||
Thread.sleep(1000) | ||
} | ||
|
||
override fun onSuccess() { | ||
// Here is AWT thread! | ||
refreshFS() | ||
} | ||
}) | ||
} | ||
|
||
override fun redo() { | ||
// invoke in such way because: | ||
// (1) changing document inside undo/redo is not allowed (see ChangeFileEncodingAction) | ||
// (2) we don't want to use UI thread, e.g. if disk IO operation will be slow due to NFS, or etc. | ||
ProgressManager.getInstance().run(object : Task.Backgroundable( | ||
project, | ||
SnakemakeBundle.message("notifier.msg.creating.env.file", fileToCreatePath.name), | ||
false | ||
) { | ||
override fun run(indicator: ProgressIndicator) { | ||
doRedo(fileToCreatePath, project) | ||
// XXX: Sometimes VFS refresh in onSuccess() called to early and doesn't see FS changes | ||
Thread.sleep(1000) | ||
} | ||
|
||
override fun onSuccess() { | ||
// Here is AWT thread! | ||
refreshFS() | ||
} | ||
}) | ||
} | ||
|
||
private fun refreshFS() { | ||
firstCreatedFileOrDirParent?.refresh(true, true) { | ||
// Post action is called in AWT thread: | ||
DaemonCodeAnalyzer.getInstance(project).restart(actionInvocationTarget) | ||
} | ||
} | ||
|
||
override fun getAffectedDocuments(): Array<DocumentReference> { | ||
// * If not affected files - action not in undo/redo | ||
// * If 'all' affected files - action undo doesn't work | ||
|
||
// So:mark only the current editor file as affected to make feature convenient. | ||
// Otherwise, new file opening will be regarded as a new action | ||
// and 'undone' will be banned | ||
return arrayOf(DocumentReferenceManager.getInstance().create(actionInvocationTargetVFile)) | ||
} | ||
|
||
override fun isGlobal() = true | ||
|
||
private fun doRedo( | ||
fileToCreate: Path, | ||
project: Project, | ||
) { | ||
try { | ||
// file could be created in background by someone else or if action triggred twice | ||
if (fileToCreate.notExists()) { | ||
Files.createDirectories(fileToCreate.parent) | ||
val targetFile = Files.createFile(fileToCreate) | ||
|
||
val context = sectionToDefaultFileContent[sectionName] | ||
if (context != null) { | ||
// We don't use the result of 'createChildFile()' because it has inappropriate | ||
// type (and throws UnsupportedOperationException) | ||
targetFile.appendText(context) | ||
} | ||
} | ||
} catch (e: SecurityException) { | ||
val message = e.message ?: "Error: ${e.javaClass.name}" | ||
SmkNotifier.notify( | ||
title = SnakemakeBundle.message("notifier.msg.create.env.file.title"), | ||
content = message, | ||
type = NotificationType.ERROR, | ||
project = project | ||
) | ||
} catch (e: IOException) { | ||
LOGGER.warn(e) | ||
SmkNotifier.notify( | ||
title = SnakemakeBundle.message("notifier.msg.create.env.file.title"), | ||
content = SnakemakeBundle.message( | ||
"notifier.msg.create.env.file.io.exception", fileToCreate.name | ||
), | ||
type = NotificationType.ERROR, | ||
project = project | ||
) | ||
} | ||
} | ||
|
||
private fun doUndo() { | ||
if (firstCreatedFileOrDir.notExists()) { | ||
return | ||
} | ||
|
||
when { | ||
firstCreatedFileOrDir.isDirectory() -> FileUtils.deleteDirectory( | ||
firstCreatedFileOrDir.toFile() | ||
) | ||
else -> Files.delete(firstCreatedFileOrDir) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.