* Layout: * ───────────────── (top border) * > input text (input line) * ───────────────── (bottom border) * /cmd1 desc (suggestions — outside input area) * /cmd2 ...
()
| 244 | * /cmd2 ... |
| 245 | */ |
| 246 | private render(): void { |
| 247 | // Compute slash-command suggestions in real time |
| 248 | const hits = this.buffer.startsWith('/') |
| 249 | ? SLASH_KEYS.filter(cmd => cmd.startsWith(this.buffer)) |
| 250 | : []; |
| 251 | const exactMatch = hits.length === 1 && hits[0] === this.buffer; |
| 252 | const showSuggestions = hits.length > 0 && !exactMatch; |
| 253 | const suggestionCount = showSuggestions ? hits.length : 0; |
| 254 | const newTotal = 3 + suggestionCount; |
| 255 | |
| 256 | let out = ''; |
| 257 | |
| 258 | // Move cursor up to the top border (cursor currently sits on input line) |
| 259 | if (this.lastTotal > 0) { |
| 260 | out += cursorUp(1); |
| 261 | } |
| 262 | |
| 263 | out += cursorCol(1) + clearLine() + this.border() + '\n'; |
| 264 | out += clearLine() + '> ' + this.buffer + '\n'; |
| 265 | out += cursorCol(1) + clearLine() + this.border() + '\n'; |
| 266 | |
| 267 | // Suggestions — rendered below the bottom border, outside the input area |
| 268 | if (showSuggestions) { |
| 269 | for (const cmd of hits) { |
| 270 | out += clearLine() + |
| 271 | ` ${this.dim}${cmd.padEnd(CMD_MAX_LEN + 2)} ${SLASH_COMMANDS[cmd]}${this.reset}\n`; |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | if (newTotal < this.lastTotal) { |
| 276 | out += clearBelow(); |
| 277 | } |
| 278 | |
| 279 | // Move cursor back to input line |
| 280 | out += cursorUp(suggestionCount + 2); |
| 281 | out += cursorCol(this.promptLen + this.cursor + 1); |
| 282 | |
| 283 | this.out.write(out); |
| 284 | this.lastTotal = newTotal; |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | // --------------------------------------------------------------------------- |
no test coverage detected