Skip to content

Commit d9647a1

Browse files
committed
Add table parsing support
1 parent c1e90ad commit d9647a1

1 file changed

Lines changed: 152 additions & 2 deletions

File tree

js/micron-parser.js

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class MicronParser {
1616
this.DEFAULT_FG_DARK = "ddd";
1717
this.DEFAULT_FG_LIGHT = "222";
1818
this.DEFAULT_BG = "default";
19+
this.MAX_TABLE_WIDTH = 100;
1920

2021
if (this.enableForceMonospace) {
2122
this.injectMonospaceStyles();
@@ -142,7 +143,11 @@ class MicronParser {
142143
align: "left",
143144
default_fg: defaultFg,
144145
default_bg: defaultBg,
145-
radio_groups: {}
146+
radio_groups: {},
147+
table_mode: false,
148+
table_buffer: [],
149+
table_align: null,
150+
table_maxwidth: this.MAX_TABLE_WIDTH
146151
};
147152

148153
const lines = markup.split("\n");
@@ -205,7 +210,11 @@ class MicronParser {
205210
align: "left",
206211
default_fg: defaultFg,
207212
default_bg: defaultBg,
208-
radio_groups: {}
213+
radio_groups: {},
214+
table_mode: false,
215+
table_buffer: [],
216+
table_align: null,
217+
table_maxwidth: this.MAX_TABLE_WIDTH
209218
};
210219

211220
// create container div for page-level colors
@@ -245,6 +254,39 @@ class MicronParser {
245254
return [];
246255
}
247256

257+
if (line.startsWith("`t")) {
258+
let rest = line.slice(2);
259+
let align = null;
260+
if (rest.length > 0 && (rest[0] === 'l' || rest[0] === 'c' || rest[0] === 'r')) {
261+
align = rest[0];
262+
rest = rest.slice(1);
263+
}
264+
let maxWidth = null;
265+
if (rest.length > 0) {
266+
const w = parseInt(rest, 10);
267+
if (!isNaN(w)) maxWidth = w;
268+
}
269+
if (state.table_mode) {
270+
const widgets = this.renderTable(state.table_buffer, state);
271+
state.table_mode = false;
272+
state.table_buffer = [];
273+
state.table_align = null;
274+
state.table_maxwidth = this.MAX_TABLE_WIDTH;
275+
return widgets || [];
276+
} else {
277+
state.table_mode = true;
278+
state.table_buffer = [];
279+
state.table_align = align;
280+
state.table_maxwidth = maxWidth;
281+
return [];
282+
}
283+
}
284+
285+
if (state.table_mode) {
286+
state.table_buffer.push(line);
287+
return [];
288+
}
289+
248290
let preEscape = false;
249291

250292
if (!state.literal) {
@@ -1299,6 +1341,114 @@ applyStyleToElement(el, style, defaultBg = "default") {
12991341
return cleanup;
13001342
}
13011343

1344+
renderTable(lines, state) {
1345+
if (lines.length < 2) return null;
1346+
1347+
const headerCells = this._parseTableRow(lines[0]);
1348+
const alignments = this._parseTableAlignments(lines[1]);
1349+
while (alignments.length < headerCells.length) alignments.push('left');
1350+
1351+
const dataRows = [];
1352+
for (let i = 2; i < lines.length; i++) {
1353+
let cells = this._parseTableRow(lines[i]);
1354+
while (cells.length < headerCells.length) cells.push("");
1355+
cells = cells.slice(0, headerCells.length);
1356+
dataRows.push(cells);
1357+
}
1358+
1359+
const borderColor = this.colorToCss(state.fg_color) || "currentColor";
1360+
const cellBorder = "1px solid " + borderColor;
1361+
const cellPadding = "0.2em 0.5em";
1362+
1363+
const table = document.createElement("table");
1364+
table.style.borderCollapse = "collapse";
1365+
table.style.display = "inline-table";
1366+
if (state.table_maxwidth) {
1367+
table.style.maxWidth = (state.table_maxwidth * 0.6) + "em";
1368+
}
1369+
1370+
const thead = document.createElement("thead");
1371+
const headerRow = document.createElement("tr");
1372+
for (let i = 0; i < headerCells.length; i++) {
1373+
const th = document.createElement("th");
1374+
th.style.border = cellBorder;
1375+
th.style.padding = cellPadding;
1376+
th.style.textAlign = alignments[i] || 'left';
1377+
this._renderTableCell(th, headerCells[i], state);
1378+
headerRow.appendChild(th);
1379+
}
1380+
thead.appendChild(headerRow);
1381+
table.appendChild(thead);
1382+
1383+
const tbody = document.createElement("tbody");
1384+
for (const row of dataRows) {
1385+
const tr = document.createElement("tr");
1386+
for (let i = 0; i < row.length; i++) {
1387+
const td = document.createElement("td");
1388+
td.style.border = cellBorder;
1389+
td.style.padding = cellPadding;
1390+
td.style.textAlign = alignments[i] || 'left';
1391+
this._renderTableCell(td, row[i], state);
1392+
tr.appendChild(td);
1393+
}
1394+
tbody.appendChild(tr);
1395+
}
1396+
table.appendChild(tbody);
1397+
1398+
const wrapper = document.createElement("div");
1399+
this.applySectionIndent(wrapper, state);
1400+
if (state.table_align === 'c') wrapper.style.textAlign = "center";
1401+
else if (state.table_align === 'r') wrapper.style.textAlign = "right";
1402+
else if (state.table_align === 'l') wrapper.style.textAlign = "left";
1403+
wrapper.appendChild(table);
1404+
1405+
return [wrapper];
1406+
}
1407+
1408+
_renderTableCell(el, text, state) {
1409+
const snap = {
1410+
fg_color: state.fg_color,
1411+
bg_color: state.bg_color,
1412+
align: state.align,
1413+
formatting: { ...state.formatting }
1414+
};
1415+
const parts = this.makeOutput(state, text);
1416+
if (parts && parts.length > 0) {
1417+
this.appendOutput(el, parts, state);
1418+
}
1419+
state.fg_color = snap.fg_color;
1420+
state.bg_color = snap.bg_color;
1421+
state.align = snap.align;
1422+
state.formatting = snap.formatting;
1423+
}
1424+
1425+
_parseTableRow(line) {
1426+
line = line.trim();
1427+
if (line.startsWith('|')) line = line.slice(1);
1428+
if (line.endsWith('|')) line = line.slice(0, -1);
1429+
const cells = [];
1430+
let current = "";
1431+
let escaped = false;
1432+
for (const ch of line) {
1433+
if (escaped) { current += ch; escaped = false; }
1434+
else if (ch === '\\') escaped = true;
1435+
else if (ch === '|') { cells.push(current.trim()); current = ""; }
1436+
else current += ch;
1437+
}
1438+
cells.push(current.trim());
1439+
return cells;
1440+
}
1441+
1442+
_parseTableAlignments(line) {
1443+
const cells = this._parseTableRow(line);
1444+
return cells.map(c => {
1445+
c = c.trim();
1446+
if (c.startsWith(':') && c.endsWith(':')) return 'center';
1447+
if (c.endsWith(':')) return 'right';
1448+
return 'left';
1449+
});
1450+
}
1451+
13021452
splitAtSpaces(line) {
13031453
let out = "";
13041454
const wordArr = line.split(/(?<= )/g);

0 commit comments

Comments
 (0)