@noisneil, thanks for sharing this very useful macro! (Nice example data too. )
No worries. Here's another approach using tabs instead of spaces.
One thing I've noticed with the tab version is that if you paste it into TextEdit, for example, and then select a monospaced font, it's perfect.
However, if you write it to a file as styled text with the same font, the tabs occasionally come out a bit squiffy.
Any ideas why that might be?
A lot of shell utilities are going to expect monospaced fonts.
I'm using Menlo, which is monospaced.
Here's a perl
version, which @griffman pointed out is sliiiightly snappier:
No idea how this Keyboard Maestro Execute JavaScript for Automation action compares in terms of speed.
Monospaced columns (as defined by delimiter) separated by given gap.kmmacros (5.3 KB)
Expand disclosure triangle to view JS source
(() => {
"use strict";
// Columns of Text (as defined by given Delimiter),
// padded to even width, and separated by given Gap.
// Rob Trew @2023
// Ver 0.01
const main = () => {
const
kmVar = kmValue(kmInstance()),
[txt, delim, gap] = ["Text", "Delimiter", "Gap"]
.map(k => kmVar(`local_${k}`)),
rows = lines(txt).map(
row => row.split(delim).map(k => k.trim())
),
n = Math.max(...rows.map(row => row.length)),
fullRows = rows.map(row => {
const m = row.length;
return n === m
? row
: row.concat(
Array.from(
{length: n - m},
() => ""
)
);
});
return transpose(
transpose(fullRows).map(column => {
const
w = Math.max(
...column.map(cell => cell.length)
);
return column.map(
cell => cell.padEnd(w, " ")
);
})
)
.map(row => row.join(gap))
.join("\n");
};
// ---------------- KEYBOARD MAESTRO -----------------
// kmValue :: KM Instance -> String -> IO String
const kmValue = instance =>
k => Application("Keyboard Maestro Engine")
.getvariable(k, {instance});
// kmInstance :: () -> IO String
const kmInstance = () =>
ObjC.unwrap(
$.NSProcessInfo.processInfo.environment
.objectForKey("KMINSTANCE")
) || "";
// --------------------- GENERIC ---------------------
// lines :: String -> [String]
const lines = s =>
// A list of strings derived from a single string
// which is delimited by \n or by \r\n or \r.
0 < s.length
? s.split(/\r\n|\n|\r/u)
: [];
// transpose :: [[a]] -> [[a]]
const transpose = rows =>
// The columns of the input transposed
// into new rows.
// Simpler version of transpose, assuming input
// rows of even length.
0 < rows.length
? rows[0].map(
(_, i) => rows.flatMap(v => v[i])
)
: [];
// MAIN ---
return main();
})();
This version sets each column to an explicit character position. Useful if you want multiple text blocks to be formatted separately yet identically.
Align Text as Spaced Columns (Explicit Positions).kmmacros (22 KB)