From d38fd0fdf866c8cab17f7ca8bc4131ea97dcd2de Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 11 Aug 2017 12:31:45 +0300 Subject: [PATCH] Need reformat inspection --- .../inspectionDescriptions/Reformat.html | 5 ++ idea/src/META-INF/plugin.xml | 9 ++ .../CollectChangesWithoutApplyModel.kt | 85 +++++++++++++++++++ .../KotlinFormattingModelBuilder.java | 12 ++- .../idea/inspections/ReformatInspection.kt | 79 +++++++++++++++++ .../reformat/inspectionData/expected.xml | 42 +++++++++ .../reformat/inspectionData/inspections.test | 1 + .../testData/inspections/reformat/reformat.kt | 12 +++ .../codeInsight/InspectionTestGenerated.java | 6 ++ 9 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 idea/resources/inspectionDescriptions/Reformat.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/formatter/CollectChangesWithoutApplyModel.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/ReformatInspection.kt create mode 100644 idea/testData/inspections/reformat/inspectionData/expected.xml create mode 100644 idea/testData/inspections/reformat/inspectionData/inspections.test create mode 100644 idea/testData/inspections/reformat/reformat.kt diff --git a/idea/resources/inspectionDescriptions/Reformat.html b/idea/resources/inspectionDescriptions/Reformat.html new file mode 100644 index 00000000000..cc4c4eac020 --- /dev/null +++ b/idea/resources/inspectionDescriptions/Reformat.html @@ -0,0 +1,5 @@ + + +This inspection reports places that are not formatted according to project settings. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index a96f2ece588..58ea2be399e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1648,6 +1648,15 @@ language="kotlin" /> + + { + try { + file.collectFormattingChanges = true + CodeStyleManager.getInstance(file.project).reformat(file) + return file.collectChangesFormattingModel?.requestedChanges ?: emptySet() + } + finally { + file.collectFormattingChanges = false + file.collectChangesFormattingModel = null + } +} + +private class CollectChangesWithoutApplyModel(val file: PsiFile, val block: Block) : FormattingModel { + private val documentModel = FormattingDocumentModelImpl.createOn(file) + private val changes = HashSet() + + val requestedChanges: Set get() = changes + + override fun commitChanges() { + /* do nothing */ + } + + override fun getDocumentModel(): FormattingDocumentModel = documentModel + override fun getRootBlock(): Block = block + + override fun shiftIndentInsideRange(node: ASTNode?, range: TextRange, indent: Int): TextRange { + changes.add(ShiftIndentInsideRange(node, range, indent)) + return range + } + + override fun replaceWhiteSpace(textRange: TextRange, whiteSpace: String): TextRange { + changes.add(ReplaceWhiteSpace(textRange, whiteSpace)) + return textRange + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java index 7b95ab5b2fe..53d01dd02d7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java @@ -16,7 +16,10 @@ package org.jetbrains.kotlin.idea.formatter; -import com.intellij.formatting.*; +import com.intellij.formatting.FormattingModel; +import com.intellij.formatting.FormattingModelBuilder; +import com.intellij.formatting.FormattingModelProvider; +import com.intellij.formatting.Indent; import com.intellij.lang.ASTNode; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.util.TextRange; @@ -45,6 +48,13 @@ public class KotlinFormattingModelBuilder implements FormattingModelBuilder { return new PsiBasedFormattingModel(containingFile, block, formattingDocumentModel); } + if (element instanceof PsiFile) { + FormattingModel collectChangesModel = CollectChangesWithoutApplyModelKt.createCollectFormattingChangesModel((PsiFile) element, block); + if (collectChangesModel != null) { + return collectChangesModel; + } + } + return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings); } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReformatInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReformatInspection.kt new file mode 100644 index 00000000000..b7e4c7b98fc --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReformatInspection.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.inspections + +import com.intellij.codeInspection.* +import com.intellij.codeInspection.ex.ProblemDescriptorImpl +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.idea.formatter.FormattingChange +import org.jetbrains.kotlin.idea.formatter.FormattingChange.ReplaceWhiteSpace +import org.jetbrains.kotlin.idea.formatter.FormattingChange.ShiftIndentInsideRange +import org.jetbrains.kotlin.idea.formatter.collectFormattingChanges +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.psi.KtFile + +class ReformatInspection : LocalInspectionTool() { + override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array? { + if (file !is KtFile || !ProjectRootsUtil.isInProjectSource(file)) { + return null + } + + val changes = collectFormattingChanges(file) + if (changes.isEmpty()) return null + + val elements = changes.map { + val rangeOffset = when (it) { + is ShiftIndentInsideRange -> it.range.startOffset + is ReplaceWhiteSpace -> it.textRange.startOffset + } + + val leaf = file.findElementAt(rangeOffset) ?: return@map null + if (!leaf.isValid) return@map null + if (leaf is PsiWhiteSpace && isEmptyLineReformat(leaf, it)) return@map null + + leaf + }.filterNotNull() + + return elements.map { + ProblemDescriptorImpl(it, it, + "File is not properly formatted", + arrayOf(ReformatQuickFix), + ProblemHighlightType.WEAK_WARNING, false, null, + isOnTheFly) + }.toTypedArray() + } + + private fun isEmptyLineReformat(whitespace: PsiWhiteSpace, change: FormattingChange): Boolean { + if (change !is FormattingChange.ReplaceWhiteSpace) return false + + val beforeText = whitespace.text + val afterText = change.whiteSpace + + return beforeText.count { it == '\n' } == afterText.count { it == '\n' } && + beforeText.substringAfterLast('\n') == afterText.substringAfterLast('\n') + } + + private object ReformatQuickFix : LocalQuickFix { + override fun getFamilyName(): String = "Reformat File" + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + CodeStyleManager.getInstance(project).reformat(descriptor.psiElement.containingFile) + } + } +} \ No newline at end of file diff --git a/idea/testData/inspections/reformat/inspectionData/expected.xml b/idea/testData/inspections/reformat/inspectionData/expected.xml new file mode 100644 index 00000000000..bf6efb3e33e --- /dev/null +++ b/idea/testData/inspections/reformat/inspectionData/expected.xml @@ -0,0 +1,42 @@ + + + reformat.kt + 4 + light_idea_test_case + + File is not formatted according to project settings + File is not properly formatted + + + reformat.kt + 4 + light_idea_test_case + + File is not formatted according to project settings + File is not properly formatted + + + reformat.kt + 4 + light_idea_test_case + + File is not formatted according to project settings + File is not properly formatted + + + reformat.kt + 5 + light_idea_test_case + + File is not formatted according to project settings + File is not properly formatted + + + reformat.kt + 10 + light_idea_test_case + + File is not formatted according to project settings + File is not properly formatted + + \ No newline at end of file diff --git a/idea/testData/inspections/reformat/inspectionData/inspections.test b/idea/testData/inspections/reformat/inspectionData/inspections.test new file mode 100644 index 00000000000..7ccdc420326 --- /dev/null +++ b/idea/testData/inspections/reformat/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ReformatInspection diff --git a/idea/testData/inspections/reformat/reformat.kt b/idea/testData/inspections/reformat/reformat.kt new file mode 100644 index 00000000000..d7f8679b2ce --- /dev/null +++ b/idea/testData/inspections/reformat/reformat.kt @@ -0,0 +1,12 @@ +package format.inspection + +fun test() { + if(true){} +} + + + +fun other() { + val test = 12 + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index b8b338efd5a..e13e197d39d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -335,6 +335,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("reformat/inspectionData/inspections.test") + public void testReformat_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/reformat/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("removeSetterParameterType/inspectionData/inspections.test") public void testRemoveSetterParameterType_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/removeSetterParameterType/inspectionData/inspections.test");