有没有一种方法在Java中强制使用制表符而不是空格?

15

CheckStyle提供了检查空格的一致使用,但可悲的是缺少相反的想法:强制源代码使用制表符。有没有办法添加这个功能?它不必是CheckStyle,也可以使用其他工具。

此问题相同,但适用于Java。

编辑

我不需要一个代码美化器,因为代码库的正常状态将是所有制表符。我只需要一个能够报告替代缩进存在的工具。这样我就可以设置一个新的持续构建配置,当引入空格时将失败。


1
你有特定的编辑器想法,还是正在寻找基于检查 SVN 提交的解决方案? - jprete
我的编辑器已经具备此功能,但我收到的补丁并不符合规范。我已经使用CheckStyle报告其他样式问题。我想要一个类似的工具,可以独立运行或作为构建的一部分。 - Craig P. Motlin
1
这就是你意识到像谷歌的“Go语言”这样的语言有多么棒的时候。不再有“空格与制表符”的问题,不再有“两个或四个空格缩进”的问题,不再有“括号最好放在下一行”的“讨论”(更像是宗教信仰,但嘿😉),也不再有“UTF-8源代码与Latin-1源代码不兼容”的问题等等。代码格式由语言规范定义,并且有一个代码格式化程序生成唯一正确的格式。这样的规范使得像你这样的问题对于Go来说无关紧要。这有多酷?虽然这并不能帮助你,但至少看到了更好的画面很酷😉。 - SyntaxT3rr0r
1
请问 "enforce" 是什么意思?它仅仅是检测非一致性的前导空格还是还可以修复它使其符合一致性? - 9000
1
将制表符移到当前作用域,然后从那里使用空格。 - Chris Dennett
显示剩余2条评论
7个回答

12
尽管Checkstyle没有内置此功能,但您可以使用RegexpSinglelineJava检查来强制执行仅使用制表符进行缩进。请注意,这仅检查用于缩进的字符,而不是正确的缩进级别
Hibernate OGM源无耻地窃取:
<module name="RegexpSinglelineJava">
    <property name="format" value="^\t* +\t*\S"/>
    <property name="message" value="Line has leading space characters; indentation should be performed with tabs only."/>
    <property name="ignoreComments" value="true"/>
</module>

3
您可以通过使用“缩进”模块并将属性设置为“8”而不是“4”来实现正确的缩进级别。制表符计算为8个空格。 - Qix - MONICA WAS MISTREATED
2
或者通过在您的“Checker”模块中设置<property name="tabWidth" value="4"/> - Qix - MONICA WAS MISTREATED

7
使用空格而不是制表符进行缩进更可取,因为它可以在所有编辑器/查看器上保持一致的布局。但如果您仍然想使用制表符,您可以始终为checkstyle或定制maven插件/ant任务创建自定义检查。 逻辑应该很容易实现 - 您只需检查任何行的前导空格是否大于选项卡长度即可。
编辑:包括一个ant示例。 自从您发布以来已经两周了,您仍不满意,并且我有一些空闲时间 :) 因此,我为您准备了一个小型的ant自定义任务解决方案。
Ant任务
public class SpaceDetectorTask extends Task {
    public static final String REGEX = "^[ ]+";
    public static final Pattern p = Pattern.compile(REGEX);

    private FileSet fileSet;
    private boolean failOnDetection;

    // Usual getters/setters

    public void addFileSet(FileSet fileSet) {
        this.fileSet = fileSet;
    }

    public void execute() {
        DirectoryScanner ds = fileSet.getDirectoryScanner();
        String[] files = ds.getIncludedFiles();
        for (int x = 0; x <= files.length -1; x++) {
            process(ds.getBasedir(), files[x]);
        }
    }

    public void process(File dir, String file) {
        try {
            BufferedReader reader = new BufferedReader(new FileReader(new File(dir, file)));
            String line;
            int linecount = 0;
            System.out.println("File: " + file);
            boolean ignore = false;
            while((line = reader.readLine()) != null) {
                linecount++;

                // exclude comment blocks
                if (line.contains("/**") || line.contains("*/")) {
                    ignore = !ignore;
                    continue;
                }

                if (!ignore) {
                    if (p.matcher(line).find()) {
                        int spcCount = line.length() - (line.replaceAll(REGEX, "")).length();
                        if (spcCount >= 4) { // break whenever 4 leading spaces are detected. Configure as you need.
                            String msg = "File: "+ file + " is using spaces as indentation.";
                            if (failOnDetection) {
                                throw new BuildException(msg);
                            } else {
                                getProject().log(msg);
                            }
                        }
                    }
                    reader.close();
                }
            }
        } catch (IOException e) {
            if (failOnDetection) {
                throw new BuildException(e);
            } else {
                getProject().log("File: " + file + "\n" + e.getMessage());
            }
        }
    }

在ant的build.xml文件中

  1. Compile the task first
  2. Declare it

    <taskdef name="detect-spaces"
             classname="com.blah.blah.build.tools.SpaceDetectorTask">
            <classpath>
                <pathelement path="${dir.classes}"/>
                <fileset dir="C:/apache-ant-1.7.1/lib">
                    <include name="**/*.jar"/>
                </fileset>
            </classpath>
    </taskdef>
    
  3. use it

    <target name="rules.spaces">
        <detect-spaces
            failOnDetection="true">
            <fileset dir="${dir.src.java}">
                <include name="**/*.java"/>
            </fileset>
        </detect-spaces>
    </target>
    
编写一个maven/checkstyle插件也不应该很难。

1
使用空格而不是制表符进行缩进更受欢迎,因为它可以在所有编辑器/查看器中提供一致的布局。请注意,这只是一个观点,并不意味着要引发关于制表符与空格的争论。 - kurczynski

4
这种方法比其他提出的方法要简单得多。
在杂项组下创建一个正则表达式测试,并设置:
格式: ^ [\t]*
消息:缩进必须为制表符
非法模式:(已选中)

2

使用此命令搜索前导空格:

grep -e "^\t* " `find . -name *.java`

然后翻转退出状态。

如果他们混合使用制表符和空格,并且第一个字符恰好是制表符,则此方法将无法正常工作。 - Brigham
@Brigham 已修复正则表达式。 - Craig P. Motlin

1

Jalopy 可能是你正在寻找的。虽然它已经有一段时间没有更新了,但它仍然可以使用。还有一个商业版本可从Triemax获得。


0
在IntelliJ中,我让它在检入时执行代码格式化到标准格式。这样,您可以随意使用空格/制表符,但代码会定期更改为标准格式,并始终正确地保存在版本控制中(必须共享)。
肯定有类似的东西可以在没有IntelliJ的情况下完成,比如一个预检入脚本。
在80年代使用制表符而不是空格是个好主意,但你真的认为这在21世纪还很重要吗?这肯定不是关于磁盘空间的问题!

1
我已经写了为什么在团队合作时应该使用制表符的原因,链接在这里:http://motlin.com/2010/tabs-vs-spaces/ - Craig P. Motlin
鉴于Java代码规范中优先使用空格而非Tab,个人而言,我更喜欢坚持IDE强制实施的每次缩进4个空格的约定。也就是说,它会将制表符转换为空格并根据需要重新排列代码。我不建议任何人在Java开发中使用vim。我认为您在尝试极力解决一个相对较小的问题。如果这对您如此重要,我会担心您没有足够的事情可做。 ;) - Peter Lawrey
1
这简直不是事实。请参阅Java代码规范的第4节。“应使用四个空格作为缩进单位。缩进的确切构造(空格与制表符)未指定。制表符必须在每8个空格处设置一次(而不是4个)。 ”看看ArrayList的源代码,它使用了制表符和空格的混合,其中假定制表符代表8个空格。这是一个奇怪且糟糕的系统,除了Sun / Oracle之外,没有人使用。我不会在这里寻求他们的建议。http://www.oracle.com/technetwork/java/codeconventions-136091.html#262 - Craig P. Motlin
@Craig,我同意使用空格和制表符的混合是奇怪的,但你似乎认为只使用制表符是唯一明智/合乎逻辑的结论。我不同意这样的假设。你似乎也比实际上更强调这个观点的重要性。我同意,你需要为你的项目制定一个约定,并坚持它。 - Peter Lawrey

0

我们的Java格式化程序可以直接完成此操作。 JavaFormatter对源文本进行漂亮的打印(某些定义为漂亮的打印),并在每个嵌套块中逻辑地缩进一定数量的空格(例如,默认情况下为indent = 3)。

可以强制Java Formatter在左边距使用TAB字符作为空格,并且它将使用TAB跳到与用户指定的制表符停止列号相对应的下一列。如果您定义制表符具有与缩进距离相同的间隔,则通过格式化代码,您将获得每个缩进一个制表符字符。如果您定义indent = 8,并且制表符停止每8列(1,9,17,...),则倾向于获得与许多编辑器配合使用的选项卡,因为选项卡的默认解释是“每8列”。


2
看起来不错,但是它是商业软件 :-| ,而且只支持 Windows :-(。如果我必须要使用额外的工具,为什么不直接使用 Eclipse 中包含的免费源代码格式化工具呢?你甚至可以在命令行上使用 - Sean Patrick Floyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接