@hhoangphuoc/escape-room-cli
Version:
A CLI for playing AI-generated escape room games. Install globally with: npm install -g @hhoangphuoc/escape-room-cli
131 lines (130 loc) • 4.65 kB
JavaScript
// Tab completion utilities for CLI commands and object names
import { COMMANDS } from '../types/constants.js';
// Find common prefix of multiple strings
export function findCommonPrefix(strings) {
if (strings.length === 0)
return '';
if (strings.length === 1)
return strings[0] || '';
let prefix = strings[0] || '';
for (let i = 1; i < strings.length; i++) {
const current = strings[i];
if (!current || !prefix)
break;
let j = 0;
while (j < prefix.length && j < current.length && prefix[j] === current[j]) {
j++;
}
prefix = prefix.substring(0, j);
if (prefix === '')
break;
}
return prefix || '';
}
// Get command completions for '/' prefixed input
export function getCommandCompletions(input) {
const availableCommands = Object.keys(COMMANDS);
const inputLower = input.toLowerCase();
const suggestions = availableCommands.filter(cmd => cmd.startsWith(inputLower));
const commonPrefix = findCommonPrefix(suggestions);
return {
suggestions,
commonPrefix,
hasUniqueMatch: suggestions.length === 1
};
}
// Get object name completions for inspect/guess commands
export function getObjectCompletions(input, objects) {
const inputLower = input.toLowerCase();
const suggestions = objects.filter(obj => obj.toLowerCase().startsWith(inputLower));
const commonPrefix = findCommonPrefix(suggestions);
return {
suggestions,
commonPrefix,
hasUniqueMatch: suggestions.length === 1
};
}
// Parse command and get appropriate completions
export function getCompletions(input, context) {
const trimmed = input.trim();
// If input starts with '/', complete commands
if (trimmed.startsWith('/')) {
const parts = trimmed.split(/\s+/);
const command = parts[0] || '';
// If we're still typing the command itself
if (parts.length === 1) {
return getCommandCompletions(command);
}
// If we have a complete command, check if it needs object completion
if (command === '/inspect' || command === '/guess') {
const objectInput = parts[1] || '';
return getObjectCompletions(objectInput, context.currentRoomObjects);
}
}
// No completions available
return {
suggestions: [],
commonPrefix: '',
hasUniqueMatch: false
};
}
// Apply tab completion to current input
export function applyTabCompletion(input, context) {
const trimmed = input.trim();
const completion = getCompletions(trimmed, context);
if (completion.suggestions.length === 0) {
return input; // No completions available
}
const parts = trimmed.split(/\s+/);
if (trimmed.startsWith('/')) {
if (parts.length === 1) {
// Completing command
if (completion.hasUniqueMatch) {
return completion.suggestions[0] + ' ';
}
else if (completion.commonPrefix.length > trimmed.length) {
return completion.commonPrefix;
}
}
else if (parts.length >= 2 && (parts[0] === '/inspect' || parts[0] === '/guess')) {
// Completing object name
if (completion.hasUniqueMatch) {
const newParts = [...parts];
newParts[1] = completion.suggestions[0] || '';
return newParts.join(' ') + (parts[0] === '/guess' ? ' ' : '');
}
else if (completion.commonPrefix.length > (parts[1] || '').length) {
const newParts = [...parts];
newParts[1] = completion.commonPrefix;
return newParts.join(' ');
}
}
}
return input;
}
// Get suggestion display info for UI
export function getCompletionDisplayInfo(input, context) {
const completion = getCompletions(input.trim(), context);
if (completion.suggestions.length === 0) {
return null;
}
const parts = input.trim().split(/\s+/);
let title = '';
let currentMatch = '';
if (input.trim().startsWith('/')) {
if (parts.length === 1) {
title = 'Available Commands';
currentMatch = parts[0] || '';
}
else if (parts.length >= 2 && (parts[0] === '/inspect' || parts[0] === '/guess')) {
title = 'Available Objects';
currentMatch = parts[1] || '';
}
}
return {
title,
suggestions: completion.suggestions,
currentMatch,
hasUniqueMatch: completion.hasUniqueMatch
};
}