Skip to content

Commit

Permalink
Add wrapOnWordBoundary option (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
speedytwenty committed Mar 31, 2022
1 parent 3950b7f commit 3c893d5
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -19,7 +19,7 @@ unmaintained. `cli-table3` includes all the additional features from
- Ability to make cells span columns and/or rows.
- Ability to set custom styles per cell (border characters/colors, padding, etc).
- Vertical alignment (top, bottom, center).
- Automatic word wrapping.
- [Word wrapping options](./basic-usage.md#set-wordwrap-to-true-to-wrap-text-on-word-boundaries).
- More robust truncation of cell text that contains ansi color characters.
- Better handling of text color that spans multiple lines.
- API compatible with the original cli-table.
Expand Down
41 changes: 32 additions & 9 deletions basic-usage.md
Expand Up @@ -125,21 +125,44 @@
```


##### Set `wordWrap` to true to make lines of text wrap instead of being truncated
┌───────┬─────────┐
│ Hello │ I am │
│ how │ fine │
│ are │ thanks! │
│ you? │
└───────┴─────────┘
##### Set `wordWrap` to true to wrap text on word boundaries
┌───────┬─────────┬───────────────────┬──────────────
│ Hello │ I am │ Words that exceed │ Text is only │
│ how │ fine │ the colWidth will │ wrapped for │
│ are │ thanks! │ be truncated. │ fixed width │
│ you? │ Looooo… │ │ columns.
└───────┴─────────┴───────────────────┴──────────────
```javascript
let table = new Table({
style: { border: [], header: [] },
colWidths: [7, 9],
colWidths: [7, 9], // Requires fixed column widths
wordWrap: true,
});

table.push(['Hello how are you?', 'I am fine thanks!']);
table.push([
'Hello how are you?',
'I am fine thanks! Looooooong',
['Words that exceed', 'the colWidth will', 'be truncated.'].join('\n'),
['Text is only', 'wrapped for', 'fixed width', 'columns.'].join('\n'),
]);

```


##### Using `wordWrap`, set `wrapOnWordBoundary` to false to ignore word boundaries
┌───┬───┐
│ W │ T │
│ r │ e │
│ a │ x │
│ p │ t │
└───┴───┘
```javascript
const table = new Table({
style: { border: [], header: [] },
colWidths: [3, 3], // colWidths must all be greater than 2!!!!
wordWrap: true,
wrapOnWordBoundary: false,
});
table.push(['Wrap', 'Text']);
```

47 changes: 38 additions & 9 deletions examples/basic-usage-examples.js
Expand Up @@ -182,26 +182,55 @@ module.exports = function (runTest) {
return [makeTable, expected, 'multi-line-colors'];
});

it('Set `wordWrap` to true to make lines of text wrap instead of being truncated', function () {
it('Set `wordWrap` to true to wrap text on word boundaries', function () {
function makeTable() {
let table = new Table({
style: { border: [], header: [] },
colWidths: [7, 9],
colWidths: [7, 9], // Requires fixed column widths
wordWrap: true,
});

table.push(['Hello how are you?', 'I am fine thanks!']);
table.push([
'Hello how are you?',
'I am fine thanks! Looooooong',
['Words that exceed', 'the colWidth will', 'be truncated.'].join('\n'),
['Text is only', 'wrapped for', 'fixed width', 'columns.'].join('\n'),
]);

return table;
}

let expected = [
'┌───────┬─────────┐',
'│ Hello │ I am │',
'│ how │ fine │',
'│ are │ thanks! │',
'│ you? │ │',
'└───────┴─────────┘',
'┌───────┬─────────┬───────────────────┬──────────────┐',
'│ Hello │ I am │ Words that exceed │ Text is only │',
'│ how │ fine │ the colWidth will │ wrapped for │',
'│ are │ thanks! │ be truncated. │ fixed width │',
'│ you? │ Looooo… │ │ columns. │',
'└───────┴─────────┴───────────────────┴──────────────┘',
];

return [makeTable, expected];
});

it('Using `wordWrap`, set `wrapOnWordBoundary` to false to ignore word boundaries', function () {
function makeTable() {
const table = new Table({
style: { border: [], header: [] },
colWidths: [3, 3], // colWidths must all be greater than 2!!!!
wordWrap: true,
wrapOnWordBoundary: false,
});
table.push(['Wrap', 'Text']);
return table;
}

let expected = [
'┌───┬───┐',
'│ W │ T │',
'│ r │ e │',
'│ a │ x │',
'│ p │ t │',
'└───┴───┘',
];

return [makeTable, expected];
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Expand Up @@ -27,6 +27,7 @@ declare namespace CliTable3 {
rowAligns: VerticalAlignment[];
head: string[];
wordWrap: boolean;
wrapOnWordBoundary: boolean;
}

interface TableInstanceOptions extends TableOptions {
Expand Down
5 changes: 3 additions & 2 deletions src/cell.js
Expand Up @@ -58,7 +58,7 @@ class Cell {
this.border = style.border || tableStyle.border;

let fixedWidth = tableOptions.colWidths[this.x];
if (tableOptions.wordWrap && fixedWidth) {
if ((tableOptions.wordWrap || tableOptions.textWrap) && fixedWidth) {
fixedWidth -= this.paddingLeft + this.paddingRight;
if (this.colSpan) {
let i = 1;
Expand All @@ -67,7 +67,8 @@ class Cell {
i++;
}
}
this.lines = utils.colorizeLines(utils.wordWrap(fixedWidth, this.content));
const { wrapOnWordBoundary = true } = tableOptions;
this.lines = utils.colorizeLines(utils.wordWrap(fixedWidth, this.content, wrapOnWordBoundary));
} else {
this.lines = utils.colorizeLines(this.content.split('\n'));
}
Expand Down
26 changes: 24 additions & 2 deletions src/utils.js
Expand Up @@ -240,6 +240,7 @@ function mergeOptions(options, defaults) {
return ret;
}

// Wrap on word boundary
function wordWrap(maxLength, input) {
let lines = [];
let split = input.split(/(\s+)/g);
Expand Down Expand Up @@ -270,11 +271,32 @@ function wordWrap(maxLength, input) {
return lines;
}

function multiLineWordWrap(maxLength, input) {
// Wrap text (ignoring word boundaries)
function textWrap(maxLength, input) {
let lines = [];
let line = '';
function pushLine(str, ws) {
if (line.length && ws) line += ws;
line += str;
while (line.length > maxLength) {
lines.push(line.slice(0, maxLength));
line = line.slice(maxLength);
}
}
let split = input.split(/(\s+)/g);
for (let i = 0; i < split.length; i += 2) {
pushLine(split[i], i && split[i - 1]);
}
if (line.length) lines.push(line);
return lines;
}

function multiLineWordWrap(maxLength, input, wrapOnWordBoundary = true) {
let output = [];
input = input.split('\n');
const handler = wrapOnWordBoundary ? wordWrap : textWrap;
for (let i = 0; i < input.length; i++) {
output.push.apply(output, wordWrap(maxLength, input[i]));
output.push.apply(output, handler(maxLength, input[i]));
}
return output;
}
Expand Down
12 changes: 12 additions & 0 deletions test/utils-test.js
Expand Up @@ -322,6 +322,18 @@ describe('utils', function () {
let expected = ['\x1b[31m漢字\x1b[0m', ' 漢字'];
expect(wordWrap(5, input)).toEqual(expected);
});

describe('textWrap', function () {
it('wraps long words', function () {
expect(wordWrap(10, 'abcdefghijklmnopqrstuvwxyz', false)).toEqual(['abcdefghij', 'klmnopqrst', 'uvwxyz']);
expect(wordWrap(10, 'abcdefghijk lmnopqrstuv wxyz', false)).toEqual(['abcdefghij', 'k lmnopqrs', 'tuv wxyz']);
expect(wordWrap(10, 'ab cdefghijk lmnopqrstuv wx yz', false)).toEqual([
'ab cdefghi',
'jk lmnopqr',
'stuv wx yz',
]);
});
});
});

describe('colorizeLines', function () {
Expand Down

0 comments on commit 3c893d5

Please sign in to comment.