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");