From 26b7089cc204cb4b3bb0e30656957627b0ebd043 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 30 Apr 2025 03:06:19 -0600 Subject: [PATCH] adds flag coalescing to argparser adds handling of inlined args to call tools fixes terminal layout clearing troubles --- cli/TerminalLayout.ts | 7 +++---- cli/argParser.ts | 23 +++++++++++++++++++++-- cli/index.ts | 34 ++++++++++++++++++++++++---------- cli/prompts.ts | 13 +++++++------ tools/fieldRename.ts | 41 +++-------------------------------------- 5 files changed, 58 insertions(+), 60 deletions(-) diff --git a/cli/TerminalLayout.ts b/cli/TerminalLayout.ts index 59addaf..a4f400c 100644 --- a/cli/TerminalLayout.ts +++ b/cli/TerminalLayout.ts @@ -79,15 +79,15 @@ export class TerminalLayout { } clearAll() { + for (const name of this.layoutOrder) { + this.blocks[name].clear(); + } Deno.stdout.writeSync( new TextEncoder().encode( TerminalLayout.ALT_BUFFER_DISABLE, ), ); Cursor.show(); - for (const name of this.layoutOrder) { - this.blocks[name].clear(); - } } clear() { @@ -223,7 +223,6 @@ export class TerminalBlock { for (let i = 0; i < this.renderedLineCount; i++) { Deno.stdout.writeSync(new TextEncoder().encode(`\x1b[2K\x1b[1E`)); } - this.renderedLineCount = 0; } clearAll() { diff --git a/cli/argParser.ts b/cli/argParser.ts index 842378b..89907b9 100644 --- a/cli/argParser.ts +++ b/cli/argParser.ts @@ -1,5 +1,6 @@ -export class ArgParser { +export class ArgParser> { private args: string[]; + private flags: Map = new Map(); constructor(args: string[]) { this.args = args; @@ -11,7 +12,22 @@ export class ArgParser { return this.args[index + 1]; } - get flags() { + setFlagDefs(flagDefs: T) { + for (const [flag, defs] of Object.entries(flagDefs)) { + for (const def of defs) { + if (this.argFlags.includes(def)) { + this.flags.set(flag, true); + } + } + } + return this; + } + + getFlag(flag: keyof T) { + return this.flags.get(flag); + } + + get argFlags() { return this.args.filter((arg) => arg.startsWith("-")); } @@ -26,6 +42,9 @@ export class ArgParser { get task() { return this.nonFlags[0]; } + get taskArgs() { + return this.nonFlags.slice(1); + } static parse(args: string[]) { return new ArgParser(args); diff --git a/cli/index.ts b/cli/index.ts index 1ce38b4..0e0f955 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -9,8 +9,11 @@ import { cliAlert, cliLog } from "./prompts.ts"; export class PdfToolsCli { private tools: Map = new Map(); private terminalLayout = new TerminalLayout(); + closeMessage?: string; - private args = ArgParser.parse(Deno.args); + private args = ArgParser.parse(Deno.args).setFlagDefs({ + help: ["-h", "--help"], + }); async importTools(tools?: string) { tools = tools?.replace(/\/$/, "").replace(/^\.?\//, "") || "tools"; @@ -51,17 +54,22 @@ export class PdfToolsCli { this.terminalLayout.register("title", titleBlock); const bodyBlock = new TerminalBlock(); this.terminalLayout.register("body", bodyBlock); - this.embiggenHeader(); - if (Deno.args.length === 0) { - // console.log( - // colorize("No tool specified. Importing all tools...", "gray"), - // ); + if (this.args.getFlag("help") && !this.args.task) { + await this.help(); + return; + } else if (this.args.nonFlags.length === 0 || !this.args.task) { + this.embiggenHeader(); await this.importTools(); + await this.toolMenu(); + } else { + await this.importTools(); + const task = this.args.task; + await this.runTool(toCase(task, "title")); } - await this.toolMenu(); } finally { this.terminalLayout.clearAll(); Deno.stdin.setRaw(false); + if (this.closeMessage) console.log(this.closeMessage); } } @@ -95,9 +103,15 @@ export class PdfToolsCli { const bodyBlock = this.terminalLayout.getBlock("body"); bodyBlock.clearAll(); tool.setBlock?.(bodyBlock); - await tool.run(); - await tool.done?.(); - this.embiggenHeader(); + if (this.args.getFlag("help")) { + await tool.help?.(); + } else { + await tool.run(...this.args.taskArgs); + await tool.done?.(); + } + await this.embiggenHeader(); + } else { + this.closeMessage = "No tool found for " + toolName; } } diff --git a/cli/prompts.ts b/cli/prompts.ts index 9e0fe1c..ec8bbf6 100644 --- a/cli/prompts.ts +++ b/cli/prompts.ts @@ -1,4 +1,5 @@ import { Cursor } from "./cursor.ts"; +import { ScrollManager } from "./scrollManager.ts"; import { colorize } from "./style.ts"; import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts"; @@ -11,11 +12,8 @@ export async function cliPrompt( await Deno.stdin.setRaw(true); - let cursorVisible = true; - if (!block) { - cursorVisible = Cursor["visible"]; - Cursor.show(); - } + const cursorVisible = Cursor["visible"]; + Cursor.show(); let range: [number, number] = [0, 1]; if (block) { @@ -54,7 +52,7 @@ export async function cliPrompt( } await Deno.stdin.setRaw(false); - if (!block && !cursorVisible) { + if (!cursorVisible) { Cursor.hide(); } Deno.stdout.writeSync(encoder.encode("\n")); @@ -86,10 +84,12 @@ export function cliLog(message: string, block?: TerminalBlock) { } if (import.meta.main) { + Cursor.hide(); const layout = new TerminalLayout(); const title = new TerminalBlock(); const block = new TerminalBlock(); block.setPreserveHistory(true); + // ScrollManager.enable(block); title.setLines(["Hello, World!"]); title.setFixedHeight(1); @@ -105,6 +105,7 @@ if (import.meta.main) { cliLog(`Hello, ${name}!`, block); const single = await cliConfirm("Are you single?", block); cliLog(single ? "Do you want to go out with me?" : "Okay", block); + // ScrollManager.enable(block); const loopingConvo = [ "No response?", "I guess that's okay", diff --git a/tools/fieldRename.ts b/tools/fieldRename.ts index 0f5898c..f0d027a 100644 --- a/tools/fieldRename.ts +++ b/tools/fieldRename.ts @@ -4,25 +4,9 @@ import { callWithArgPrompt } from "util/call.ts"; import { TerminalBlock } from "../cli/TerminalLayout.ts"; import { forceArgs } from "../cli/forceArgs.ts"; import { colorize } from "../cli/style.ts"; -import { cliLog, cliPrompt } from "../cli/prompts.ts"; +import { cliAlert, cliLog, cliPrompt } from "../cli/prompts.ts"; import { multiSelectMenuInteractive } from "../cli/selectMenu.ts"; -// const thing = PDFAcroField.prototype.getFullyQualifiedName; -// PDFAcroField.prototype.getFullyQualifiedName = function () { -// const name = thing.call(this) -// // if (name?.includes('langauge')) -// console.log(name) -// return name; -// } - -// const thing = PDFHexString.prototype.copyBytesInto -// PDFHexString.prototype.copyBytesInto = function (buffer: Uint8Array, offset: number) { -// console.log((this as any).value) - -// const result = thing.call(this, buffer, offset) -// return result; -// } - async function renameFields( path: string, pattern: string | RegExp, @@ -48,29 +32,10 @@ async function renameFields( if (mName) { changesMade = true; cField.dict.set(PDFName.of("T"), PDFString.of(mName)); - // console.log(cField.getPartialName()) } } cField = cField.getParent(); - // console.log(cField?.getPartialName()) } - console.log(field.getName()); - // const newName = name.replace(pattern, change); - // console.log("Change to: %c" + newName, "color: yellow"); - // if (confirm('Ok?')) { - // let parent = field.acroField.getParent(); - // field.acroField.setPartialName(segments.pop()) - // while (parent && segments.length) { - // console.log(parent.getPartialName()) - // parent.setPartialName(segments.pop()) - // parent = parent.getParent(); - // } - // changesMade = true; - // console.log(field.getName()) - // // dict.set(PDFName.of("T"), PDFHexString.fromText(newName)) - // console.log("%cDone!", "color: lime") - // } - // break; } } if (changesMade) { @@ -123,8 +88,8 @@ class RenameFields implements ITool { } help(standalone = false) { - cliLog( - "Usage: renamefields ", + cliAlert( + "Usage: rename-fields \n", standalone ? undefined : this.block, ); }