Published
Edited
Aug 22, 2018
4 stars
Insert cell
Insert cell
// NOTE: pipes are placed after each tab like '\t|' only to make each tab easier to see in input and output
example = expandAndColorElasticTabs(
`
#include <stdio.h>

int someDemoCode(\t|int fred,
\t|int wilma)
{
\t|x(); \t|/* try making \t|*/
\t|print("hello again!"); \t|/* this comment \t|*/
\t|makeThisFunctionNameShorter(); \t|/* a bit longer \t|*/
\t|for (i = start; i < end; ++i)
\t|{
\t| \t|if (isPrime(i))
\t| \t|{
\t| \t| \t|++numPrimes;
\t| \t|}
\t|}
\t|return numPrimes;
}

`,
{ blockPad: 0, minBlockWidth: 4 } // configure spacing
)
Insert cell
Insert cell
// Computes the size of all elastic tabs in the given text.
function computeElasticTabs(text) {
// We ignore the last cell of each line
// since the standard says we only count cells _behind_ a tab character.
const table = text.split("\n").map(line => line.split("\t").slice(0, -1));

// result objects
const tableUnpruned = text.split("\n").map(line => line.split("\t"));
const cellBlocks = {}; // map a cell coordinate `${row},${col}` to a block index
const blockWidths = []; // map a block index to a width

// cells by coordinate
const numRows = table.length;
const numCols = Math.max(...table.map(cells => cells.length));
const getCell = (r, c) => ({ r, c, text: table[r][c] });

// for each column, we group cells into blocks
for (const c of range(numCols)) {
// Get every cell in this column.
const column = range(numRows)
.map(r => getCell(r, c))
.filter(({ text }) => text != null);

// Group contiguous cells into blocks.
const blocks = splitArray(column, (curr, prev) => curr.r !== prev.r + 1);

// process each block
for (const cells of blocks) {
// compute block width
const w = Math.max(...cells.map(({ text }) => text.length));
// create a new index to identify this block
const blockI = blockWidths.length;
// associate each of our cell coordinates to this block
for (const { r } of cells) {
cellBlocks[`${r},${c}`] = blockI;
}
// store the width of the block
blockWidths.push(w);
}
}

return { table: tableUnpruned, cellBlocks, blockWidths };
}
Insert cell
// View the given text with the elastic tabs expanded into spaces.
function expandElasticTabs(text, { blockPad = 1, minBlockWidth = 3 } = {}) {
const { table, cellBlocks, blockWidths } = computeElasticTabs(text);
const expandCell = (r, c) => {
const text = table[r][c];
const i = cellBlocks[`${r},${c}`];
const w = Math.max(minBlockWidth, blockWidths[i]);
return text.padEnd(w);
};
const numRows = table.length;
const numCols = r => table[r].length;
const lines = range(numRows).map(r => {
const cells = range(numCols(r)).map(c => expandCell(r, c));
const pad = " ".repeat(blockPad);
return cells.join(pad);
});
return lines.join("\n");
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more