pick static method
Shows an interactive menu to select a switch case from the given command. Returns the selected switch name or null if cancelled.
Implementation
static String? pick(Command command, String commandPath) {
if (command.switches.isEmpty) {
return null;
}
final switchNames = command.switches.keys.toList();
int selectedIndex = 0; // Currently selected option (0-based)
// Calculate max switch name length for alignment
int maxSwitchNameLength = 0;
for (final switchName in switchNames) {
if (switchName.length > maxSwitchNameLength) {
maxSwitchNameLength = switchName.length;
}
}
// Helper function to display the menu
void displayMenu() {
print('');
print('Select an option for ${colors.blue}$commandPath${colors.reset}:');
print('');
for (var i = 0; i < switchNames.length; i++) {
final switchName = switchNames[i];
final switchInfo = command.getSwitchInfo(switchName);
final number = i + 1;
final isSelected = i == selectedIndex;
final checkMark = isSelected ? ' ✓' : ' '; // Always 2 chars width
// Pad switch name to align everything
final paddedSwitchName = switchName.padRight(maxSwitchNameLength);
// Build display string
String display = isSelected
? ' ${colors.green}$number. $paddedSwitchName$checkMark${colors.reset}'
: ' $number. $paddedSwitchName$checkMark';
// Add flags if available
if (switchInfo != null && switchInfo.flags != null && switchInfo.flags!.isNotEmpty) {
final flagsStr = switchInfo.aliases.map((a) => '-$a').join(', ');
display += ' ${colors.gray}($flagsStr)${colors.reset}';
}
// Add description if available
if (switchInfo != null && switchInfo.description != null) {
display += ' ${colors.gray}- ${switchInfo.description}${colors.reset}';
}
print(display);
}
print('');
print('${colors.gray}Press number (1-${switchNames.length}) or press Esc to cancel:${colors.reset}');
}
// Display initial menu
displayMenu();
// Enable raw mode to detect arrow keys and ESC
stdin.echoMode = false;
stdin.lineMode = false;
while (true) {
final charCode = stdin.readByteSync();
// ESC key (27) - check if it's part of arrow key sequence
if (charCode == 27) {
// Sleep briefly to see if more bytes are coming (for arrow keys)
sleep(Duration(milliseconds: 10));
// Check if there's more input (arrow keys send ESC[A/B/C/D)
if (stdin.hasTerminal) {
try {
// Try to read next byte without blocking
final bytesAvailable = stdin.readByteSync();
if (bytesAvailable == 91) {
// '[' character - this is an arrow key sequence
final arrowChar = stdin.readByteSync();
// Arrow Up (65 = 'A')
if (arrowChar == 65) {
selectedIndex = (selectedIndex - 1 + switchNames.length) % switchNames.length;
// Clear screen and redraw
_clearScreen(switchNames.length + 5);
displayMenu();
continue;
}
// Arrow Down (66 = 'B')
if (arrowChar == 66) {
selectedIndex = (selectedIndex + 1) % switchNames.length;
// Clear screen and redraw
_clearScreen(switchNames.length + 5);
displayMenu();
continue;
}
}
} catch (e) {
// No more bytes available - it's just ESC key
}
}
// Just ESC key - cancel
stdin.echoMode = true;
stdin.lineMode = true;
print('\n${colors.yellow}Cancelled${colors.reset}');
return null;
}
// Enter key (10 or 13) - execute selected option
if (charCode == 10 || charCode == 13) {
stdin.echoMode = true;
stdin.lineMode = true;
print('');
return switchNames[selectedIndex];
}
// Number keys (49-57 correspond to '1'-'9')
if (charCode >= 49 && charCode <= 57) {
final number = charCode - 48; // Convert to 1-9
if (number >= 1 && number <= switchNames.length) {
stdin.echoMode = true;
stdin.lineMode = true;
print('');
return switchNames[number - 1];
}
}
}
}