diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7e76021c9..704200cf4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -13,6 +13,10 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co == Unreleased +Bug Fixes:: + +* Column#setWidth is ignored (#1265) (@Vampire) + == 2.5.12 (2024-03-10) Improvement:: diff --git a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Table.java b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Table.java index 1830adaf8..a9b031041 100644 --- a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Table.java +++ b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Table.java @@ -53,4 +53,10 @@ enum VerticalAlignment { */ void setGrid(String grid); + /** + * Computes the percentual column widths for all columns. + * Before calling this method all columns must have been added to the Table by adding them to + * {@link #getColumns()} and the desired widths must have been set by calling {@link Column#setWidth(int)}. + */ + void assignColumnWidths(); } diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/TableImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/TableImpl.java index f5b269ed3..5cc94f31d 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/TableImpl.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/TableImpl.java @@ -7,6 +7,7 @@ import org.asciidoctor.jruby.internal.RubyObjectWrapper; import org.jruby.RubyArray; import org.jruby.RubyNil; +import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import java.util.AbstractList; @@ -72,6 +73,26 @@ public List getHeader() { return rows.getHeader(); } + @Override + public void assignColumnWidths() { + int widthBase = 0; + RubyArray autowidthCols = getRuntime().newArray(); + for (Column column : getColumns()) { + int width = column.getWidth(); + if (width < 0) { + autowidthCols.add(((ColumnImpl)column).getRubyObject()); + } else { + widthBase += width; + } + } + ThreadContext threadContext = getRuntime().getThreadService().getCurrentContext(); + + getRubyObject().callMethod(threadContext, "assign_column_widths", new IRubyObject[] { + getRuntime().newFixnum(widthBase), + autowidthCols.isEmpty() ? getRuntime().getNil() : autowidthCols + }); + } + private class Rows extends RubyObjectWrapper { public Rows(IRubyObject rubyNode) { diff --git a/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/GithubContributorsBlockMacro.groovy b/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/GithubContributorsBlockMacro.groovy index 3f555b202..4c98ecc33 100644 --- a/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/GithubContributorsBlockMacro.groovy +++ b/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/GithubContributorsBlockMacro.groovy @@ -12,6 +12,7 @@ import org.asciidoctor.util.TestHttpServer class GithubContributorsBlockMacro extends BlockMacroProcessor { private static final String IMAGE = 'image' + private static final String WIDTHS = 'widths' GithubContributorsBlockMacro(String macroName) { super(macroName) @@ -29,12 +30,22 @@ class GithubContributorsBlockMacro extends BlockMacroProcessor { table.grid = 'rows' table.title = "Github contributors of $target" + List widths = [1,2,2] + if (attributes.containsKey(WIDTHS)) { + widths = (attributes[WIDTHS] as String).split(',').collect {Integer.parseInt(it) } + } // Create the columns 'Login' and 'Contributions' Column avatarColumn = createTableColumn(table, 0) + avatarColumn.width = widths[0] Column loginColumn = createTableColumn(table, 1) + loginColumn.width = widths[1] Column contributionsColumn = createTableColumn(table, 2) + contributionsColumn.width = widths[2] contributionsColumn.horizontalAlignment = Table.HorizontalAlignment.CENTER - + table.columns << avatarColumn + table.columns << loginColumn + table.columns << contributionsColumn + table.assignColumnWidths() // Create the single header row with the column titles Row headerRow = createTableRow(table) headerRow.cells << createTableCell(avatarColumn, 'Avatar') diff --git a/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenABlockMacroProcessorCreatesATable.groovy b/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenABlockMacroProcessorCreatesATable.groovy index e35adca22..bb7178d67 100644 --- a/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenABlockMacroProcessorCreatesATable.groovy +++ b/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenABlockMacroProcessorCreatesATable.groovy @@ -1,6 +1,7 @@ package org.asciidoctor.extension import org.asciidoctor.Asciidoctor +import org.asciidoctor.Options import org.asciidoctor.OptionsBuilder import org.asciidoctor.SafeMode import org.asciidoctor.util.ClasspathResources @@ -9,10 +10,13 @@ import org.jboss.arquillian.spock.ArquillianSputnik import org.jboss.arquillian.test.api.ArquillianResource import org.jsoup.Jsoup import org.jsoup.nodes.Document +import org.jsoup.select.Elements import org.junit.rules.TemporaryFolder import org.junit.runner.RunWith import spock.lang.Specification +import static java.nio.charset.StandardCharsets.UTF_8 + @RunWith(ArquillianSputnik) class WhenABlockMacroProcessorCreatesATable extends Specification { @@ -20,10 +24,23 @@ class WhenABlockMacroProcessorCreatesATable extends Specification { public static final String SECOND_TD = 'td:nth-child(2)' public static final String THIRD_TD = 'td:nth-child(3)' public static final String IMG_ELEMENT = 'img' + public static final String COL = 'col' + public static final String STYLE = 'style' + public static final String WIDTH = 'width' + public static final String WIDTH_2 = '2%' + public static final String WIDTH_3 = '3%' + public static final String WIDTH_20 = '20%' + public static final String WIDTH_40 = '40%' public static final String CONTRIBUTOR = 'bob' public static final String BLOCKMACRO_NAME = 'githubcontributors' public static final String AVATAR_URL_REGEXP = /https:\/\/avatars.githubusercontent.com\/u\/.*/ + public static final String CSS_QUERY_TABLE = 'table' + public static final String CLASS_GRID_ROWS = 'grid-rows' + public static final String CLASS_HALIGN_LEFT = 'halign-left' + public static final String CLASS_HALIGN_CENTER = 'halign-center' + public static final String ATTR_SRC = 'src' + public static final int THREE = 3 @ArquillianResource private Asciidoctor asciidoctor @@ -31,14 +48,21 @@ class WhenABlockMacroProcessorCreatesATable extends Specification { @ArquillianResource private TemporaryFolder tmp + @ArquillianResource + private ClasspathResources classpathResources + private static final String DOCUMENT = ''' = AsciidoctorJRuby contributors githubcontributors::asciidoctor/asciidoctorj[] ''' - @ArquillianResource - private ClasspathResources classpathResources + private static final String DOCUMENT_WITH_NEGATIVE_WIDTHS = ''' += AsciidoctorJRuby contributors + +githubcontributors::asciidoctor/asciidoctorj[widths="2,3,-1"] +''' + def setup() { TestHttpServer.start(['http://api.github.com/repos/asciidoctor/asciidoctorj/contributors' : classpathResources.getResource('githubcontributors.json')]) @@ -60,18 +84,61 @@ githubcontributors::asciidoctor/asciidoctorj[] asciidoctor.convert(DOCUMENT, OptionsBuilder.options().safe(SafeMode.SAFE).inPlace(false).baseDir(tmp.getRoot()).toDir(tmp.getRoot()).toFile(resultFile)) then: - Document htmlDocument = Jsoup.parse(resultFile, 'UTF-8') + Document htmlDocument = Jsoup.parse(resultFile, UTF_8.name()) + + Elements cols = htmlDocument.select(COL) + cols.size() == THREE + cols.get(0).attr(STYLE).contains(WIDTH_20) || cols.get(0).attr(WIDTH).equals(WIDTH_20) + cols.get(1).attr(STYLE).contains(WIDTH_40) || cols.get(1).attr(WIDTH).equals(WIDTH_40) + cols.get(2).attr(STYLE).contains(WIDTH_40) || cols.get(2).attr(WIDTH).equals(WIDTH_40) + + htmlDocument.select(CSS_QUERY_TABLE).hasClass(CLASS_GRID_ROWS) + + htmlDocument.select(FIRST_TD).every { tdElement -> tdElement.select(IMG_ELEMENT).size() == 1 } + htmlDocument.select(FIRST_TD).every { tdElement -> tdElement.select(IMG_ELEMENT)[0].attr(ATTR_SRC) =~ AVATAR_URL_REGEXP } + + htmlDocument.select(SECOND_TD).size() == htmlDocument.select(SECOND_TD) != 0 + htmlDocument.select(SECOND_TD).size() != 0 + + htmlDocument.select(SECOND_TD).every { tdElement -> tdElement.hasClass(CLASS_HALIGN_LEFT) } + htmlDocument.select(THIRD_TD).every { tdElement -> tdElement.hasClass(CLASS_HALIGN_CENTER) } + + htmlDocument.select(SECOND_TD).find { tdElement -> tdElement.text() == CONTRIBUTOR } != null + } + + def "the table should be rendered to html with negative widths"() { + given: + asciidoctor.javaExtensionRegistry().blockMacro(BLOCKMACRO_NAME, GithubContributorsBlockMacro) + File resultFile = tmp.newFile('resultWithNegativeWidth.html') + + when: + def options = Options.builder() + .safe(SafeMode.SAFE) + .inPlace(false) + .baseDir(tmp.getRoot()) + .toFile(resultFile) + .build() + asciidoctor.convert(DOCUMENT_WITH_NEGATIVE_WIDTHS, options) + + then: + Document htmlDocument = Jsoup.parse(resultFile, UTF_8.name()) + + Elements cols = htmlDocument.select(COL) + cols.size() == THREE + cols.get(0).attr(STYLE).contains(WIDTH_2) || cols.get(0).attr(WIDTH).equals(WIDTH_2) + cols.get(1).attr(STYLE).contains(WIDTH_3) || cols.get(1).attr(WIDTH).equals(WIDTH_3) + cols.get(2).attr(STYLE).length() == 0 && cols.get(2).attr(WIDTH).length() == 0 - htmlDocument.select('table').hasClass('grid-rows') + htmlDocument.select(CSS_QUERY_TABLE).hasClass(CLASS_GRID_ROWS) htmlDocument.select(FIRST_TD).every { tdElement -> tdElement.select(IMG_ELEMENT).size() == 1 } - htmlDocument.select(FIRST_TD).every { tdElement -> tdElement.select(IMG_ELEMENT)[0].attr('src') =~ AVATAR_URL_REGEXP } + htmlDocument.select(FIRST_TD).every { tdElement -> tdElement.select(IMG_ELEMENT)[0].attr(ATTR_SRC) =~ AVATAR_URL_REGEXP } htmlDocument.select(SECOND_TD).size() == htmlDocument.select(SECOND_TD) != 0 htmlDocument.select(SECOND_TD).size() != 0 - htmlDocument.select(SECOND_TD).every { tdElement -> tdElement.hasClass('halign-left')} - htmlDocument.select(THIRD_TD).every { tdElement -> tdElement.hasClass('halign-center')} + htmlDocument.select(SECOND_TD).every { tdElement -> tdElement.hasClass(CLASS_HALIGN_LEFT) } + htmlDocument.select(THIRD_TD).every { tdElement -> tdElement.hasClass(CLASS_HALIGN_CENTER) } htmlDocument.select(SECOND_TD).find { tdElement -> tdElement.text() == CONTRIBUTOR } != null } diff --git a/config/codenarc/codenarc.groovy b/config/codenarc/codenarc.groovy index 7c5a5b5fc..6f7d308c6 100644 --- a/config/codenarc/codenarc.groovy +++ b/config/codenarc/codenarc.groovy @@ -43,7 +43,9 @@ ruleset { ruleset('rulesets/braces.xml') { exclude 'IfStatementBraces' } - ruleset('rulesets/size.xml') + ruleset('rulesets/size.xml') { + exclude 'AbcMetric' + } ruleset('rulesets/junit.xml') { // Does not play well with Spock tests exclude 'JUnitPublicNonTestMethod'