diff --git a/cli/prompt_secret.ts b/cli/prompt_secret.ts index f6a93e2cd17b8..f262695affa8a 100644 --- a/cli/prompt_secret.ts +++ b/cli/prompt_secret.ts @@ -4,8 +4,11 @@ const input = Deno.stdin; const output = Deno.stdout; const encoder = new TextEncoder(); const decoder = new TextDecoder(); -const LF = "\n".charCodeAt(0); -const CR = "\r".charCodeAt(0); +const LF = "\n".charCodeAt(0); // ^J - Enter on Linux +const CR = "\r".charCodeAt(0); // ^M - Enter on macOS and Windows (CRLF) +const BS = "\b".charCodeAt(0); // ^H - Backspace on Linux and Windows +const DEL = 0x7f; // ^? - Backspace on macOS +const CLR = encoder.encode("\r\u001b[K"); // Clear the current line /** * Shows the given message and waits for the user's input. Returns the user's input as string. @@ -20,9 +23,10 @@ export function promptSecret( return null; } - const maskChar = encoder.encode(mask); - const callback = mask === "" ? undefined : () => { - output.writeSync(maskChar); + const callback = mask === "" ? undefined : (n: number) => { + // Clear the current line + output.writeSync(CLR); + output.writeSync(encoder.encode(`${message}${mask.repeat(n)}`)); }; output.writeSync(encoder.encode(message)); @@ -30,6 +34,7 @@ export function promptSecret( try { return readLineFromStdinSync(callback); } finally { + output.writeSync(CLR); Deno.stdin.setRaw(false); } } @@ -38,7 +43,7 @@ export function promptSecret( // This implementation immediately break on CR or LF and accept callback. // The original version waits LF when CR is received. // https://github.com/denoland/deno/blob/e4593873a9c791238685dfbb45e64b4485884174/runtime/js/41_prompt.js#L52-L77 -function readLineFromStdinSync(callback?: () => void): string { +function readLineFromStdinSync(callback?: (n: number) => void): string { const c = new Uint8Array(1); const buf = []; @@ -50,8 +55,12 @@ function readLineFromStdinSync(callback?: () => void): string { if (c[0] === CR || c[0] === LF) { break; } - buf.push(c[0]); - if (callback) callback(); + if (c[0] === BS || c[0] === DEL) { + buf.pop(); + } else { + buf.push(c[0]); + } + if (callback) callback(buf.length); } return decoder.decode(new Uint8Array(buf)); }