IDEA-72889 (CodeStyle inspection to check tabs instead of spaces in file)

This commit is contained in:
Bas Leijdekkers
2013-11-06 15:57:58 +01:00
parent f345cfdbbe
commit 37caefe8ac
6 changed files with 261 additions and 9 deletions
@@ -2388,6 +2388,10 @@
key="size.replaceable.by.isempty.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.style.SizeReplaceableByIsEmptyInspection"/>
<localInspection language="JAVA" shortName="ProblematicWhitespace" bundle="com.siyeh.InspectionGadgetsBundle"
key="problematic.whitespace.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.style.ProblematicWhitespaceInspection"/>
<!--group.names.threading.issues-->
<localInspection language="JAVA" suppressId="AccessToNonThreadSafeStaticField" shortName="AccessToNonThreadSafeStaticFieldFromInstance"
@@ -2053,3 +2053,7 @@ package.info.without.package.family.quickfix=add package statement
auto.closeable.resource.display.name=AutoCloseable used without 'try'-with-resources
auto.closeable.resource.problem.descriptor=''{0}'' used without ''try''-with-resources statement
auto.closeable.resource.returned.option=Ignore AutoCloseable instances returned from method calls
problematic.whitespace.display.name=Problematic whitespace
problematic.whitespace.tabs.problem.descriptor=File ''{0}'' uses tabs for indentation
problematic.whitespace.spaces.problem.descriptor=File ''{0}'' uses spaces for indentation
problematic.whitespace.show.whitespaces.quickfix=Toggle show whitespace in the editor
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2013 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -222,17 +222,10 @@ public abstract class BaseInspectionVisitor extends JavaElementVisitor {
}
@Override
public void visitReferenceExpression(
PsiReferenceExpression expression) {
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitExpression(expression);
}
@Override
public final void visitWhiteSpace(PsiWhiteSpace space) {
// none of our inspections need to do anything with white space,
// so this is a performance optimization
}
public final void setProblemsHolder(ProblemsHolder holder) {
this.holder = holder;
}
@@ -0,0 +1,165 @@
/*
* Copyright 2000-2013 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 com.siyeh.ig.style;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorSettings;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public class ProblematicWhitespaceInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("problematic.whitespace.display.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
final Boolean useTabs = (Boolean)infos[2];
return useTabs.booleanValue()
? InspectionGadgetsBundle.message("problematic.whitespace.spaces.problem.descriptor", (String)infos[0])
: InspectionGadgetsBundle.message("problematic.whitespace.tabs.problem.descriptor", (String)infos[0]);
}
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
final boolean buildFix = ((Boolean)infos[1]).booleanValue();
if (!buildFix) {
return null;
}
return new ShowWhitespaceFix();
}
private static class ShowWhitespaceFix extends InspectionGadgetsFix {
@NotNull
@Override
public String getName() {
return InspectionGadgetsBundle.message("problematic.whitespace.show.whitespaces.quickfix");
}
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) {
final FileEditorManager editorManager = FileEditorManager.getInstance(project);
final Editor editor = editorManager.getSelectedTextEditor();
if (editor == null) {
return;
}
final EditorSettings settings = editor.getSettings();
settings.setWhitespacesShown(!settings.isWhitespacesShown());
editor.getComponent().repaint();
}
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new ProblematicWhitespaceVisitor();
}
private static class ProblematicWhitespaceVisitor extends BaseInspectionVisitor {
@Override
public void visitJavaFile(PsiJavaFile file) {
super.visitJavaFile(file);
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject());
final CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptions(JavaFileType.INSTANCE);
final boolean useTabs = indentOptions.USE_TAB_CHARACTER;
final boolean smartTabs = indentOptions.SMART_TABS;
final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
if (document == null) {
return;
}
final int lineCount = document.getLineCount();
int previousLineIndent = 0;
for (int i = 0; i < lineCount; i++) {
final int startOffset = document.getLineStartOffset(i);
final int endOffset = document.getLineEndOffset(i);
final String line = document.getText(new TextRange(startOffset, endOffset));
boolean spaceSeen = false;
for (int j = 0, length = line.length(); j < length; j++) {
final char c = line.charAt(j);
if (c == '\t') {
if (useTabs) {
if (smartTabs && spaceSeen) {
registerError(file, file.getName(), Boolean.valueOf(isOnTheFly()), Boolean.TRUE);
return;
}
}
else {
registerError(file, file.getName(), Boolean.valueOf(isOnTheFly()), Boolean.FALSE);
return;
}
}
else if (c == ' ') {
if (useTabs) {
if (!smartTabs) {
registerError(file, file.getName(), Boolean.valueOf(isOnTheFly()), Boolean.TRUE);
return;
}
else {
final int currentIndent = Math.max(0, j);
if (currentIndent != previousLineIndent) {
registerError(file, file.getName(), Boolean.valueOf(isOnTheFly()), Boolean.TRUE);
return;
}
previousLineIndent = currentIndent;
}
}
spaceSeen = true;
}
else {
if (!spaceSeen) {
previousLineIndent = Math.max(0, j);
}
break;
}
}
}
}
}
}
@@ -0,0 +1,10 @@
<html>
<body>
Reports tabs used for indentation when the code style is configured to use only spaces. Reports spaces used for indentation when
the code style is configured to use only tabs. Reports spaces used for indentation and tabs used for alignment when the code
style is configured to use smart tabs.
<!-- tooltip end -->
<p>
</body>
</html>
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2013 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 com.siyeh.ig.style;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.siyeh.ig.LightInspectionTestCase;
/**
* @author Bas Leijdekkers
*/
public class ProblematicWhitespaceInspectionTest extends LightInspectionTestCase {
public void testTabsInFile() {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.getIndentOptions(JavaFileType.INSTANCE).USE_TAB_CHARACTER = false;
doTest("/*File 'X.java' uses tabs for indentation*/class X {\n" +
"\tString s;\n" +
"}\n/**/");
}
public void testTabsInFile2() {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.getIndentOptions(JavaFileType.INSTANCE).USE_TAB_CHARACTER = true;
doTest("class X {\n" +
"\tString s;\n" +
"}\n");
}
public void testSpacesInFile() {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.getIndentOptions(JavaFileType.INSTANCE).USE_TAB_CHARACTER = true;
doTest("/*File 'X.java' uses spaces for indentation*/class X {\n" +
" String s;\n" +
"}\n/**/");
}
public void testSpacesInFile2() {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.getIndentOptions(JavaFileType.INSTANCE).USE_TAB_CHARACTER = false;
doTest("class X {\n" +
" String s;\n" +
"}\n");
}
public void testSmartTabsInFile() {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
final CommonCodeStyleSettings.IndentOptions options = settings.getIndentOptions(JavaFileType.INSTANCE);
options.USE_TAB_CHARACTER = true;
options.SMART_TABS = true;
doTest("/*File 'X.java' uses spaces for indentation*/class X {\n" +
" \tString s;\n" +
"}\n/**/");
}
@Override
protected InspectionProfileEntry getInspection() {
return new ProblematicWhitespaceInspection();
}
}