Turned fixed-size lists of debugger/console commands and variables into more flexible data structures

svn-id: r40458
This commit is contained in:
Max Horn 2009-05-11 18:02:27 +00:00
parent e3e3fbd7d1
commit 95a4b26efe
4 changed files with 80 additions and 83 deletions

View file

@ -327,14 +327,13 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
str[i] = buffer(_promptStartPos + i); str[i] = buffer(_promptStartPos + i);
str[len] = '\0'; str[len] = '\0';
char *completion = 0; Common::String completion;
if ((*_completionCallbackProc)(this, str, completion, _callbackRefCon)) { if ((*_completionCallbackProc)(this, str, completion, _callbackRefCon)) {
if (_caretVisible) if (_caretVisible)
drawCaret(true); drawCaret(true);
insertIntoPrompt(completion); insertIntoPrompt(completion.c_str());
scrollToCurrent(); scrollToCurrent();
drawLine(pos2line(_currentPos)); drawLine(pos2line(_currentPos));
delete[] completion;
} }
delete[] str; delete[] str;
} }

View file

@ -46,7 +46,7 @@ class ScrollBarWidget;
implement this simple rewrapping -- we currently don't do that at all!). implement this simple rewrapping -- we currently don't do that at all!).
Or, one can go and implement a more complete console, by replacing the Or, one can go and implement a more complete console, by replacing the
_buffer by a real line buffer -- an arrach of char* pointers. _buffer by a real line buffer -- an array of char* pointers.
This will allow one to implement resizing perfectly, but has the drawback This will allow one to implement resizing perfectly, but has the drawback
of making things like scrolling, drawing etc. more complicated. of making things like scrolling, drawing etc. more complicated.
@ -67,7 +67,7 @@ class ScrollBarWidget;
class ConsoleDialog : public Dialog { class ConsoleDialog : public Dialog {
public: public:
typedef bool (*InputCallbackProc)(ConsoleDialog *console, const char *input, void *refCon); typedef bool (*InputCallbackProc)(ConsoleDialog *console, const char *input, void *refCon);
typedef bool (*CompletionCallbackProc)(ConsoleDialog* console, const char *input, char*& completion, void *refCon); typedef bool (*CompletionCallbackProc)(ConsoleDialog* console, const char *input, Common::String &completion, void *refCon);
protected: protected:
enum { enum {

View file

@ -35,8 +35,6 @@ namespace GUI {
Debugger::Debugger() { Debugger::Debugger() {
_frame_countdown = 0; _frame_countdown = 0;
_dvar_count = 0;
_dcmd_count = 0;
_detach_now = false; _detach_now = false;
_isAttached = false; _isAttached = false;
_errStr = NULL; _errStr = NULL;
@ -57,10 +55,6 @@ Debugger::Debugger() {
} }
Debugger::~Debugger() { Debugger::~Debugger() {
for (int i = 0; i < _dcmd_count; i++) {
delete _dcmds[i].debuglet;
_dcmds[i].debuglet = 0;
}
delete _debuggerDialog; delete _debuggerDialog;
} }
@ -167,21 +161,19 @@ void Debugger::enter() {
} }
bool Debugger::handleCommand(int argc, const char **argv, bool &result) { bool Debugger::handleCommand(int argc, const char **argv, bool &result) {
for (int i = 0; i < _dcmd_count; ++i) { if (_cmds.contains(argv[0])) {
if (!strcmp(_dcmds[i].name, argv[0])) { Debuglet *debuglet = _cmds[argv[0]].get();
Debuglet *debuglet = _dcmds[i].debuglet;
assert(debuglet); assert(debuglet);
result = (*debuglet)(argc, argv); result = (*debuglet)(argc, argv);
return true; return true;
} }
}
return false; return false;
} }
// Command execution loop // Command execution loop
bool Debugger::parseCommand(const char *inputOrig) { bool Debugger::parseCommand(const char *inputOrig) {
int i = 0, num_params = 0; int num_params = 0;
const char *param[256]; const char *param[256];
char *input = strdup(inputOrig); // One of the rare occasions using strdup is OK (although avoiding strtok might be more elegant here). char *input = strdup(inputOrig); // One of the rare occasions using strdup is OK (although avoiding strtok might be more elegant here).
@ -203,8 +195,8 @@ bool Debugger::parseCommand(const char *inputOrig) {
} }
// It's not a command, so things get a little tricky for variables. Do fuzzy matching to ignore things like subscripts. // It's not a command, so things get a little tricky for variables. Do fuzzy matching to ignore things like subscripts.
for (i = 0; i < _dvar_count; i++) { for (uint i = 0; i < _dvars.size(); i++) {
if (!strncmp(_dvars[i].name, param[0], strlen(_dvars[i].name))) { if (!strncmp(_dvars[i].name.c_str(), param[0], _dvars[i].name.size())) {
if (num_params > 1) { if (num_params > 1) {
// Alright, we need to check the TYPE of the variable to deref and stuff... the array stuff is a bit ugly :) // Alright, we need to check the TYPE of the variable to deref and stuff... the array stuff is a bit ugly :)
switch (_dvars[i].type) { switch (_dvars[i].type) {
@ -235,7 +227,7 @@ bool Debugger::parseCommand(const char *inputOrig) {
} }
break; break;
default: default:
DebugPrintf("Failed to set variable %s to %s - unknown type\n", _dvars[i].name, param[1]); DebugPrintf("Failed to set variable %s to %s - unknown type\n", _dvars[i].name.c_str(), param[1]);
break; break;
} }
} else { } else {
@ -286,7 +278,7 @@ bool Debugger::parseCommand(const char *inputOrig) {
// returns true if something has been completed // returns true if something has been completed
// completion has to be delete[]-ed then // completion has to be delete[]-ed then
bool Debugger::tabComplete(const char *input, char*& completion) { bool Debugger::tabComplete(const char *input, Common::String &completion) const {
// very basic tab completion // very basic tab completion
// for now it just supports command completions // for now it just supports command completions
@ -297,63 +289,61 @@ bool Debugger::tabComplete(const char *input, char*& completion) {
if (strchr(input, ' ')) if (strchr(input, ' '))
return false; // already finished the first word return false; // already finished the first word
unsigned int inputlen = strlen(input); const uint inputlen = strlen(input);
unsigned int matchlen = 0; completion.clear();
char match[30]; // the max. command name is 30 chars
for (int i = 0; i < _dcmd_count; i++) { CommandsMap::const_iterator i, e = _cmds.end();
if (!strncmp(_dcmds[i].name, input, inputlen)) { for (i = _cmds.begin(); i != e; ++i) {
unsigned int commandlen = strlen(_dcmds[i].name); if (i->_key.hasPrefix(input)) {
if (commandlen == inputlen) { // perfect match uint commandlen = i->_key.size();
if (commandlen == inputlen) { // perfect match, so no tab completion possible
return false; return false;
} }
if (commandlen > inputlen) { // possible match if (commandlen > inputlen) { // possible match
// no previous match // no previous match
if (matchlen == 0) { if (completion.empty()) {
strcpy(match, _dcmds[i].name + inputlen); completion = i->_key.c_str() + inputlen;
matchlen = commandlen - inputlen;
} else { } else {
// take common prefix of previous match and this command // take common prefix of previous match and this command
unsigned int j; for (uint j = 0; j < completion.size(); j++) {
for (j = 0; j < matchlen; j++) { if (completion[j] != i->_key[inputlen + j]) {
if (match[j] != _dcmds[i].name[inputlen + j]) break; completion = Common::String(completion.begin(), completion.begin() + j);
} // If there is no unambiguous completion, abort
matchlen = j; if (completion.empty())
}
if (matchlen == 0)
return false; return false;
break;
} }
} }
} }
if (matchlen == 0) }
}
}
if (completion.empty())
return false; return false;
completion = new char[matchlen + 1];
memcpy(completion, match, matchlen);
completion[matchlen] = 0;
return true; return true;
} }
// Variable registration function // Variable registration function
void Debugger::DVar_Register(const char *varname, void *pointer, int type, int optional) { void Debugger::DVar_Register(const Common::String &varname, void *pointer, int type, int optional) {
assert(_dvar_count < ARRAYSIZE(_dvars)); // TODO: Filter out duplicates
strcpy(_dvars[_dvar_count].name, varname); // TODO: Sort this list? Then we can do binary search later on when doing lookups.
_dvars[_dvar_count].type = type; assert(pointer);
_dvars[_dvar_count].variable = pointer;
_dvars[_dvar_count].optional = optional;
_dvar_count++; DVar tmp;
tmp.name = varname;
tmp.type = type;
tmp.variable = pointer;
tmp.optional = optional;
_dvars.push_back(tmp);
} }
// Command registration function // Command registration function
void Debugger::DCmd_Register(const char *cmdname, Debuglet *debuglet) { void Debugger::DCmd_Register(const Common::String &cmdname, Debuglet *debuglet) {
assert(debuglet->isValid()); assert(debuglet && debuglet->isValid());
assert(_dcmd_count < ARRAYSIZE(_dcmds)); _cmds[cmdname] = Common::SharedPtr<Debuglet>(debuglet);
strcpy(_dcmds[_dcmd_count].name, cmdname);
_dcmds[_dcmd_count].debuglet = debuglet;
_dcmd_count++;
} }
@ -368,12 +358,23 @@ bool Debugger::Cmd_Exit(int argc, const char **argv) {
bool Debugger::Cmd_Help(int argc, const char **argv) { bool Debugger::Cmd_Help(int argc, const char **argv) {
const int charsPerLine = _debuggerDialog->getCharsPerLine(); const int charsPerLine = _debuggerDialog->getCharsPerLine();
int width, size, i; int width, size;
uint i;
DebugPrintf("Commands are:\n"); DebugPrintf("Commands are:\n");
// Obtain a list of sorted command names
Common::StringList cmds;
CommandsMap::const_iterator iter, e = _cmds.end();
for (iter = _cmds.begin(); iter != e; ++iter) {
cmds.push_back(iter->_key);
}
sort(cmds.begin(), cmds.end());
// Print them all
width = 0; width = 0;
for (i = 0; i < _dcmd_count; i++) { for (i = 0; i < cmds.size(); i++) {
size = strlen(_dcmds[i].name) + 1; size = cmds[i].size() + 1;
if ((width + size) >= charsPerLine) { if ((width + size) >= charsPerLine) {
DebugPrintf("\n"); DebugPrintf("\n");
@ -381,16 +382,16 @@ bool Debugger::Cmd_Help(int argc, const char **argv) {
} else } else
width += size; width += size;
DebugPrintf("%s ", _dcmds[i].name); DebugPrintf("%s ", cmds[i].c_str());
} }
DebugPrintf("\n"); DebugPrintf("\n");
if (_dvar_count > 0) { if (!_dvars.empty()) {
DebugPrintf("\n"); DebugPrintf("\n");
DebugPrintf("Variables are:\n"); DebugPrintf("Variables are:\n");
width = 0; width = 0;
for (i = 0; i < _dvar_count; i++) { for (i = 0; i < _dvars.size(); i++) {
size = strlen(_dvars[i].name) + 1; size = _dvars[i].name.size() + 1;
if ((width + size) >= charsPerLine) { if ((width + size) >= charsPerLine) {
DebugPrintf("\n"); DebugPrintf("\n");
@ -398,7 +399,7 @@ bool Debugger::Cmd_Help(int argc, const char **argv) {
} else } else
width += size; width += size;
DebugPrintf("%s ", _dvars[i].name); DebugPrintf("%s ", _dvars[i].name.c_str());
} }
DebugPrintf("\n"); DebugPrintf("\n");
} }
@ -459,7 +460,7 @@ bool Debugger::debuggerInputCallback(GUI::ConsoleDialog *console, const char *in
} }
bool Debugger::debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, char*& completion, void *refCon) { bool Debugger::debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, Common::String &completion, void *refCon) {
Debugger *debugger = (Debugger *)refCon; Debugger *debugger = (Debugger *)refCon;
return debugger->tabComplete(input, completion); return debugger->tabComplete(input, completion);

View file

@ -26,6 +26,9 @@
#define GUI_DEBUGGER_H #define GUI_DEBUGGER_H
#include "common/func.h" #include "common/func.h"
#include "common/ptr.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
namespace GUI { namespace GUI {
@ -51,7 +54,7 @@ public:
protected: protected:
typedef Common::Functor2<int, const char **, bool> Debuglet; typedef Common::Functor2<int, const char **, bool> Debuglet;
// Convenicence macro for registering a method of a debugger class // Convenience macro for registering a method of a debugger class
// as the current command. // as the current command.
#define WRAP_METHOD(cls, method) \ #define WRAP_METHOD(cls, method) \
new Common::Functor2Mem<int, const char **, bool, cls>(this, &cls::method) new Common::Functor2Mem<int, const char **, bool, cls>(this, &cls::method)
@ -65,26 +68,20 @@ protected:
}; };
struct DVar { struct DVar {
char name[30]; Common::String name;
void *variable; void *variable;
int type, optional; int type;
}; int optional;
struct DCmd {
char name[30];
Debuglet *debuglet;
}; };
int _frame_countdown; int _frame_countdown;
bool _detach_now; bool _detach_now;
private: private:
// TODO: Consider replacing the following two arrays by a Hashmap Common::Array<DVar> _dvars;
int _dvar_count;
DVar _dvars[256];
int _dcmd_count; typedef Common::HashMap<Common::String, Common::SharedPtr<Debuglet>, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> CommandsMap;
DCmd _dcmds[256]; CommandsMap _cmds;
bool _isAttached; bool _isAttached;
char *_errStr; char *_errStr;
@ -109,11 +106,11 @@ private:
void enter(); void enter();
bool parseCommand(const char *input); bool parseCommand(const char *input);
bool tabComplete(const char *input, char*& completion); bool tabComplete(const char *input, Common::String &completion) const;
protected: protected:
void DVar_Register(const char *varname, void *pointer, int type, int optional); void DVar_Register(const Common::String &varname, void *pointer, int type, int optional);
void DCmd_Register(const char *cmdname, Debuglet *debuglet); void DCmd_Register(const Common::String &cmdname, Debuglet *debuglet);
bool Cmd_Exit(int argc, const char **argv); bool Cmd_Exit(int argc, const char **argv);
bool Cmd_Help(int argc, const char **argv); bool Cmd_Help(int argc, const char **argv);
@ -124,7 +121,7 @@ protected:
#if USE_CONSOLE #if USE_CONSOLE
private: private:
static bool debuggerInputCallback(GUI::ConsoleDialog *console, const char *input, void *refCon); static bool debuggerInputCallback(GUI::ConsoleDialog *console, const char *input, void *refCon);
static bool debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, char*& completion, void *refCon); static bool debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, Common::String &completion, void *refCon);
#endif #endif
}; };