-
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.
Features/#148 multiline function call in a single-line declared secti…
- Loading branch information
Showing
8 changed files
with
237 additions
and
9 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
99 changes: 99 additions & 0 deletions
99
src/main/kotlin/com/jetbrains/snakecharm/inspections/SmkMultilineFunctionCallInspection.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,99 @@ | ||
package com.jetbrains.snakecharm.inspections | ||
|
||
import com.intellij.codeInspection.LocalInspectionToolSession | ||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement | ||
import com.intellij.codeInspection.ProblemsHolder | ||
import com.intellij.openapi.project.Project | ||
import com.intellij.psi.* | ||
import com.intellij.psi.util.elementType | ||
import com.intellij.refactoring.suggested.startOffset | ||
import com.jetbrains.python.PyTokenTypes | ||
import com.jetbrains.python.psi.PyCallExpression | ||
import com.jetbrains.snakecharm.SnakemakeBundle | ||
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpointArgsSection | ||
|
||
class SmkMultilineFunctionCallInspection : SnakemakeInspection() { | ||
override fun buildVisitor( | ||
holder: ProblemsHolder, | ||
isOnTheFly: Boolean, | ||
session: LocalInspectionToolSession | ||
) = object : SnakemakeInspectionVisitor(holder, session) { | ||
override fun visitSmkRuleOrCheckpointArgsSection(st: SmkRuleOrCheckpointArgsSection) { | ||
val args = st.argumentList ?: return | ||
|
||
if (st.multilineSectionDefinition()) { | ||
return | ||
} | ||
|
||
val invalidWhitespaces = mutableListOf<PsiElement>() | ||
args.arguments.forEach { psi -> | ||
if (psi is PyCallExpression) { | ||
collectNewlinesInMultilineCall(psi, invalidWhitespaces) | ||
} | ||
} | ||
|
||
invalidWhitespaces.forEach { | ||
registerProblem( | ||
it, | ||
SnakemakeBundle.message("INSP.NAME.multiline.func.call"), | ||
ShiftToNextLine(st, invalidWhitespaces) | ||
) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Checks whether there are any whitespaces in [expression] argument list, | ||
* if so, checks if it contains new line character, | ||
* if so, adds whitespace nodes to [incorrectElements] | ||
*/ | ||
private fun collectNewlinesInMultilineCall( | ||
expression: PyCallExpression, | ||
incorrectElements: MutableList<PsiElement> | ||
) { | ||
var element = expression.argumentList?.firstChild | ||
while (element != null) { | ||
if (element.elementType == TokenType.WHITE_SPACE && element.text.contains('\n')) { | ||
incorrectElements.add(element) | ||
break | ||
} | ||
element = element.nextSibling | ||
} | ||
} | ||
|
||
private class ShiftToNextLine(expr: PsiElement, val incorrectElements: MutableList<PsiElement>) : | ||
LocalQuickFixOnPsiElement(expr) { | ||
|
||
override fun getFamilyName() = SnakemakeBundle.message("INSP.NAME.multiline.func.call.fix") | ||
|
||
override fun getText() = familyName | ||
|
||
override fun invoke(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement) { | ||
val doc = PsiDocumentManager.getInstance(project).getDocument(file) | ||
val indentCandidate = startElement.prevSibling | ||
if (indentCandidate !is PsiWhiteSpace || doc == null) { | ||
return | ||
} | ||
val argumentList = (startElement as SmkRuleOrCheckpointArgsSection).argumentList ?: return | ||
val indent = indentCandidate.text.replace("\n", "") | ||
// Moves every argument list element to new line | ||
argumentList.arguments.forEach { expression -> | ||
doc.insertString(expression.startOffset, "\n$indent$indent") | ||
PsiDocumentManager.getInstance(project).commitDocument(doc) | ||
} | ||
// Deletes every incorrect whitespace | ||
// If there are END_OF_LINE_COMMENT, new whitespace will be inserted automatically | ||
// Otherwise, we need to insert it manually | ||
incorrectElements.forEach { space -> | ||
val hasComment = space.prevSibling.elementType == PyTokenTypes.END_OF_LINE_COMMENT | ||
val offset = space.startOffset | ||
space.delete() | ||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(doc) | ||
if (!hasComment) { | ||
doc.insertString(offset, "\n$indent$indent$indent") | ||
PsiDocumentManager.getInstance(project).commitDocument(doc) | ||
} | ||
} | ||
} | ||
} | ||
} |
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
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
5 changes: 5 additions & 0 deletions
5
src/main/resources/inspectionDescriptions/SmkMultilineFunctionCallInspection.html
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,5 @@ | ||
<html lang="Snakemake"> | ||
<body> | ||
Checks whether there are function call which spans over multiple lines but starts at the same line as section. | ||
</body> | ||
</html> |
83 changes: 83 additions & 0 deletions
83
...st/resources/features/highlighting/inspections/multiline_function_call_inspection.feature
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,83 @@ | ||
Feature: Inspection for multiline function calls in sections, which were declared in a single line style | ||
|
||
Scenario Outline: No inspection in multiline declared section | ||
Given a snakemake project | ||
Given I open a file "foo.smk" with text | ||
""" | ||
<rule_like> NAME: | ||
input: | ||
foo("abc","abcde"), | ||
foo2("text1", | ||
"text2","text3"), | ||
foo3("text4","text2", | ||
"text3") | ||
<rule_like> rule_148_c: | ||
input: #ffff | ||
foo1("text1", | ||
"text2", "text3") | ||
""" | ||
And SmkMultilineFunctionCallInspection inspection is enabled | ||
Then I expect no inspection errors | ||
When I check highlighting errors | ||
Examples: | ||
| rule_like | | ||
| rule | | ||
| checkpoint | | ||
|
||
Scenario Outline: Inspection in single line declared section | ||
Given a snakemake project | ||
Given I open a file "foo.smk" with text | ||
""" | ||
<rule_like> NAME: | ||
input: foo("abc","abcde"), foo2("text1", | ||
"text2","text3"), foo3("text4","text2", | ||
"text3") | ||
""" | ||
And SmkMultilineFunctionCallInspection inspection is enabled | ||
Then I expect inspection error on pattern <\n > with message | ||
""" | ||
Invalid function call. Rewrite section as multiline or rewrite function using a single line | ||
""" | ||
Then I expect inspection error on pattern <\n > with message | ||
""" | ||
Invalid function call. Rewrite section as multiline or rewrite function using a single line | ||
""" | ||
When I check highlighting errors | ||
Examples: | ||
| rule_like | | ||
| rule | | ||
| checkpoint | | ||
|
||
Scenario Outline: Quickfix for inspection in single line declared section | ||
Given a snakemake project | ||
Given I open a file "foo.smk" with text | ||
""" | ||
<rule_like> NAME: | ||
input:foo("abc","abcde"),foo2("text1", #comments here | ||
"text2","text3"),foo3("text4","text2", | ||
"text3") | ||
""" | ||
And SmkMultilineFunctionCallInspection inspection is enabled | ||
Then I expect inspection error on pattern <\n > with message | ||
""" | ||
Invalid function call. Rewrite section as multiline or rewrite function using a single line | ||
""" | ||
Then I expect inspection error on pattern <\n > with message | ||
""" | ||
Invalid function call. Rewrite section as multiline or rewrite function using a single line | ||
""" | ||
When I check highlighting errors | ||
Then I invoke quick fix Rewrite section as multiline and see text: | ||
""" | ||
<rule_like> NAME: | ||
input: | ||
foo("abc","abcde"), | ||
foo2("text1", #comments here | ||
"text2","text3"), | ||
foo3("text4","text2", | ||
"text3") | ||
""" | ||
Examples: | ||
| rule_like | | ||
| rule | | ||
| checkpoint | |