diff --git a/doc-build/md060.md b/doc-build/md060.md index cf6912c9..34a92708 100644 --- a/doc-build/md060.md +++ b/doc-build/md060.md @@ -1,9 +1,9 @@ -This rule is triggered when the column separators of a +This rule is triggered when the column separator pipe characters (`|`) of a [GitHub Flavored Markdown table][gfm-table-060] are used inconsistently. This rule recognizes three table column styles based on popular use. -Style `aligned` ensures table delimiters are vertically aligned: +Style `aligned` ensures pipe characters are vertically aligned: ```markdown | Character | Meaning | @@ -46,7 +46,40 @@ see if it satisfies any supported style. If so, no violations are reported. If not, violations are be reported for whichever style would produce the *fewest* issues (i.e., whichever style is the closest match). -Note: Delimiter placement for the `aligned` style is based on visual appearance +Setting the `aligned_delimiter` parameter to `true` requires pipe characters in +the delimiter row to align with those in the header row. This can be used with +`compact` and `tight` tables to make the header text more obvious. (It's already +required for tables with style `aligned`.) + +Style `compact` with `aligned_delimiter`: + +```markdown +| Character | Meaning | +| --------- | ------- | +| Y | Yes | +| N | No | +``` + +Style `tight` with `aligned_delimiter`: + +```markdown +|Character|Meaning| +|---------|-------| +|Y|Yes| +|N|No| +``` + +**Note**: This rule does not require leading/trailing pipe characters, so this +is also a valid table for style `compact`: + +```markdown +Character | Meaning +--- | --- +Y | Yes +N | No +``` + +**Note**: Pipe alignment for the `aligned` style is based on visual appearance and not character count. Because editors typically render [emoji][emoji] and [CJK characters][cjk-characters] at *twice* the width of [Latin characters][latin-script], this rule takes that into account for tables diff --git a/doc/Rules.md b/doc/Rules.md index 042b7dbe..43f7f972 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -2694,15 +2694,16 @@ Aliases: `table-column-style` Parameters: +- `aligned_delimiter`: Aligned delimiter columns (`boolean`, default `false`) - `style`: Table column style (`string`, default `any`, values `aligned` / `any` / `compact` / `tight`) -This rule is triggered when the column separators of a +This rule is triggered when the column separator pipe characters (`|`) of a [GitHub Flavored Markdown table][gfm-table-060] are used inconsistently. This rule recognizes three table column styles based on popular use. -Style `aligned` ensures table delimiters are vertically aligned: +Style `aligned` ensures pipe characters are vertically aligned: ```markdown | Character | Meaning | @@ -2745,7 +2746,40 @@ see if it satisfies any supported style. If so, no violations are reported. If not, violations are be reported for whichever style would produce the *fewest* issues (i.e., whichever style is the closest match). -Note: Delimiter placement for the `aligned` style is based on visual appearance +Setting the `aligned_delimiter` parameter to `true` requires pipe characters in +the delimiter row to align with those in the header row. This can be used with +`compact` and `tight` tables to make the header text more obvious. (It's already +required for tables with style `aligned`.) + +Style `compact` with `aligned_delimiter`: + +```markdown +| Character | Meaning | +| --------- | ------- | +| Y | Yes | +| N | No | +``` + +Style `tight` with `aligned_delimiter`: + +```markdown +|Character|Meaning| +|---------|-------| +|Y|Yes| +|N|No| +``` + +**Note**: This rule does not require leading/trailing pipe characters, so this +is also a valid table for style `compact`: + +```markdown +Character | Meaning +--- | --- +Y | Yes +N | No +``` + +**Note**: Pipe alignment for the `aligned` style is based on visual appearance and not character count. Because editors typically render [emoji][emoji] and [CJK characters][cjk-characters] at *twice* the width of [Latin characters][latin-script], this rule takes that into account for tables diff --git a/doc/md060.md b/doc/md060.md index 9041f600..222af213 100644 --- a/doc/md060.md +++ b/doc/md060.md @@ -6,15 +6,16 @@ Aliases: `table-column-style` Parameters: +- `aligned_delimiter`: Aligned delimiter columns (`boolean`, default `false`) - `style`: Table column style (`string`, default `any`, values `aligned` / `any` / `compact` / `tight`) -This rule is triggered when the column separators of a +This rule is triggered when the column separator pipe characters (`|`) of a [GitHub Flavored Markdown table][gfm-table-060] are used inconsistently. This rule recognizes three table column styles based on popular use. -Style `aligned` ensures table delimiters are vertically aligned: +Style `aligned` ensures pipe characters are vertically aligned: ```markdown | Character | Meaning | @@ -57,7 +58,40 @@ see if it satisfies any supported style. If so, no violations are reported. If not, violations are be reported for whichever style would produce the *fewest* issues (i.e., whichever style is the closest match). -Note: Delimiter placement for the `aligned` style is based on visual appearance +Setting the `aligned_delimiter` parameter to `true` requires pipe characters in +the delimiter row to align with those in the header row. This can be used with +`compact` and `tight` tables to make the header text more obvious. (It's already +required for tables with style `aligned`.) + +Style `compact` with `aligned_delimiter`: + +```markdown +| Character | Meaning | +| --------- | ------- | +| Y | Yes | +| N | No | +``` + +Style `tight` with `aligned_delimiter`: + +```markdown +|Character|Meaning| +|---------|-------| +|Y|Yes| +|N|No| +``` + +**Note**: This rule does not require leading/trailing pipe characters, so this +is also a valid table for style `compact`: + +```markdown +Character | Meaning +--- | --- +Y | Yes +N | No +``` + +**Note**: Pipe alignment for the `aligned` style is based on visual appearance and not character count. Because editors typically render [emoji][emoji] and [CJK characters][cjk-characters] at *twice* the width of [Latin characters][latin-script], this rule takes that into account for tables diff --git a/lib/configuration-strict.d.ts b/lib/configuration-strict.d.ts index 563e22ed..cf893f4a 100644 --- a/lib/configuration-strict.d.ts +++ b/lib/configuration-strict.d.ts @@ -2281,6 +2281,10 @@ export interface ConfigurationStrict { * Table column style */ style?: "any" | "aligned" | "compact" | "tight"; + /** + * Aligned delimiter columns + */ + aligned_delimiter?: boolean; }; /** * MD060/table-column-style : Table column style : https://github.com/DavidAnson/markdownlint/blob/v0.39.0/doc/md060.md @@ -2301,6 +2305,10 @@ export interface ConfigurationStrict { * Table column style */ style?: "any" | "aligned" | "compact" | "tight"; + /** + * Aligned delimiter columns + */ + aligned_delimiter?: boolean; }; /** * headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043 diff --git a/lib/md060.mjs b/lib/md060.mjs index 3675b847..6c32c397 100644 --- a/lib/md060.mjs +++ b/lib/md060.mjs @@ -8,6 +8,22 @@ import stringWidth from "string-width"; /** @typedef {import("markdownlint").MicromarkToken} MicromarkToken */ /** @typedef {import("markdownlint").RuleOnErrorInfo} RuleOnErrorInfo */ +/** + * Adds a RuleOnErrorInfo object to a list of RuleOnErrorInfo objects. + * + * @param {RuleOnErrorInfo[]} errors List of errors. + * @param {number} lineNumber Line number. + * @param {number} column Column number. + * @param {string} detail Detail message. + */ +function addError(errors, lineNumber, column, detail) { + errors.push({ + lineNumber, + detail, + "range": [ column, 1 ] + }); +} + /** * @typedef Column * @property {number} actual Actual column (1-based). @@ -34,19 +50,28 @@ function getTableDividerColumns(lines, row) { } /** - * Adds a RuleOnErrorInfo object to a list of RuleOnErrorInfo objects. + * Checks the specified table rows for consistency with the "aligned" style. * - * @param {RuleOnErrorInfo[]} errors List of errors. - * @param {number} lineNumber Line number. - * @param {number} column Column number. + * @param {readonly string[]} lines File/string lines. + * @param {MicromarkToken[]} rows Micromark row tokens. * @param {string} detail Detail message. + * @returns {RuleOnErrorInfo[]} List of errors. */ -function addError(errors, lineNumber, column, detail) { - errors.push({ - lineNumber, - detail, - "range": [ column, 1 ] - }); +function checkStyleAligned(lines, rows, detail) { + /** @type {RuleOnErrorInfo[]} */ + const errorInfos = []; + const headingRow = rows[0]; + const headingDividerColumns = getTableDividerColumns(lines, headingRow); + for (const row of rows.slice(1)) { + const remainingHeadingDividerColumns = new Set(headingDividerColumns.map((column) => column.effective)); + const rowDividerColumns = getTableDividerColumns(lines, row); + for (const dividerColumn of rowDividerColumns) { + if ((remainingHeadingDividerColumns.size > 0) && !remainingHeadingDividerColumns.delete(dividerColumn.effective)) { + addError(errorInfos, row.startLine, dividerColumn.actual, detail); + } + } + } + return errorInfos; } /** @type {import("markdownlint").Rule} */ @@ -60,27 +85,19 @@ export default { const styleAlignedAllowed = (style === "any") || (style === "aligned"); const styleCompactAllowed = (style === "any") || (style === "compact"); const styleTightAllowed = (style === "any") || (style === "tight"); + const alignedDelimiter = !!params.config.aligned_delimiter; + const lines = params.lines; // Scan all tables/rows const tables = filterByTypesCached([ "table" ]); for (const table of tables) { const rows = filterByTypes(table.children, [ "tableDelimiterRow", "tableRow" ]); - const headingRow = rows[0]; // Determine errors for style "aligned" /** @type {RuleOnErrorInfo[]} */ const errorsIfAligned = []; if (styleAlignedAllowed) { - const headingDividerColumns = getTableDividerColumns(params.lines, headingRow); - for (const row of rows.slice(1)) { - const remainingHeadingDividerColumns = new Set(headingDividerColumns.map((column) => column.effective)); - const rowDividerColumns = getTableDividerColumns(params.lines, row); - for (const dividerColumn of rowDividerColumns) { - if ((remainingHeadingDividerColumns.size > 0) && !remainingHeadingDividerColumns.delete(dividerColumn.effective)) { - addError(errorsIfAligned, row.startLine, dividerColumn.actual, "Table pipe does not align with heading for style \"aligned\""); - } - } - } + errorsIfAligned.push(...checkStyleAligned(lines, rows, "Table pipe does not align with heading for style \"aligned\"")); } // Determine errors for styles "compact" and "tight" @@ -92,6 +109,11 @@ export default { (styleCompactAllowed || styleTightAllowed) && !(styleAlignedAllowed && (errorsIfAligned.length === 0)) ) { + if (alignedDelimiter) { + const errorInfos = checkStyleAligned(lines, rows.slice(0, 2), "Table pipe does not align with heading for option \"aligned_delimiter\""); + errorsIfCompact.push(...errorInfos); + errorsIfTight.push(...errorInfos); + } for (const row of rows) { const tokensOfInterest = filterByTypes(row.children, [ "tableCellDivider", "tableContent", "whitespace" ]); for (let i = 0; i < tokensOfInterest.length; i++) { diff --git a/schema/.markdownlint.jsonc b/schema/.markdownlint.jsonc index 831440ad..5a71809f 100644 --- a/schema/.markdownlint.jsonc +++ b/schema/.markdownlint.jsonc @@ -340,6 +340,8 @@ // MD060/table-column-style : Table column style : https://github.com/DavidAnson/markdownlint/blob/v0.39.0/doc/md060.md "MD060": { // Table column style - "style": "any" + "style": "any", + // Aligned delimiter columns + "aligned_delimiter": false } } \ No newline at end of file diff --git a/schema/.markdownlint.yaml b/schema/.markdownlint.yaml index e769041e..2391e55c 100644 --- a/schema/.markdownlint.yaml +++ b/schema/.markdownlint.yaml @@ -304,3 +304,5 @@ MD059: MD060: # Table column style style: "any" + # Aligned delimiter columns + aligned_delimiter: false diff --git a/schema/build-config-schema.mjs b/schema/build-config-schema.mjs index 3b5a7c2b..0c44c872 100644 --- a/schema/build-config-schema.mjs +++ b/schema/build-config-schema.mjs @@ -645,6 +645,12 @@ for (const rule of rules) { ], "default": "any" }; + // @ts-ignore + subscheme.properties.aligned_delimiter = { + "description": "Aligned delimiter columns", + "type": "boolean", + "default": false + }; break; default: break; diff --git a/schema/markdownlint-config-schema-strict.json b/schema/markdownlint-config-schema-strict.json index f9c0a250..a23211f9 100644 --- a/schema/markdownlint-config-schema-strict.json +++ b/schema/markdownlint-config-schema-strict.json @@ -4701,6 +4701,11 @@ "tight" ], "default": "any" + }, + "aligned_delimiter": { + "description": "Aligned delimiter columns", + "type": "boolean", + "default": false } } } @@ -4747,6 +4752,11 @@ "tight" ], "default": "any" + }, + "aligned_delimiter": { + "description": "Aligned delimiter columns", + "type": "boolean", + "default": false } } } diff --git a/schema/markdownlint-config-schema.json b/schema/markdownlint-config-schema.json index 91b2bc2f..1b505cf0 100644 --- a/schema/markdownlint-config-schema.json +++ b/schema/markdownlint-config-schema.json @@ -4701,6 +4701,11 @@ "tight" ], "default": "any" + }, + "aligned_delimiter": { + "description": "Aligned delimiter columns", + "type": "boolean", + "default": false } } } @@ -4747,6 +4752,11 @@ "tight" ], "default": "any" + }, + "aligned_delimiter": { + "description": "Aligned delimiter columns", + "type": "boolean", + "default": false } } } diff --git a/test/markdownlint-test.mjs b/test/markdownlint-test.mjs index c4c74236..b96d9eee 100644 --- a/test/markdownlint-test.mjs +++ b/test/markdownlint-test.mjs @@ -1101,7 +1101,7 @@ test("readme", async(t) => { }); test("validateJsonUsingConfigSchemaStrict", async(t) => { - t.plan(221); + t.plan(222); // @ts-ignore const ajv = new Ajv(ajvOptions); const validateSchemaStrict = ajv.compile(configSchemaStrict); diff --git a/test/snapshots/markdownlint-test-scenarios.mjs.md b/test/snapshots/markdownlint-test-scenarios.mjs.md index 7749d0cd..75fb2d3f 100644 --- a/test/snapshots/markdownlint-test-scenarios.mjs.md +++ b/test/snapshots/markdownlint-test-scenarios.mjs.md @@ -62068,6 +62068,349 @@ Generated by [AVA](https://avajs.dev). `, } +## table-column-style-aligned-delimiter.md + +> Snapshot 1 + + { + errors: [ + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 5, + 1, + ], + fixInfo: null, + lineNumber: 28, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 9, + 1, + ], + fixInfo: null, + lineNumber: 28, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 13, + 1, + ], + fixInfo: null, + lineNumber: 28, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 22, + 1, + ], + fixInfo: null, + lineNumber: 36, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 4, + 1, + ], + fixInfo: null, + lineNumber: 50, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 20, + 1, + ], + fixInfo: null, + lineNumber: 58, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 3, + 1, + ], + fixInfo: null, + lineNumber: 72, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 5, + 1, + ], + fixInfo: null, + lineNumber: 72, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 7, + 1, + ], + fixInfo: null, + lineNumber: 72, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 18, + 1, + ], + fixInfo: null, + lineNumber: 80, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 2, + 1, + ], + fixInfo: null, + lineNumber: 94, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 4, + 1, + ], + fixInfo: null, + lineNumber: 94, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + { + errorContext: null, + errorDetail: 'Table pipe does not align with heading for option "aligned_delimiter"', + errorRange: [ + 17, + 1, + ], + fixInfo: null, + lineNumber: 102, + ruleDescription: 'Table column style', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md060.md', + ruleNames: [ + 'MD060', + 'table-column-style', + ], + severity: 'error', + }, + ], + fixed: `# Table Column Style - Aligned Delimiter␊ + ␊ + ## Aligned / Edge Pipes␊ + ␊ + | Heading | Heading | Heading |␊ + | ------- | --------- | ------- |␊ + | Text | Text text | Text |␊ + | Text | Text text | Text |␊ + | Text | Text text | Text |␊ + ␊ + ## Aligned / No Edge Pipes␊ + ␊ + Heading | Heading | Heading␊ + ------- | --------- | -------␊ + Text | Text text | Text␊ + Text | Text text | Text␊ + Text | Text text | Text␊ + ␊ + ## Compact / Edge Pipes␊ + ␊ + | Heading | Heading | Heading |␊ + | ------- | ------- | ------- |␊ + | Text | Text text | Text |␊ + | Text text | Text text text | Text |␊ + | Text | Text | Text |␊ + ␊ + | Heading | Heading | Heading |␊ + | - | - | - |␊ + | Text | Text text | Text |␊ + | Text text | Text text text | Text |␊ + | Text | Text | Text |␊ + ␊ + {MD060:-5}␊ + ␊ + | Heading | Heading | Heading |␊ + | ------- | -------- | ------ |␊ + | Text text | Text text text | Text |␊ + ␊ + {MD060:-3}␊ + ␊ + ## Compact / No Edge Pipes␊ + ␊ + Heading | Heading | Heading␊ + ------- | ------- | -------␊ + Text | Text text | Text␊ + Text text | Text text text | Text␊ + Text | Text | Text␊ + ␊ + Heading | Heading | Heading␊ + -- | -- | --␊ + Text | Text text | Text␊ + Text text | Text text text | Text␊ + Text | Text | Text␊ + ␊ + {MD060:-5}␊ + ␊ + Heading | Heading | Heading␊ + ------- | -------- | ------␊ + Text text | Text text text | Text␊ + ␊ + {MD060:-3}␊ + ␊ + ## Tight / Edge Pipes␊ + ␊ + |Heading|Heading|Heading|␊ + |-------|-------|-------|␊ + |Text|Text text|Text|␊ + |Text text|Text text text|Text|␊ + |Text|Text|Text|␊ + ␊ + |Heading|Heading|Heading|␊ + |-|-|-|␊ + |Text|Text text|Text|␊ + |Text text|Text text text|Text|␊ + |Text|Text|Text|␊ + ␊ + {MD060:-5}␊ + ␊ + |Heading|Heading|Heading|␊ + |-------|--------|------|␊ + |Text text|Text text text|Text|␊ + ␊ + {MD060:-3}␊ + ␊ + ## Tight / No Edge Pipes␊ + ␊ + Heading|Heading|Heading␊ + -------|-------|-------␊ + Text|Text text|Text␊ + Text text|Text text text|Text␊ + Text|Text|Text␊ + ␊ + Heading|Heading|Heading␊ + -|-|-␊ + Text|Text text|Text␊ + Text text|Text text text|Text␊ + Text|Text|Text␊ + ␊ + {MD060:-5}␊ + ␊ + Heading|Heading|Heading␊ + -------|--------|------␊ + Text text|Text text text|Text␊ + ␊ + {MD060:-3}␊ + ␊ + ␊ + `, + } + ## table-column-style-aligned.md > Snapshot 1 diff --git a/test/snapshots/markdownlint-test-scenarios.mjs.snap b/test/snapshots/markdownlint-test-scenarios.mjs.snap index 09e4eea6..c71bd371 100644 Binary files a/test/snapshots/markdownlint-test-scenarios.mjs.snap and b/test/snapshots/markdownlint-test-scenarios.mjs.snap differ diff --git a/test/table-column-style-aligned-delimiter.md b/test/table-column-style-aligned-delimiter.md new file mode 100644 index 00000000..dbc37f74 --- /dev/null +++ b/test/table-column-style-aligned-delimiter.md @@ -0,0 +1,112 @@ +# Table Column Style - Aligned Delimiter + +## Aligned / Edge Pipes + +| Heading | Heading | Heading | +| ------- | --------- | ------- | +| Text | Text text | Text | +| Text | Text text | Text | +| Text | Text text | Text | + +## Aligned / No Edge Pipes + +Heading | Heading | Heading +------- | --------- | ------- +Text | Text text | Text +Text | Text text | Text +Text | Text text | Text + +## Compact / Edge Pipes + +| Heading | Heading | Heading | +| ------- | ------- | ------- | +| Text | Text text | Text | +| Text text | Text text text | Text | +| Text | Text | Text | + +| Heading | Heading | Heading | +| - | - | - | +| Text | Text text | Text | +| Text text | Text text text | Text | +| Text | Text | Text | + +{MD060:-5} + +| Heading | Heading | Heading | +| ------- | -------- | ------ | +| Text text | Text text text | Text | + +{MD060:-3} + +## Compact / No Edge Pipes + +Heading | Heading | Heading +------- | ------- | ------- +Text | Text text | Text +Text text | Text text text | Text +Text | Text | Text + +Heading | Heading | Heading +-- | -- | -- +Text | Text text | Text +Text text | Text text text | Text +Text | Text | Text + +{MD060:-5} + +Heading | Heading | Heading +------- | -------- | ------ +Text text | Text text text | Text + +{MD060:-3} + +## Tight / Edge Pipes + +|Heading|Heading|Heading| +|-------|-------|-------| +|Text|Text text|Text| +|Text text|Text text text|Text| +|Text|Text|Text| + +|Heading|Heading|Heading| +|-|-|-| +|Text|Text text|Text| +|Text text|Text text text|Text| +|Text|Text|Text| + +{MD060:-5} + +|Heading|Heading|Heading| +|-------|--------|------| +|Text text|Text text text|Text| + +{MD060:-3} + +## Tight / No Edge Pipes + +Heading|Heading|Heading +-------|-------|------- +Text|Text text|Text +Text text|Text text text|Text +Text|Text|Text + +Heading|Heading|Heading +-|-|- +Text|Text text|Text +Text text|Text text text|Text +Text|Text|Text + +{MD060:-5} + +Heading|Heading|Heading +-------|--------|------ +Text text|Text text text|Text + +{MD060:-3} + +