* Sort the registered commands by name when entering KDL.

* Don't execute a command anymore, if the given prefix is ambiguous.
* Added tab completion for commands.
* Added on-the-fly help while typing a command line. It is triggered
  by pressing tab at a position after the space following the command.
  It is implemented by calling the command with argument "--help", which
  doesn't work yet with most commands, but some already print their
  usage in this case.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23521 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-01-14 15:39:44 +00:00
parent 2d81f04529
commit 07b8a5de22
1 changed files with 251 additions and 34 deletions

View File

@ -36,7 +36,11 @@
#include <syslog.h>
static const char* const kKDLPrompt = "kdebug> ";
extern "C" int kgets(char *buffer, int length);
static int invoke_command(struct debugger_command *command, int argc,
char** argv);
typedef struct debugger_command {
struct debugger_command *next;
@ -93,11 +97,27 @@ static char *sArguments[MAX_ARGS] = { NULL, };
#define distance(a, b) ((a) < (b) ? (b) - (a) : (a) - (b))
static debugger_command*
next_command(debugger_command* command, const char* prefix, int prefixLen)
{
if (command == NULL)
command = sCommands;
else
command = command->next;
while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0)
command = command->next;
return command;
}
static debugger_command *
find_command(char *name, bool partialMatch)
find_command(const char *name, bool partialMatch, bool& ambiguous)
{
debugger_command *command;
int length;
ambiguous = false;
// search command by full name
@ -109,13 +129,13 @@ find_command(char *name, bool partialMatch)
// if it couldn't be found, search for a partial match
if (partialMatch) {
length = strlen(name);
if (length == 0)
return NULL;
for (command = sCommands; command != NULL; command = command->next) {
if (strncmp(name, command->name, length) == 0)
int length = strlen(name);
command = next_command(NULL, name, length);
if (command != NULL) {
if (next_command(command, name, length) == NULL)
return command;
ambiguous = true;
}
}
@ -143,27 +163,36 @@ kputs(const char *s)
}
static void
insert_chars_into_line(char* buffer, int32& position, int32& length,
const char* chars, int32 charCount)
{
// move the following chars to make room for the ones to insert
if (position < length) {
memmove(buffer + position + charCount, buffer + position,
length - position);
}
// insert chars
memcpy(buffer + position, chars, charCount);
int32 oldPosition = position;
position += charCount;
length += charCount;
// print the new chars (and the following ones)
kprintf("%.*s", (int)(length - oldPosition),
buffer + oldPosition);
// reposition cursor, if necessary
if (position < length)
kprintf("\x1b[%ldD", length - position);
}
static void
insert_char_into_line(char* buffer, int32& position, int32& length, char c)
{
// make room for the new char in the buffer by moving the subsequent ones
if (position < length)
memmove(buffer + position + 1, buffer + position, length - position);
buffer[position++] = c;
length++;
// print the char
kputchar(c);
// print the rest of the line again, if necessary
if (position < length) {
for (int32 i = position; i < length; i++)
kputchar(buffer[i]);
// reposition the cursor
kprintf("\x1b[%ldD", length - position);
}
insert_chars_into_line(buffer, position, length, &c, 1);
}
@ -192,8 +221,156 @@ remove_char_from_line(char* buffer, int32& position, int32& length)
}
class LineEditingHelper {
public:
virtual ~LineEditingHelper() {}
virtual void TabCompletion(char* buffer, int32 capacity, int32& position,
int32& length) = 0;
};
class CommandLineEditingHelper : public LineEditingHelper {
public:
CommandLineEditingHelper()
{
}
virtual ~CommandLineEditingHelper() {}
virtual void TabCompletion(char* buffer, int32 capacity, int32& position,
int32& length)
{
// find the first space
char tmpChar = buffer[position];
buffer[position] = '\0';
char* firstSpace = strchr(buffer, ' ');
buffer[position] = tmpChar;
bool reprintLine = false;
if (firstSpace != NULL) {
// a complete command -- print its help
// get the command
tmpChar = *firstSpace;
*firstSpace = '\0';
bool ambiguous;
debugger_command* command = find_command(buffer, true, ambiguous);
*firstSpace = tmpChar;
if (command != NULL) {
kputchar('\n');
char* args[3] = { NULL, "--help", NULL };
invoke_command(command, 2, args);
} else {
if (ambiguous)
kprintf("\nambiguous command\n");
else
kprintf("\nno such command\n");
}
reprintLine = true;
} else {
// a partial command -- look for completions
// check for possible completions
int32 count = 0;
int32 longestName = 0;
debugger_command* command = NULL;
int32 longestCommonPrefix = 0;
const char* previousCommandName = NULL;
while ((command = next_command(command, buffer, position))
!= NULL) {
count++;
int32 nameLength = strlen(command->name);
longestName = max_c(longestName, nameLength);
// updated the length of the longest common prefix of the
// commands
if (count == 1) {
longestCommonPrefix = longestName;
} else {
longestCommonPrefix = min_c(longestCommonPrefix,
nameLength);
for (int32 i = position; i < longestCommonPrefix; i++) {
if (previousCommandName[i] != command->name[i]) {
longestCommonPrefix = i;
break;
}
}
}
previousCommandName = command->name;
}
if (count == 0) {
// no possible completions
kprintf("\nno completions\n");
reprintLine = true;
} else if (count == 1) {
// exactly one completion
command = next_command(NULL, buffer, position);
// check for sufficient space in the buffer
int32 neededSpace = longestName - position + 1;
// remainder of the name plus one space
// also consider the terminating null char
if (length + neededSpace + 1 >= capacity)
return;
insert_chars_into_line(buffer, position, length,
command->name + position, longestName - position);
insert_char_into_line(buffer, position, length, ' ');
} else if (longestCommonPrefix > position) {
// multiple possible completions with longer common prefix
// -- insert the remainder of the common prefix
// check for sufficient space in the buffer
int32 neededSpace = longestCommonPrefix - position;
// also consider the terminating null char
if (length + neededSpace + 1 >= capacity)
return;
insert_chars_into_line(buffer, position, length,
previousCommandName + position, neededSpace);
} else {
// multiple possible completions without longer common prefix
// -- print them all
kprintf("\n");
reprintLine = true;
int columns = 80 / (longestName + 2);
debugger_command* command = NULL;
int column = 0;
while ((command = next_command(command, buffer, position))
!= NULL) {
// spacing
if (column > 0 && column % columns == 0)
kputchar('\n');
column++;
kprintf(" %-*s", (int)longestName, command->name);
}
kputchar('\n');
}
}
// reprint the editing line, if necessary
if (reprintLine) {
kprintf("%s%.*s", kKDLPrompt, (int)length, buffer);
if (position < length)
kprintf("\x1b[%ldD", length - position);
}
}
};
static int
read_line(char *buffer, int32 maxLength)
read_line(char *buffer, int32 maxLength,
LineEditingHelper* editingHelper = NULL)
{
int32 currentHistoryLine = sCurrentLine;
int32 position = 0;
@ -217,6 +394,14 @@ read_line(char *buffer, int32 maxLength)
kputchar('\n');
done = true;
break;
case '\t':
{
if (editingHelper != NULL) {
editingHelper->TabCompletion(buffer, maxLength,
position, length);
}
break;
}
case 8: // backspace
if (position > 0) {
kputs("\x1b[1D"); // move to the left one
@ -471,8 +656,9 @@ kernel_debugger_loop(void)
struct debugger_command *cmd = NULL;
int argc;
kprintf("kdebug> ");
read_line(sLineBuffer[sCurrentLine], LINE_BUFFER_SIZE);
CommandLineEditingHelper editingHelper;
kprintf(kKDLPrompt);
read_line(sLineBuffer[sCurrentLine], LINE_BUFFER_SIZE, &editingHelper);
parse_line(sLineBuffer[sCurrentLine], sArguments, &argc, MAX_ARGS);
// We support calling last executed command again if
@ -482,12 +668,20 @@ kernel_debugger_loop(void)
sDebuggerOnCPU = smp_get_current_cpu();
bool ambiguous;
if (argc > 0)
cmd = find_command(sArguments[0], true);
cmd = find_command(sArguments[0], true, ambiguous);
if (cmd == NULL)
kprintf("unknown command, enter \"help\" to get a list of all supported commands\n");
else {
if (cmd == NULL) {
if (ambiguous) {
kprintf("Ambiguous command. Use tab completion to get a list "
"of matching commands. Enter \"help\" to get a list of "
"all supported commands.\n");
} else {
kprintf("Unknown command. Enter \"help\" to get a list of all "
"supported commands.\n");
}
} else {
int rc = invoke_command(cmd, argc, sArguments);
if (rc == B_KDEBUG_QUIT)
@ -528,9 +722,10 @@ cmd_help(int argc, char **argv)
debugger_command *command, *specified = NULL;
const char *start = NULL;
int32 startLength = 0;
bool ambiguous;
if (argc > 1) {
specified = find_command(argv[1], false);
specified = find_command(argv[1], false, ambiguous);
if (specified == NULL) {
start = argv[1];
startLength = strlen(start);
@ -1012,6 +1207,28 @@ kernel_debugger(const char *message)
sCurrentKernelDebuggerMessage = message;
// bubble sort the commands
debugger_command* stopCommand = NULL;
while (stopCommand != sCommands) {
debugger_command** command = &sCommands;
while (true) {
debugger_command* nextCommand = (*command)->next;
if (nextCommand == stopCommand) {
stopCommand = *command;
break;
}
if (strcmp((*command)->name, nextCommand->name) > 0) {
debugger_command* tmpCommand = nextCommand->next;
(*command)->next = nextCommand->next;
nextCommand->next = *command;
*command = nextCommand;
}
command = &(*command)->next;
}
}
kernel_debugger_loop();
set_dprintf_enabled(dprintfState);