From 5b36417e3ce97392626b0278eff799774c529817 Mon Sep 17 00:00:00 2001 From: alingse Date: Sun, 8 Dec 2024 21:55:53 +0800 Subject: [PATCH] add option --- lib/build.gradle | 8 +- .../java/thriftlabs/thriftfmt/Option.java | 58 +++++++++ .../thriftfmt/PureThriftFormatter.java | 111 +++++++----------- .../thriftlabs/thriftfmt/ThriftFormatter.java | 62 +++++----- .../{FormatterUtil.java => Util.java} | 4 +- .../integration/ThriftFormatterTest.java | 72 ++++++++++++ 6 files changed, 215 insertions(+), 100 deletions(-) create mode 100644 lib/src/main/java/thriftlabs/thriftfmt/Option.java rename lib/src/main/java/thriftlabs/thriftfmt/{FormatterUtil.java => Util.java} (99%) create mode 100644 lib/src/test/java/thriftlabs/thriftfmt/integration/ThriftFormatterTest.java diff --git a/lib/build.gradle b/lib/build.gradle index 2436dac..d631984 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -19,11 +19,11 @@ dependencies { testImplementation libs.junit implementation 'org.antlr:antlr4-runtime:4.13.0' - implementation 'thriftlabs:thriftparser:0.0.3' + implementation 'thriftlabs:thriftparser:0.0.4' } group = 'thriftlabs' -version = '0.0.1' +version = '0.0.2' jar { archiveBaseName.set('thriftfmt') @@ -33,9 +33,9 @@ publishing { publications { mavenJava(MavenPublication) { from components.java - groupId = 'thriftlabs' + groupId = group artifactId = 'thriftfmt' - version = '0.0.1' + version = version } } diff --git a/lib/src/main/java/thriftlabs/thriftfmt/Option.java b/lib/src/main/java/thriftlabs/thriftfmt/Option.java new file mode 100644 index 0000000..1aef508 --- /dev/null +++ b/lib/src/main/java/thriftlabs/thriftfmt/Option.java @@ -0,0 +1,58 @@ +package thriftlabs.thriftfmt; + +public class Option { + public static final int DEFAULT_INDENT = 4; + // We use a comma as seprator, and this is not exported to set(maybe some day). + public static final String DEFAULT_SEPARATOR = ","; + + private int indent; + private boolean patchRequired; + private boolean patchSeparator; + private boolean keepComment; + private boolean alignByAssign; + private boolean alignByField; + + public Option() { + this(DEFAULT_INDENT, true, true, true, true, false); + } + + public Option(int indent, boolean patchRequired, boolean patchSeparator, boolean keepComment, boolean alignByAssign, + boolean alignByField) { + if (indent > 0) { + this.indent = indent; + } else { + this.indent = DEFAULT_INDENT; + } + + this.patchRequired = patchRequired; + this.patchSeparator = patchSeparator; + this.keepComment = keepComment; + + this.alignByAssign = alignByAssign; + this.alignByField = !alignByAssign && alignByField; + } + + public int getIndent() { + return indent; + } + + public boolean isPatchRequired() { + return patchRequired; + } + + public boolean isPatchSeparator() { + return patchSeparator; + } + + public boolean isKeepComment() { + return keepComment; + } + + public boolean isAlignByAssign() { + return alignByAssign; + } + + public boolean isAlignByField() { + return alignByField; + } +} diff --git a/lib/src/main/java/thriftlabs/thriftfmt/PureThriftFormatter.java b/lib/src/main/java/thriftlabs/thriftfmt/PureThriftFormatter.java index c11f9ba..d927086 100644 --- a/lib/src/main/java/thriftlabs/thriftfmt/PureThriftFormatter.java +++ b/lib/src/main/java/thriftlabs/thriftfmt/PureThriftFormatter.java @@ -10,38 +10,15 @@ public class PureThriftFormatter { - protected static class Option { - int indent; - boolean patchRequired; - boolean patchSeparator; - boolean keepComment; - boolean alignByAssign; - boolean alignByField; - - public Option(int indent, boolean patchRequired, boolean patchSeparator, boolean keepComment, - boolean alignByAssign, boolean alignByField) { - this.indent = indent; - this.patchRequired = patchRequired; - this.patchSeparator = patchSeparator; - this.keepComment = keepComment; - this.alignByAssign = alignByAssign; - this.alignByField = alignByField; - } - } - - protected Option option = new Option(4, true, true, true, false, false); + protected Option option = new Option(); protected String out; protected int newlineCounter; protected String currentIndent; - public void option(Option opt) { + public void setOption(Option opt) { this.option = opt; } - public String getOut() { - return out; - } - public String formatNode(ParseTree node) { out = ""; newlineCounter = 0; @@ -115,7 +92,7 @@ protected void processBlockNodes(List nodes, String indent) { } beforeBlockNode(node); if (index > 0 && lastNode != null) { - if (!lastNode.getClass().equals(node.getClass()) || FormatterUtil.isNeedNewLineNode(node)) { + if (!lastNode.getClass().equals(node.getClass()) || Util.isNeedNewLineNode(node)) { newline(2); } else { newline(); @@ -140,11 +117,11 @@ protected void processInlineNodes(List nodes, String join) { protected void processNode(ParseTree node) { beforeProcessNode(node); - _processNode(node); + dispatchProcessNode(node); afterProcessNode(node); } - private void _processNode(ParseTree node) { + protected void dispatchProcessNode(ParseTree node) { if (node instanceof TerminalNode) { TerminalNode((TerminalNode) node); } else if (node instanceof ThriftParser.DocumentContext) { @@ -228,7 +205,7 @@ private void _processNode(ParseTree node) { } protected void TerminalNode(TerminalNode node) { - if (FormatterUtil.isEOF(node)) { + if (Util.isEOF(node)) { return; } @@ -238,7 +215,7 @@ protected void TerminalNode(TerminalNode node) { } protected void DocumentContext(ThriftParser.DocumentContext node) { - this.processBlockNodes(FormatterUtil.getNodeChildren(node), ""); + this.processBlockNodes(Util.getNodeChildren(node), ""); } protected void HeaderContext(ThriftParser.HeaderContext node) { @@ -250,75 +227,75 @@ protected void DefinitionContext(ThriftParser.DefinitionContext node) { } private void Include_Context(ThriftParser.Include_Context node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Namespace_Context(ThriftParser.Namespace_Context node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Typedef_Context(ThriftParser.Typedef_Context node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Base_typeContext(ThriftParser.Base_typeContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Real_base_typeContext(ThriftParser.Real_base_typeContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Const_ruleContext(ThriftParser.Const_ruleContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Const_valueContext(ThriftParser.Const_valueContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void IntegerContext(ThriftParser.IntegerContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Container_typeContext(ThriftParser.Container_typeContext node) { - FormatterUtil.tightInline.process(this, node); + Util.tightInline.process(this, node); } protected void Set_typeContext(ThriftParser.Set_typeContext node) { - FormatterUtil.tightInline.process(this, node); + Util.tightInline.process(this, node); } protected void List_typeContext(ThriftParser.List_typeContext node) { - FormatterUtil.tightInline.process(this, node); + Util.tightInline.process(this, node); } protected void Cpp_typeContext(ThriftParser.Cpp_typeContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Const_mapContext(ThriftParser.Const_mapContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Const_map_entryContext(ThriftParser.Const_map_entryContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void List_separatorContext(ThriftParser.List_separatorContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Field_idContext(ThriftParser.Field_idContext node) { - FormatterUtil.tightInline.process(this, node); + Util.tightInline.process(this, node); } protected void Field_reqContext(ThriftParser.Field_reqContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Field_typeContext(ThriftParser.Field_typeContext node) { - FormatterUtil.defaultInline.process(this, node); + Util.defaultInline.process(this, node); } protected void Map_typeContext(ThriftParser.Map_typeContext node) { @@ -327,75 +304,75 @@ protected void Map_typeContext(ThriftParser.Map_typeContext node) { if (child.getParent() == null) { return false; } - if (!FormatterUtil.isToken(child.getParent().getChild(index - 1), ",")) { + if (!Util.isToken(child.getParent().getChild(index - 1), ",")) { return true; } return false; }; - FormatterUtil.genInlineContext(" ", tightFn).process(this, node); + Util.genInlineContext(" ", tightFn).process(this, node); } protected void Const_listContext(ThriftParser.Const_listContext node) { - FormatterUtil.listSeparatorInline.process(this, node); + Util.listSeparatorInline.process(this, node); } protected void Enum_ruleContext(ThriftParser.Enum_ruleContext node) { - FormatterUtil.genSubblocksContext(3, ThriftParser.Enum_fieldContext.class).process(this, node); + Util.genSubblocksContext(3, ThriftParser.Enum_fieldContext.class).process(this, node); } protected void Struct_Context(ThriftParser.Struct_Context node) { - FormatterUtil.fieldSubblocks.process(this, node); + Util.fieldSubblocks.process(this, node); } protected void Union_Context(ThriftParser.Union_Context node) { - FormatterUtil.fieldSubblocks.process(this, node); + Util.fieldSubblocks.process(this, node); } protected void Exception_Context(ThriftParser.Exception_Context node) { - FormatterUtil.fieldSubblocks.process(this, node); + Util.fieldSubblocks.process(this, node); } protected void Enum_fieldContext(ThriftParser.Enum_fieldContext node) { - FormatterUtil.listSeparatorInline.process(this, node); + Util.listSeparatorInline.process(this, node); } protected void FieldContext(ThriftParser.FieldContext node) { - FormatterUtil.listSeparatorInline.process(this, node); + Util.listSeparatorInline.process(this, node); } protected void Function_Context(ThriftParser.Function_Context node) { - FormatterUtil.tupleTightInline.process(this, node); + Util.tupleTightInline.process(this, node); } protected void OnewayContext(ThriftParser.OnewayContext node) { - FormatterUtil.genInlineContext(" ", null).process(this, node); + Util.genInlineContext(" ", null).process(this, node); } protected void Function_typeContext(ThriftParser.Function_typeContext node) { - FormatterUtil.genInlineContext(" ", null).process(this, node); + Util.genInlineContext(" ", null).process(this, node); } protected void Throws_listContext(ThriftParser.Throws_listContext node) { - FormatterUtil.tupleTightInline.process(this, node); + Util.tupleTightInline.process(this, node); } protected void Type_annotationsContext(ThriftParser.Type_annotationsContext node) { - FormatterUtil.tupleTightInline.process(this, node); + Util.tupleTightInline.process(this, node); } protected void Type_annotationContext(ThriftParser.Type_annotationContext node) { - FormatterUtil.tupleTightInline.process(this, node); + Util.tupleTightInline.process(this, node); } protected void Annotation_valueContext(ThriftParser.Annotation_valueContext node) { - FormatterUtil.genInlineContext(" ", null).process(this, node); + Util.genInlineContext(" ", null).process(this, node); } protected void ServiceContext(ThriftParser.ServiceContext node) { - if (FormatterUtil.isToken(node.getChild(2), "extends")) { - FormatterUtil.genSubblocksContext(5, ThriftParser.Function_Context.class).process(this, node); + if (Util.isToken(node.getChild(2), "extends")) { + Util.genSubblocksContext(5, ThriftParser.Function_Context.class).process(this, node); } else { - FormatterUtil.genSubblocksContext(3, ThriftParser.Function_Context.class).process(this, node); + Util.genSubblocksContext(3, ThriftParser.Function_Context.class).process(this, node); } } diff --git a/lib/src/main/java/thriftlabs/thriftfmt/ThriftFormatter.java b/lib/src/main/java/thriftlabs/thriftfmt/ThriftFormatter.java index d71818d..79b85a7 100644 --- a/lib/src/main/java/thriftlabs/thriftfmt/ThriftFormatter.java +++ b/lib/src/main/java/thriftlabs/thriftfmt/ThriftFormatter.java @@ -33,18 +33,26 @@ public ThriftFormatter(Thrift.ParserResult data) { this.fieldAlignByFieldPaddingMap = new HashMap<>(); } + public ThriftFormatter(Thrift.ParserResult data, Option opt) { + this(data); + if (opt == null) { + throw new IllegalArgumentException("Option cannot be null."); + } + this.setOption(opt); + } + public String format() { patch(); return formatNode(document); } private void patch() { - if (this.option.patchRequired) { - FormatterUtil.walkNode(this.document, node -> this.patchFieldRequired(node)); + if (this.option.isPatchRequired()) { + Util.walkNode(this.document, node -> this.patchFieldRequired(node)); } - if (this.option.patchSeparator) { - FormatterUtil.walkNode(this.document, node -> this.patchFieldListSeparator(node)); - FormatterUtil.walkNode(this.document, node -> this.patchRemoveLastListSeparator(node)); + if (this.option.isPatchSeparator()) { + Util.walkNode(this.document, node -> this.patchFieldListSeparator(node)); + Util.walkNode(this.document, node -> this.patchRemoveLastListSeparator(node)); } } @@ -56,7 +64,7 @@ private void patchFieldRequired(ParseTree node) { ThriftParser.FieldContext field = (ThriftParser.FieldContext) node; // 检查父节点是否为 undefined 或者是 FunctionOrThrowsListNode 的实例 - if (field.getParent() == null || FormatterUtil.isFunctionOrThrowsListNode(field.getParent())) { + if (field.getParent() == null || Util.isFunctionOrThrowsListNode(field.getParent())) { return; } @@ -73,7 +81,7 @@ private void patchFieldRequired(ParseTree node) { } // 创建伪节点 - TerminalNode fakeNode = FormatterUtil.createFakeNode(ThriftParser.T__20, "required"); + TerminalNode fakeNode = Util.createFakeNode(ThriftParser.T__20, "required"); ThriftParser.Field_reqContext fakeReq = new ThriftParser.Field_reqContext(field, 0); fakeNode.setParent(fakeReq); @@ -95,12 +103,12 @@ private void patchFieldListSeparator(ParseTree node) { if (child instanceof ThriftParser.List_separatorContext) { TerminalNodeImpl comma = (TerminalNodeImpl) child.getChild(0); CommonToken token = (CommonToken) comma.getSymbol(); - token.setText(","); + token.setText(Option.DEFAULT_SEPARATOR); return; } - ParserRuleContext currentNode = (ParserRuleContext) node; - TerminalNode fakeNode = FormatterUtil.createFakeNode(ThriftParser.COMMA, ","); + ParserRuleContext currentNode = (ParserRuleContext) node; + TerminalNode fakeNode = Util.createFakeNode(ThriftParser.COMMA, Option.DEFAULT_SEPARATOR); ThriftParser.List_separatorContext fakeCtx = new ThriftParser.List_separatorContext(currentNode, 0); fakeNode.setParent(fakeCtx); @@ -113,7 +121,7 @@ private void patchFieldListSeparator(ParseTree node) { private void patchRemoveLastListSeparator(ParseTree node) { boolean isInlineField = node instanceof ThriftParser.FieldContext && node.getParent() != null && - FormatterUtil.isFunctionOrThrowsListNode(node.getParent()); + Util.isFunctionOrThrowsListNode(node.getParent()); boolean isInlineNode = node instanceof ThriftParser.Type_annotationContext; if (!(isInlineField || isInlineNode)) { return; @@ -131,7 +139,7 @@ private void patchRemoveLastListSeparator(ParseTree node) { for (int i = 0; i < brotherCount; i++) { if (brothers.get(i) == node) { - if (i == brotherCount - 1 || !FormatterUtil.notSameClass(node, brothers.get(i + 1))) { + if (i == brotherCount - 1 || !Util.notSameClass(node, brothers.get(i + 1))) { last = true; break; } @@ -148,30 +156,30 @@ private void patchRemoveLastListSeparator(ParseTree node) { private int calcAddIndentPadding(int padding) { if (padding > 0) { - padding += this.option.indent; + padding += this.option.getIndent(); } return padding; } protected void beforeSubblocks(List subblocks) { - if (this.option.alignByField) { - Pair, Integer> result = FormatterUtil.calcFieldAlignByFieldPaddingMap(subblocks); + if (this.option.isAlignByField()) { + Pair, Integer> result = Util.calcFieldAlignByFieldPaddingMap(subblocks); Map paddingMap = result.a; Integer commentPadding = result.b; paddingMap.forEach((key, value) -> paddingMap.put(key, this.calcAddIndentPadding(value))); this.fieldAlignByFieldPaddingMap = paddingMap; this.fieldCommentPadding = this.calcAddIndentPadding(commentPadding); - } else if (this.option.alignByAssign) { - Pair result = FormatterUtil.calcFieldAlignByAssignPadding(subblocks); + } else if (this.option.isAlignByAssign()) { + Pair result = Util.calcFieldAlignByAssignPadding(subblocks); int alignPadding = result.a; int commentPadding = result.b; this.fieldAlignByAssignPadding = this.calcAddIndentPadding(alignPadding); this.fieldCommentPadding = this.calcAddIndentPadding(commentPadding); } - if (this.option.keepComment && this.fieldCommentPadding == 0) { - int commentPadding = FormatterUtil.calcSubBlocksCommentPadding(subblocks); + if (this.option.isKeepComment() && this.fieldCommentPadding == 0) { + int commentPadding = Util.calcSubBlocksCommentPadding(subblocks); this.fieldCommentPadding = this.calcAddIndentPadding(commentPadding); } } @@ -191,12 +199,12 @@ protected void beforeProcessNode(ParseTree n) { } private void addAlignPadding(ParseTree n) { - if (!FormatterUtil.isFieldOrEnumField(n.getParent())) { + if (!Util.isFieldOrEnumField(n.getParent())) { return; } - if (this.option.alignByField && !this.fieldAlignByFieldPaddingMap.isEmpty()) { - String name = FormatterUtil.getFieldChildName(n); + if (this.option.isAlignByField() && !this.fieldAlignByFieldPaddingMap.isEmpty()) { + String name = Util.getFieldChildName(n); Integer padding = this.fieldAlignByFieldPaddingMap.get(name); if (padding != null && padding > 0) { this.padding(padding); @@ -204,7 +212,7 @@ private void addAlignPadding(ParseTree n) { return; } - if (this.option.alignByAssign && FormatterUtil.isToken(n, "=")) { + if (this.option.isAlignByAssign() && Util.isToken(n, "=")) { this.padding(this.fieldAlignByAssignPadding); return; } @@ -236,7 +244,7 @@ protected String getCurrentLine() { } private void addTailComment() { - if (!this.option.keepComment) { + if (!this.option.isKeepComment()) { return; } if (this.lastTokenIndex == -1) { @@ -271,11 +279,11 @@ private void addTailComment() { } private void addInlineComments(TerminalNode node) { - if (!this.option.keepComment) { + if (!this.option.isKeepComment()) { return; } - if (FormatterUtil.isFakeNode(node)) { + if (Util.isFakeNode(node)) { return; } @@ -304,7 +312,7 @@ private void addInlineComments(TerminalNode node) { int lastLine = token.getLine() + text.split("\n").length - 1; int lineDiff = node.getSymbol().getLine() - lastLine; boolean isTight = token.getType() == ThriftParser.SL_COMMENT || - FormatterUtil.isEOF(node) || + Util.isEOF(node) || (0 < lineDiff && lineDiff <= 1); if (isTight) { diff --git a/lib/src/main/java/thriftlabs/thriftfmt/FormatterUtil.java b/lib/src/main/java/thriftlabs/thriftfmt/Util.java similarity index 99% rename from lib/src/main/java/thriftlabs/thriftfmt/FormatterUtil.java rename to lib/src/main/java/thriftlabs/thriftfmt/Util.java index 8b7cd78..1666da4 100644 --- a/lib/src/main/java/thriftlabs/thriftfmt/FormatterUtil.java +++ b/lib/src/main/java/thriftlabs/thriftfmt/Util.java @@ -18,7 +18,7 @@ import thriftlabs.thriftparser.ThriftParser; -public final class FormatterUtil { +class Util { public static final int FAKE_NODE_LINE_NO = -1; public static final int FAKE_TOKEN_INDEX = -1; // 用于 token @@ -176,7 +176,7 @@ public void process(PureThriftFormatter formatter, ParseTree node) { List leftNodes = result.b; // 剩余的节点 formatter.beforeSubblocks(subblocks); - formatter.processBlockNodes(subblocks, " ".repeat(formatter.option.indent)); + formatter.processBlockNodes(subblocks, " ".repeat(formatter.option.getIndent())); formatter.afterSubblocks(subblocks); formatter.newline(); diff --git a/lib/src/test/java/thriftlabs/thriftfmt/integration/ThriftFormatterTest.java b/lib/src/test/java/thriftlabs/thriftfmt/integration/ThriftFormatterTest.java new file mode 100644 index 0000000..a8c564d --- /dev/null +++ b/lib/src/test/java/thriftlabs/thriftfmt/integration/ThriftFormatterTest.java @@ -0,0 +1,72 @@ +package thriftlabs.thriftfmt.integration; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +import thriftlabs.thriftparser.Thrift; +import thriftlabs.thriftfmt.ThriftFormatter; +import thriftlabs.thriftfmt.Option; + +public class ThriftFormatterTest { + + @SuppressWarnings("resource") + public String readResourceFile(String fileName) { + ClassLoader classLoader = getClass().getClassLoader(); + try (InputStream inputStream = classLoader.getResourceAsStream(fileName)) { + if (inputStream == null) { + throw new IllegalArgumentException("File not found: " + fileName); + } + return new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A").next(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Test + public void testFindThriftFiles() { + String directory = "src/test/resources/thrifts"; // 资源目录 + try { + List thriftFiles = findThriftFiles(directory); + thriftFiles.forEach(path -> testFormatterFile(path)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public List findThriftFiles(String directory) throws IOException { + Path dirPath = Paths.get(directory); + return Files.walk(dirPath) + .filter(path -> path.toString().endsWith(".thrift")) + .map(Path::getFileName) + .map(Path::toString) + .collect(Collectors.toList()); + } + + public void testFormatterFile(String fileName) { + String content = readResourceFile("thrifts/" + fileName); + assertNotNull("Fixture file should be found", content); + + Thrift.ParserResult result = Thrift.parse(content); + assertTrue(result.isSuccess()); + + var formatter = new ThriftFormatter(result); + formatter.setOption(new Option(4, true, true, true, false, false)); + formatter.format(); + + var option = new Option(4, true, true, true, true, false); + var formatter2 = new ThriftFormatter(result, option); + formatter2.format(); + } +}