Skip to content

Commit

Permalink
fix: add character length dictionary for string length computation in…
Browse files Browse the repository at this point in the history
… console (#431)
  • Loading branch information
mkah91 committed Aug 24, 2022
1 parent bcb6e8c commit 89f6db2
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 30 deletions.
24 changes: 17 additions & 7 deletions src/internalTable/internal-table-printer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Row } from '../models/common';
import { CharLengthDict, Row } from '../models/common';
import { Column, TableStyleDetails } from '../models/internal-table';
import ColoredConsoleLine, { ColorMap } from '../utils/colored-console-line';
import { textWithPadding } from '../utils/string-utils';
Expand Down Expand Up @@ -27,7 +27,8 @@ const renderOneLine = (
widthLimitedColumnsArray: { [key: string]: string[] },
isHeader: boolean | undefined,
row: Row,
colorMap: ColorMap
colorMap: ColorMap,
charLength?: CharLengthDict
): string => {
const line = new ColoredConsoleLine(colorMap);
line.addCharsWithColor('', tableStyle.vertical); // dont Color the Column borders
Expand All @@ -45,7 +46,8 @@ const renderOneLine = (
textWithPadding(
textForThisLine,
column.alignment || DEFAULT_ROW_ALIGNMENT,
column.length || DEFAULT_COLUMN_LEN
column.length || DEFAULT_COLUMN_LEN,
charLength
)
);
line.addCharsWithColor('', ` ${tableStyle.vertical}`); // dont Color the Column borders
Expand All @@ -60,10 +62,15 @@ const renderWidthLimitedLines = (
columns: Column[],
row: Row,
colorMap: ColorMap,
isHeader?: boolean
isHeader?: boolean,
charLength?: CharLengthDict
): string[] => {
// { col1: ['How', 'Is', 'Going'], col2: ['I am', 'Tom'], }
const widthLimitedColumnsArray = getWidthLimitedColumnsArray(columns, row);
const widthLimitedColumnsArray = getWidthLimitedColumnsArray(
columns,
row,
charLength
);

const totalLines = Object.values(widthLimitedColumnsArray).reduce(
(a, b) => Math.max(a, b.length),
Expand All @@ -83,7 +90,8 @@ const renderWidthLimitedLines = (
widthLimitedColumnsArray,
isHeader,
row,
colorMap
colorMap,
charLength
);

ret.push(singleLine);
Expand All @@ -100,7 +108,9 @@ const renderRow = (table: TableInternal, row: Row): string[] => {
table.tableStyle,
table.columns,
row,
table.colorMap
table.colorMap,
undefined,
table.charLength
)
);
return ret;
Expand Down
13 changes: 8 additions & 5 deletions src/internalTable/internal-table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dictionary, Row } from '../models/common';
import { CharLengthDict, Dictionary, Row } from '../models/common';
import {
ComplexOptions,
ComputedColumn,
Expand Down Expand Up @@ -49,6 +49,8 @@ class TableInternal {

colorMap: ColorMap;

charLength: CharLengthDict;

initSimple(columns: string[]) {
this.columns = columns.map((column) => ({
name: column,
Expand All @@ -68,11 +70,11 @@ class TableInternal {
this.columns =
options?.columns?.map(rawColumnToInternalColumn) || this.columns;
this.rowSeparator = options?.rowSeparator || this.rowSeparator;

if(options?.colorMap) {
this.colorMap = { ...this.colorMap, ...options.colorMap };
this.charLength = options?.charLength || this.charLength;

if (options?.colorMap) {
this.colorMap = { ...this.colorMap, ...options.colorMap };
}


if (options.rows !== undefined) {
this.addRows(options.rows);
Expand All @@ -92,6 +94,7 @@ class TableInternal {
this.computedColumns = [];
this.rowSeparator = DEFAULT_ROW_SEPARATOR;
this.colorMap = DEFAULT_COLOR_MAP;
this.charLength = {};

if (options instanceof Array) {
this.initSimple(options);
Expand Down
2 changes: 1 addition & 1 deletion src/internalTable/table-pre-processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const enableColumnsIfNecessary = (table: TableInternal) => {

const findColumnWidth = (table: TableInternal) => {
table.columns.forEach((column) => {
column.length = findLenOfColumn(column, table.rows);
column.length = findLenOfColumn(column, table.rows, table.charLength);
});
};

Expand Down
4 changes: 4 additions & 0 deletions src/models/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export type COLOR = typeof COLORS[number];
export interface Dictionary {
[key: string]: any;
}

export interface CharLengthDict {
[key: string]: number;
}
export interface Row {
color: COLOR;
separator: boolean;
Expand Down
3 changes: 2 additions & 1 deletion src/models/external-table.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ColorMap } from '../utils/colored-console-line';
import { ALIGNMENT, COLOR, Dictionary } from './common';
import { ALIGNMENT, CharLengthDict, COLOR, Dictionary } from './common';
import { TableStyleDetails } from './internal-table';

export { ALIGNMENT, COLOR };
Expand Down Expand Up @@ -33,4 +33,5 @@ export interface ComplexOptions {
computedColumns?: ComputedColumn[];
rowSeparator?: boolean;
colorMap?: ColorMap;
charLength?: CharLengthDict;
}
20 changes: 19 additions & 1 deletion src/utils/console-utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import { wcswidth } from 'simple-wcswidth';
import { CharLengthDict } from '../models/common';

/* eslint-disable no-control-regex */
const colorRegex = /\x1b\[\d{1,3}m/g; // \x1b[30m \x1b[305m

const stripAnsi = (str: string): string => str.replace(colorRegex, '');
const findWidthInConsole = (str: string): number => wcswidth(stripAnsi(str));

export const findWidthInConsole = (
str: string,
charLength?: CharLengthDict
): number => {
let strLen = 0;
str = stripAnsi(str);
if (charLength) {
Object.entries(charLength).forEach(([key, value]) => {
// count appearance of the key in the string and remove from original string
let regex = new RegExp(key, 'g');
strLen += (str.match(regex) || []).length * value;
str = str.replace(key, '');
});
}
strLen += wcswidth(str);
return strLen;
};

export default findWidthInConsole;
24 changes: 17 additions & 7 deletions src/utils/string-utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ALIGNMENT } from '../models/common';
import { ALIGNMENT, CharLengthDict } from '../models/common';
import findWidthInConsole from './console-utils';

// ("How are you?",center, 20) => " How are you? "
// ("How are you?",right, 20) => " How are you?"
export const textWithPadding = (
text: string,
alignment: ALIGNMENT,
columnLen: number
columnLen: number,
charLength?: CharLengthDict
): string => {
const curTextSize = findWidthInConsole(text);
const curTextSize = findWidthInConsole(text, charLength);
// alignments for center padding case
const leftPadding = Math.floor((columnLen - curTextSize) / 2);
const rightPadding = columnLen - leftPadding - curTextSize;
Expand All @@ -34,15 +35,19 @@ export const textWithPadding = (
};

// ("How are you?",10) => ["How are ", "you?"]
export const limitWidth = (inpStr: string, width: number): string[] => {
export const limitWidth = (
inpStr: string,
width: number,
charLength?: CharLengthDict
): string[] => {
const ret: string[] = [];

const spaceSeparatedStrings = inpStr.split(' ');

let now: string[] = [];
let cnt = 0;
spaceSeparatedStrings.forEach((strWithoutSpace) => {
const consoleWidth = findWidthInConsole(strWithoutSpace);
const consoleWidth = findWidthInConsole(strWithoutSpace, charLength);
if (cnt + consoleWidth <= width) {
cnt += consoleWidth + 1; // 1 for the space
now.push(strWithoutSpace);
Expand All @@ -58,5 +63,10 @@ export const limitWidth = (inpStr: string, width: number): string[] => {
};

// ("How are you?",10) => ["How are ", "you?"]
export const biggestWordInSentence = (inpStr: string): number =>
inpStr.split(' ').reduce((a, b) => Math.max(a, findWidthInConsole(b)), 0);
export const biggestWordInSentence = (
inpStr: string,
charLength?: CharLengthDict
): number =>
inpStr
.split(' ')
.reduce((a, b) => Math.max(a, findWidthInConsole(b, charLength)), 0);
28 changes: 20 additions & 8 deletions src/utils/table-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { objIfExists } from '../internalTable/input-converter';
import { COLOR, Dictionary, Row } from '../models/common';
import { CharLengthDict, COLOR, Dictionary, Row } from '../models/common';
import { ComputedColumn } from '../models/external-table';
import { Column } from '../models/internal-table';
import findWidthInConsole from './console-utils';
Expand Down Expand Up @@ -90,7 +90,11 @@ export const createRow = (
text,
});

export const findLenOfColumn = (column: Column, rows: Row[]): number => {
export const findLenOfColumn = (
column: Column,
rows: Row[],
charLength?: CharLengthDict
): number => {
const columnId = column.name;
const columnTitle = column.title;
let length = max(0, column?.minLen || 0);
Expand All @@ -100,20 +104,26 @@ export const findLenOfColumn = (column: Column, rows: Row[]): number => {
// if others cant fit find the max word length so that at least the table can be printed
length = max(
length,
max(column.maxLen, biggestWordInSentence(columnTitle))
max(column.maxLen, biggestWordInSentence(columnTitle, charLength))
);
length = rows.reduce(
(acc, row) =>
max(acc, biggestWordInSentence(cellText(row.text[columnId]))),
max(
acc,
biggestWordInSentence(cellText(row.text[columnId]), charLength)
),
length
);
return length;
}

length = max(length, findWidthInConsole(columnTitle));
length = max(length, findWidthInConsole(columnTitle, charLength));

rows.forEach((row) => {
length = max(length, findWidthInConsole(cellText(row.text[columnId])));
length = max(
length,
findWidthInConsole(cellText(row.text[columnId]), charLength)
);
});

return length;
Expand All @@ -139,14 +149,16 @@ export const createHeaderAsRow = (createRowFn: any, columns: Column[]): Row => {
// { col1: ['How', 'Is', 'Going'], col2: ['I am', 'Tom'], }
export const getWidthLimitedColumnsArray = (
columns: Column[],
row: Row
row: Row,
charLength?: CharLengthDict
): { [key: string]: string[] } => {
const ret: { [key: string]: string[] } = {};

columns.forEach((column) => {
ret[column.name] = limitWidth(
cellText(row.text[column.name]),
column.length || DEFAULT_COLUMN_LEN
column.length || DEFAULT_COLUMN_LEN,
charLength
);
});

Expand Down
8 changes: 8 additions & 0 deletions test/utils/console-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ describe('Console Width Calculation', () => {
expect(findWidthInConsole(line.renderConsole())).toBe(2);
});

it('Character length test: No character substitution', () => {
expect(findWidthInConsole('abc')).toBe(3);
});

it('Character length test: Character length substitution', () => {
expect(findWidthInConsole('abc', { a: 2 })).toBe(4);
});

/* these fail on travis bcs travis has another kind of console
it('Simplest test: chalk', () => {
const testFunction = (Fn: any) => {
Expand Down

0 comments on commit 89f6db2

Please sign in to comment.