diff --git a/install.sh b/install.sh index f801988b..1968fe95 100755 --- a/install.sh +++ b/install.sh @@ -133,15 +133,15 @@ if [[ ! -d ~/.tmux-power ]]; then git clone git@github.com:wfxr/tmux-power.git ~/.tmux-power fi -if [[ ! -f $(which tblf) ]]; then - echo_cyan "Installing tblf..." - file=$(mktemp -d) - git clone https://github.com/chenasraf/tblf --depth=1 $file/tblf - cd $file/tblf - make build && make install - cd $cwd - rm -rf $file -fi +# if [[ ! -f $(which tblf) ]]; then +# echo_cyan "Installing tblf..." +# file=$(mktemp -d) +# git clone https://github.com/chenasraf/tblf --depth=1 $file/tblf +# cd $file/tblf +# make build && make install +# cd $cwd +# rm -rf $file +# fi # .config echo_cyan "Copying $DOTFILES/.config to $HOME/.config..." diff --git a/utils/package.json b/utils/package.json index 6816ef66..a3dbe5de 100644 --- a/utils/package.json +++ b/utils/package.json @@ -5,10 +5,12 @@ "description": "", "main": "index.js", "bin": { - "tx": "tmux.js" + "tx": "tmux.js", + "tblf": "tblf.js" }, "scripts": { - "tmux": "ts-node src/tmux.ts", + "tx": "ts-node src/tmux.ts", + "tblf": "ts-node src/tblf.ts", "build": "tsc && cp package.json build/", "ginst": "pnpm build && pnpm i -g utils@file:$(pwd)/build", "dev": "tsc -w" diff --git a/utils/src/common.ts b/utils/src/common.ts new file mode 100644 index 00000000..24623915 --- /dev/null +++ b/utils/src/common.ts @@ -0,0 +1,51 @@ +import { spawn } from 'node:child_process' + +export function log({ verbose, dry }: Opts, ...content: any[]) { + if (!verbose && !dry) return + console.log(...content) +} + +export type Opts = { + key: string + verbose: boolean + dry: boolean +} + +export async function runCommand(opts: Opts, command: string) { + const [cmd, ...args] = command.split(' ') + log(opts, '$ ' + command) + if (opts.dry) return 0 + const proc = spawn(cmd, args, { stdio: 'inherit' }) + return new Promise((resolve, reject) => { + proc.on('close', (code) => { + if (code === 0) { + resolve(code) + } else { + reject(code) + } + }) + }) +} + +export async function getCommandOutput( + opts: Opts, + command: string, +): Promise<{ code: number; output: string }> { + const [cmd, ...args] = command.split(' ') + log(opts, '$ ' + command) + if (opts.dry) return { code: 0, output: '' } + const proc = spawn(cmd, args, { stdio: 'pipe' }) + return new Promise<{ code: number; output: string }>((resolve, reject) => { + let output = '' + proc.stdout.on('data', (data) => { + output += data.toString() + }) + proc.on('close', (code) => { + if (code === 0) { + resolve({ code, output }) + } else { + reject(code) + } + }) + }) +} diff --git a/utils/src/tblf.ts b/utils/src/tblf.ts new file mode 100644 index 00000000..8d9a534e --- /dev/null +++ b/utils/src/tblf.ts @@ -0,0 +1,97 @@ +import { massarg } from 'massarg' +import { Opts as _Opts, log } from './common' + +type Opts = _Opts & { + input?: string + header?: string + separator?: string + outputSeparator?: string +} + +async function main(opts: Opts) { + let input = opts.input || '' + if (!opts.input) { + // read from stdin + log(opts, 'Reading from stdin...') + input = await new Promise((resolve) => { + let data = '' + process.stdin.on('data', (chunk) => { + data += chunk + }) + process.stdin.on('end', () => { + resolve(data) + }) + }) + } else { + input = input.replaceAll('\\n', '\n') + } + + if (!input) { + throw new Error('No input provided.') + } + + log(opts, 'Input:', input + '\n') + + const lines = input.split('\n') + if (opts.header) { + lines.unshift(opts.header) + } + const separator = opts.separator ? new RegExp(opts.separator) : /[\s\t]+/ + const outputSeparator = opts.outputSeparator || ' ' + const rows = lines.map((line) => line.split(separator)) + const columnMaxes = new Array(rows[0].length).fill(0) + + for (const row of rows) { + for (let i = 0; i < row.length; i++) { + columnMaxes[i] = Math.max(columnMaxes[i], row[i].length) + } + } + + let output = '' + for (const row of rows) { + for (let i = 0; i < row.length; i++) { + const column = row[i] + output += column.padEnd(columnMaxes[i]) + outputSeparator + } + output += '\n' + } + + console.log(output.trimEnd()) +} + +massarg({ + name: 'tblf', + description: 'Generate a table from a file or stdin.', +}) + .main(main) + .option({ + name: 'input', + description: 'The input file to read from.', + aliases: ['i'], + isDefault: true, + }) + .option({ + name: 'header', + description: 'The header to prepend to the input, which will be aligned.', + aliases: ['th'], + }) + .option({ + name: 'separator', + description: 'The separator to use when reading from stdin.', + aliases: ['s'], + }) + .option({ + name: 'output-separator', + description: 'The separator to use when writing to stdout.', + aliases: ['o'], + }) + .flag({ + name: 'verbose', + aliases: ['v'], + description: 'Print verbose output.', + }) + .help({ + bindOption: true, + bindCommand: true, + }) + .parse()