diff --git a/engine/qclib/gui.h b/engine/qclib/gui.h index aa8734b7..2cb31305 100644 --- a/engine/qclib/gui.h +++ b/engine/qclib/gui.h @@ -19,6 +19,7 @@ void GUI_RevealOptions(void); int GUIprintf(const char *msg, ...); pbool GenBuiltinsList(char *buffer, int buffersize); +pbool GenAutoCompleteList(char *prefix, char *buffer, int buffersize); extern char parameters[16384]; diff --git a/engine/qclib/qccguiqt.cpp b/engine/qclib/qccguiqt.cpp index 0ef1f92a..d1cf8116 100644 --- a/engine/qclib/qccguiqt.cpp +++ b/engine/qclib/qccguiqt.cpp @@ -3,8 +3,6 @@ /*Todo (in no particular order): tooltips for inspecting variables/types. variables/watch list. - calltips for argument info - autocompletion calltips, for people who can't remember function names initial open project prompt shpuld's styling decompiler output saving @@ -47,6 +45,9 @@ extern int fl_tabsize; extern char enginebinary[MAX_OSPATH]; extern char enginebasedir[MAX_OSPATH]; extern char enginecommandline[8192]; + +extern QCC_def_t *sourcefilesdefs[]; +extern int sourcefilesnumdefs; }; static char *cmdlineargs; @@ -60,7 +61,7 @@ template inline T cpprealloc(T p, size_t s) {return static_cast(real #define STRINGIFY2(s) #s #define STRINGIFY(s) STRINGIFY2(s) - +static QProcess *qcdebugger; static void DebuggerStop(void); static bool DebuggerSendCommand(const char *msg, ...); static void DebuggerStart(void); @@ -407,9 +408,120 @@ public: } }; +class WrappedQsciScintilla:public QsciScintilla +{ +public: + WrappedQsciScintilla(QWidget *parent):QsciScintilla(parent){} + + char *WordUnderCursor(char *word, int wordsize, char *term, int termsize, int position) + { + unsigned char linebuf[1024]; + long charidx; + long lineidx; + long len; + pbool noafter = (position==-2); + + if (position == -3) + { + len = SendScintilla(QsciScintillaBase::SCI_GETSELTEXT, NULL); + if (len > 0 && len < wordsize) + { + len = SendScintilla(QsciScintillaBase::SCI_GETSELTEXT, word); + if (len>0) + return word; + } + } + + if (position < 0) + position = SendScintilla(QsciScintillaBase::SCI_GETCURRENTPOS); + + lineidx = SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, position); + charidx = position - SendScintilla(QsciScintillaBase::SCI_POSITIONFROMLINE, lineidx); + + len = SendScintilla(QsciScintillaBase::SCI_LINELENGTH, lineidx); + if (len >= sizeof(linebuf)) + { + *word = 0; + return word; + } + len = SendScintilla(QsciScintillaBase::SCI_GETLINE, lineidx, linebuf); + linebuf[len] = 0; + if (charidx >= len) + charidx = len-1; + + if (noafter) //truncate it here if we're not meant to be reading anything after. + linebuf[charidx] = 0; + + if (word) + { + //skip back to the start of the word + while(charidx > 0 && ( + (linebuf[charidx-1] >= 'a' && linebuf[charidx-1] <= 'z') || + (linebuf[charidx-1] >= 'A' && linebuf[charidx-1] <= 'Z') || + (linebuf[charidx-1] >= '0' && linebuf[charidx-1] <= '9') || + linebuf[charidx-1] == '_' || linebuf[charidx-1] == ':' || + linebuf[charidx-1] >= 128 + )) + { + charidx--; + } + //copy the result out + lineidx = 0; + wordsize--; + while (wordsize && ( + (linebuf[charidx] >= 'a' && linebuf[charidx] <= 'z') || + (linebuf[charidx] >= 'A' && linebuf[charidx] <= 'Z') || + (linebuf[charidx] >= '0' && linebuf[charidx] <= '9') || + linebuf[charidx] == '_' || linebuf[charidx] == ':' || + linebuf[charidx] >= 128 + )) + { + word[lineidx++] = linebuf[charidx++]; + wordsize--; + } + word[lineidx++] = 0; + } + + if (term) + { + //skip back to the start of the word + while(charidx > 0 && ( + (linebuf[charidx-1] >= 'a' && linebuf[charidx-1] <= 'z') || + (linebuf[charidx-1] >= 'A' && linebuf[charidx-1] <= 'Z') || + (linebuf[charidx-1] >= '0' && linebuf[charidx-1] <= '9') || + linebuf[charidx-1] == '_' || linebuf[charidx-1] == ':' || linebuf[charidx-1] == '.' || + linebuf[charidx-1] == '[' || linebuf[charidx-1] == ']' || + linebuf[charidx-1] >= 128 + )) + { + charidx--; + } + //copy the result out + lineidx = 0; + termsize--; + while (termsize && ( + (linebuf[charidx] >= 'a' && linebuf[charidx] <= 'z') || + (linebuf[charidx] >= 'A' && linebuf[charidx] <= 'Z') || + (linebuf[charidx] >= '0' && linebuf[charidx] <= '9') || + linebuf[charidx] == '_' || linebuf[charidx] == ':' || linebuf[charidx] == '.' || + linebuf[charidx] == '[' || linebuf[charidx] == ']' || + linebuf[charidx] >= 128 + )) + { + term[lineidx++] = linebuf[charidx++]; + termsize--; + } + term[lineidx++] = 0; + } + return word; + } +protected: + void contextMenuEvent(QContextMenuEvent *event); +}; + class documentlist : public QAbstractListModel { - QsciScintilla *s; //this is the widget that we load our documents into + WrappedQsciScintilla *s; //this is the widget that we load our documents into int numdocuments; struct document_s @@ -480,7 +592,7 @@ class documentlist : public QAbstractListModel } void UpdateTitle(void); public: - documentlist(QsciScintilla *editor) + documentlist(WrappedQsciScintilla *editor) { s = editor; numdocuments = 0; @@ -868,6 +980,18 @@ public: break; } + connect(s, &QsciScintillaBase::SCN_CHARADDED, [=](int charadded) + { + if (charadded == '(') + { + int pos = s->SendScintilla(QsciScintillaBase::SCI_GETCURRENTPOS); + char *tooltext = GetCalltipForLine(pos-1); + //tooltip_editor = NULL; + if (tooltext) + s->SendScintilla(QsciScintillaBase::SCI_CALLTIPSHOW, pos, tooltext); + } + }); + s->setUtf8(ed->savefmt != UTF_ANSI); s->setText(QString(file)); @@ -927,6 +1051,155 @@ public: return ret; } + char *GetCalltipForLine(int cursorpos) //calltips are used for tooltips too, so there's some extra weirdness in here + { + static char buffer[1024]; + char wordbuf[256], *text; + char term[256]; + char *defname; + defname = s->WordUnderCursor(wordbuf, sizeof(wordbuf), term, sizeof(term), cursorpos); + if (!*defname) + return NULL; + else if (globalstable.numbuckets) + { + QCC_def_t *def; + int fno; + int line; + int best, bestline; + char *macro = QCC_PR_CheckCompConstTooltip(defname, buffer, buffer + sizeof(buffer)); + if (macro && *macro) + return macro; + + /*if (dwell) + { + tooltip_editor = NULL; + *tooltip_variable = 0; + tooltip_position = 0; + *tooltip_type = 0; + *tooltip_comment = 0; + }*/ + + line = s->SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, cursorpos); + + for (best = 0,bestline=0, fno = 1; fno < numfunctions; fno++) + { + if (line > functions[fno].line && bestline < functions[fno].line) + { + if (!strcmp(curdoc->fname, functions[fno].filen)) + { + best = fno; + bestline = functions[fno].line; + } + } + } + if (best) + { + if (strstr(functions[best].name, "::")) + { + QCC_type_t *type; + char tmp[256]; + char *c; + QC_strlcpy(tmp, functions[best].name, sizeof(tmp)); + c = strstr(tmp, "::"); + if (c) + *c = 0; + type = QCC_TypeForName(tmp); + + if (type->type == ev_entity) + { + QCC_def_t *def; + QC_snprintfz(tmp, sizeof(tmp), "%s::__m%s", type->name, term); + + for (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope && def->scope != &functions[best]) + continue; + // OutputDebugString(def->name); + // OutputDebugString("\n"); + if (!strcmp(def->name, tmp)) + { + //FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals + break; + } + } + } + + if (def && def->type->type == ev_field) + { + // QC_strlcpy(tmp, term, sizeof(tmp)); + QC_snprintfz(term, sizeof(term), "self.%s", tmp); + } + else + { + for (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope && def->scope != &functions[best]) + continue; + if (!strcmp(def->name, term)) + { + //FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals + break; + } + } + } + if (def && def->type->type == ev_field) + { + QC_strlcpy(tmp, term, sizeof(tmp)); + QC_snprintfz(term, sizeof(term), "self.%s", tmp); + } + } + } + } + } + + //FIXME: we may need to display types too + for (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope) + continue; + if (!strcmp(def->name, defname)) + { + //FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals + break; + } + } + } + + if (def) + { + char typebuf[1024]; + char valuebuf[1024]; + if (def->constant && def->type->type == ev_float) + QC_snprintfz(valuebuf, sizeof(valuebuf), " = %g", def->symboldata[def->ofs]._float); + else if (def->constant && def->type->type == ev_integer) + QC_snprintfz(valuebuf, sizeof(valuebuf), " = %i", def->symboldata[def->ofs]._int); + else if (def->constant && def->type->type == ev_vector) + QC_snprintfz(valuebuf, sizeof(valuebuf), " = '%g %g %g'", def->symboldata[def->ofs].vector[0], def->symboldata[def->ofs].vector[1], def->symboldata[def->ofs].vector[2]); + else + *valuebuf = 0; + //note function argument names do not persist beyond the function def. we might be able to read the function's localdefs for them, but that's unreliable/broken with builtins where they're most needed. + if (def->comment) + QC_snprintfz(buffer, sizeof(buffer)-1, "%s %s%s\r\n%s", TypeName(def->type, typebuf, sizeof(typebuf)), def->name, valuebuf, def->comment); + else + QC_snprintfz(buffer, sizeof(buffer)-1, "%s %s%s", TypeName(def->type, typebuf, sizeof(typebuf)), def->name, valuebuf); + + text = buffer; + } + else + text = NULL; + + return text; + } + else + return NULL;//"Type info not available. Compile first."; + } + bool annotate(const char *line) { auto filename = line+6; @@ -1124,6 +1397,47 @@ public: DebuggerSendCommand("qcbreakpoint %i \"%s\" %i\n", mode, curdoc->fname, curdoc->cursorline); } + /*void setMarkerLine(int linenum) + { //moves the 'current-line' marker to the active document and current line + for (int i = 0; i < numdocuments; i++) + { + docstacklock lock(this, docs[i]); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 1); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 2); + } + + if (linenum <= 0) + return; + s->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum-1, 1); + s->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum-1, 2); + }*/ + void setNextStatement(void) + { + if (!curdoc) + return; + if (!qcdebugger) + return; //can't move it if we're not running. + + DebuggerSendCommand("qcjump \"%s\" %i\n", curdoc->fname, curdoc->cursorline); + } + void autoComplete(void) + { + if (!curdoc) + return; + + char word[256]; + char suggestions[65536]; + s->WordUnderCursor(word, sizeof(word), NULL, 0, -2); + if (*word && GenAutoCompleteList(word, suggestions, sizeof(suggestions))) + { + s->SendScintilla(QsciScintillaBase::SCI_AUTOCSETFILLUPS, ".,[<>(*/+-=\t\n"); + s->SendScintilla(QsciScintillaBase::SCI_AUTOCSHOW, strlen(word), suggestions); + } + + //s->SendScintilla(QsciScintillaBase::SCI_CALLTIPSHOW, (int)0, "hello world"); + + //SCI_CALLTIPSHOW pos, "text" + } void setNext(void) { if (!curdoc) @@ -1304,7 +1618,7 @@ public: static class guimainwindow : public QMainWindow { public: - QsciScintilla s; + WrappedQsciScintilla s; QSplitter leftrightsplit; QSplitter logsplit; QSplitter leftsplit; @@ -1318,215 +1632,231 @@ public: private: void CreateMenus(void) { - auto fileMenu = menuBar()->addMenu(tr("&File")); + { + auto fileMenu = menuBar()->addMenu(tr("&File")); - //editor UI things - auto fileopen = new QAction(tr("Open"), this); - fileMenu->addAction(fileopen); - fileopen->setShortcuts(QKeySequence::listFromString("Ctrl+O")); - connect(fileopen, &QAction::triggered, [=]() - { - GUIprintf("Ctrl+O hit\n"); - }); - auto filesave = new QAction(tr("Save"), this); - fileMenu->addAction(filesave); - filesave->setShortcuts(QKeySequence::listFromString("Ctrl+S")); - connect(filesave, &QAction::triggered, [=]() - { - if (!docs.saveDocument(NULL)) - QMessageBox::critical(nullptr, "Error", QString::asprintf("Unable to save")); - }); - - auto prefs = new QAction(tr("&Preferences"), this); - fileMenu->addAction(prefs); - prefs->setShortcuts(QKeySequence::Preferences); - prefs->setStatusTip(tr("Reconfigure stuff")); - connect(prefs, &QAction::triggered, [](){(new optionswindow())->show();}); - - auto goline = new QAction(tr("Go to line"), this); - fileMenu->addAction(goline); - goline->setShortcuts(QKeySequence::listFromString("Ctrl+G")); - goline->setStatusTip(tr("Jump to line")); - connect(goline, &QAction::triggered, [=]() - { - struct grepargs_s + //editor UI things + auto fileopen = new QAction(tr("Open"), this); + fileMenu->addAction(fileopen); + fileopen->setShortcuts(QKeySequence::listFromString("Ctrl+O")); + connect(fileopen, &QAction::triggered, [=]() { - guimainwindow *mw; - QDialog *d; - QLineEdit *t; - } args = {this, new QDialog()}; - args.t = new QLineEdit(QString("")); - auto l = new QVBoxLayout; - l->addWidget(args.t); - args.d->setLayout(l); - args.d->setWindowTitle("FTEQCC Go To Line"); - args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) - { - ctx.mw->docs.EditFile(NULL, atoi(ctx.t->text().toUtf8().data())); - ctx.d->done(0); - }, args)); - args.d->show(); - }); - - auto godef = new QAction(tr("Go To Definition"), this); - fileMenu->addAction(godef); - godef->setShortcuts(QKeySequence::listFromString("F12")); - connect(godef, &QAction::triggered, [=]() - { - struct grepargs_s + GUIprintf("Ctrl+O hit\n"); + }); + auto filesave = new QAction(tr("Save"), this); + fileMenu->addAction(filesave); + filesave->setShortcuts(QKeySequence::listFromString("Ctrl+S")); + connect(filesave, &QAction::triggered, [=]() { - guimainwindow *mw; - QDialog *d; - QLineEdit *t; - } args = {this, new QDialog()}; - args.t = new QLineEdit(this->s.selectedText()); - auto l = new QVBoxLayout; - l->addWidget(args.t); - args.d->setLayout(l); - args.d->setWindowTitle("Go To Definition"); - args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) - { - GoToDefinition(ctx.t->text().toUtf8().data()); - ctx.d->done(0); - }, args)); - args.d->show(); - }); + if (!docs.saveDocument(NULL)) + QMessageBox::critical(nullptr, "Error", QString::asprintf("Unable to save")); + }); - auto find = new QAction(tr("Find"), this); - fileMenu->addAction(find); - find->setShortcuts(QKeySequence::listFromString("Ctrl+F")); - find->setStatusTip(tr("Search through all project files")); - connect(find, &QAction::triggered, [=]() - { - struct grepargs_s + auto prefs = new QAction(tr("&Preferences"), this); + fileMenu->addAction(prefs); + prefs->setShortcuts(QKeySequence::Preferences); + prefs->setStatusTip(tr("Reconfigure stuff")); + connect(prefs, &QAction::triggered, [](){(new optionswindow())->show();}); + + auto goline = new QAction(tr("Go to line"), this); + fileMenu->addAction(goline); + goline->setShortcuts(QKeySequence::listFromString("Ctrl+G")); + goline->setStatusTip(tr("Jump to line")); + connect(goline, &QAction::triggered, [=]() { - guimainwindow *mw; - QDialog *d; - QLineEdit *t; - } args = {this, new QDialog()}; - args.t = new QLineEdit(this->s.selectedText()); - auto l = new QVBoxLayout; - l->addWidget(args.t); - args.d->setLayout(l); - args.d->setWindowTitle("FTEQCC Find"); - args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + struct grepargs_s { - ctx.mw->s.findFirst(ctx.t->text(), false, false, false, true); - ctx.d->done(0); - }, args)); - args.d->show(); - }); - connect(new QShortcut(QKeySequence(tr("F3", "File|FindNext")), this), &QShortcut::activated, - [=]() - { - s.findNext(); - }); + guimainwindow *mw; + QDialog *d; + QLineEdit *t; + } args = {this, new QDialog()}; + args.t = new QLineEdit(QString("")); + auto l = new QVBoxLayout; + l->addWidget(args.t); + args.d->setLayout(l); + args.d->setWindowTitle("FTEQCC Go To Line"); + args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + { + ctx.mw->docs.EditFile(NULL, atoi(ctx.t->text().toUtf8().data())); + ctx.d->done(0); + }, args)); + args.d->show(); + }); - auto grep = new QAction(tr("Grep"), this); - fileMenu->addAction(grep); - grep->setShortcuts(QKeySequence::listFromString("Ctrl+Shift+G")); - grep->setStatusTip(tr("Search through all project files")); - connect(grep, &QAction::triggered, [=]() - { - struct grepargs_s + auto find = new QAction(tr("Find In File"), this); + fileMenu->addAction(find); + find->setShortcuts(QKeySequence::listFromString("Ctrl+F")); + find->setStatusTip(tr("Search for a token in the current file")); + connect(find, &QAction::triggered, [=]() { - guimainwindow *mw; - QDialog *d; - QLineEdit *t; - } args = {this, new QDialog()}; - args.t = new QLineEdit(this->s.selectedText()); - auto l = new QVBoxLayout; - l->addWidget(args.t); - args.d->setLayout(l); - args.d->setWindowTitle("FTEQCC Grep Project Files"); - args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + struct grepargs_s { - ctx.mw->files.GrepAll(ctx.t->text().toUtf8().data()); - ctx.d->done(0); - }, args)); - args.d->show(); - }); + guimainwindow *mw; + QDialog *d; + QLineEdit *t; + } args = {this, new QDialog()}; + args.t = new QLineEdit(this->s.selectedText()); + auto l = new QVBoxLayout; + l->addWidget(args.t); + args.d->setLayout(l); + args.d->setWindowTitle("Find In File"); + args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + { + ctx.mw->s.findFirst(ctx.t->text(), false, false, false, true); + ctx.d->done(0); + }, args)); + args.d->show(); + }); + connect(new QShortcut(QKeySequence(tr("F3", "File|FindNext")), this), &QShortcut::activated, + [=]() + { + s.findNext(); + }); - //convert to utf-8 chars - //convert to Quake chars + auto grep = new QAction(tr("Find In Project"), this); + fileMenu->addAction(grep); + grep->setShortcuts(QKeySequence::listFromString("Ctrl+Shift+F")); + grep->setStatusTip(tr("Search through all project files")); + connect(grep, &QAction::triggered, [=]() + { + struct grepargs_s + { + guimainwindow *mw; + QDialog *d; + QLineEdit *t; + } args = {this, new QDialog()}; + args.t = new QLineEdit(this->s.selectedText()); + auto l = new QVBoxLayout; + l->addWidget(args.t); + args.d->setLayout(l); + args.d->setWindowTitle("Find In Project"); + args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + { + ctx.mw->files.GrepAll(ctx.t->text().toUtf8().data()); + ctx.d->done(0); + }, args)); + args.d->show(); + }); - auto convertunix = new QAction(tr("Convert to Unix Endings"), this); - fileMenu->addAction(convertunix); - connect(convertunix, &QAction::triggered, [=]() - { - s.convertEols(QsciScintilla::EolUnix); - s.setEolVisibility(false); - }); - auto convertdos = new QAction(tr("Convert to DOS Endings"), this); - fileMenu->addAction(convertdos); - connect(convertdos, &QAction::triggered, [=]() - { - s.convertEols(QsciScintilla::EolWindows); - s.setEolVisibility(false); - }); + auto quit = new QAction(tr("Quit"), this); + fileMenu->addAction(quit); + connect(quit, &QAction::triggered, [=]() + { + this->close(); + }); + } + { + auto editMenu = menuBar()->addMenu(tr("&Edit")); - auto quit = new QAction(tr("Quit"), this); - fileMenu->addAction(quit); - connect(quit, &QAction::triggered, [=]() - { - this->close(); - }); + //undo + //redo + auto godef = new QAction(tr("Go To Definition"), this); + editMenu->addAction(godef); + godef->setShortcuts(QKeySequence::listFromString("F12")); + connect(godef, &QAction::triggered, [=]() + { + struct grepargs_s + { + guimainwindow *mw; + QDialog *d; + QLineEdit *t; + } args = {this, new QDialog()}; + args.t = new QLineEdit(this->s.selectedText()); + auto l = new QVBoxLayout; + l->addWidget(args.t); + args.d->setLayout(l); + args.d->setWindowTitle("Go To Definition"); + args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + { + GoToDefinition(ctx.t->text().toUtf8().data()); + ctx.d->done(0); + }, args)); + args.d->show(); + }); - auto debugMenu = menuBar()->addMenu(tr("&Debug")); - auto debugrebuild = new QAction(tr("Rebuild"), this); - debugMenu->addAction(debugrebuild); - debugrebuild->setShortcuts(QKeySequence::listFromString("F7")); - connect(debugrebuild, &QAction::triggered, [=]() - { - RunCompiler("", false); - }); - auto debugsetnext = new QAction(tr("Set Next Statement"), this); - debugMenu->addAction(debugsetnext); - debugsetnext->setShortcuts(QKeySequence::listFromString("F8")); - connect(debugsetnext, &QAction::triggered, [=]() - { - //FIXME - }); - auto debugresume = new QAction(tr("Resume"), this); - debugMenu->addAction(debugresume); - debugresume->setShortcuts(QKeySequence::listFromString("F5")); - connect(debugresume, &QAction::triggered, [=]() - { - if (!DebuggerSendCommand("qcresume\n")) - DebuggerStart(); //unable to send? assume its not running. - }); - auto debugover = new QAction(tr("Step Over"), this); - debugMenu->addAction(debugover); - debugover->setShortcuts(QKeySequence::listFromString("F10")); - connect(debugover, &QAction::triggered, [=]() - { - if (!DebuggerSendCommand("qcstep over\n")) - DebuggerStart(); - }); - auto debuginto = new QAction(tr("Step Into"), this); - debugMenu->addAction(debuginto); - debuginto->setShortcuts(QKeySequence::listFromString("F11")); - connect(debuginto, &QAction::triggered, [=]() - { - if (!DebuggerSendCommand("qcstep into\n")) - DebuggerStart(); - }); - auto debugout = new QAction(tr("Step Out"), this); - debugMenu->addAction(debugout); - debugout->setShortcuts(QKeySequence::listFromString("Shift+F11")); - connect(debugout, &QAction::triggered, [=]() - { - if (!DebuggerSendCommand("qcstep out\n")) - DebuggerStart(); - }); - auto debugbreak = new QAction(tr("Toggle Breakpoint"), this); - debugMenu->addAction(debugbreak); - debugbreak->setShortcuts(QKeySequence::listFromString("F9")); - connect(debugbreak, &QAction::triggered, [=]() - { - docs.toggleBreak(); - }); + auto autocomplete = new QAction(tr("AutoComplete"), this); + editMenu->addAction(autocomplete); + autocomplete->setShortcuts(QKeySequence::listFromString("Ctrl+Space")); + connect(autocomplete, &QAction::triggered, [=]() + { + docs.autoComplete(); + }); + auto convertunix = new QAction(tr("Convert to Unix Endings"), this); + editMenu->addAction(convertunix); + connect(convertunix, &QAction::triggered, [=]() + { + s.convertEols(QsciScintilla::EolUnix); + s.setEolVisibility(false); + }); + auto convertdos = new QAction(tr("Convert to DOS Endings"), this); + editMenu->addAction(convertdos); + connect(convertdos, &QAction::triggered, [=]() + { + s.convertEols(QsciScintilla::EolWindows); + s.setEolVisibility(false); + }); + + //convert to utf-8 chars + //convert to Quake chars + } + { + auto debugMenu = menuBar()->addMenu(tr("&Debug")); + + auto debugrebuild = new QAction(tr("Rebuild"), this); + debugMenu->addAction(debugrebuild); + debugrebuild->setShortcuts(QKeySequence::listFromString("F7")); + connect(debugrebuild, &QAction::triggered, [=]() + { + RunCompiler("", false); + }); + auto debugsetnext = new QAction(tr("Set Next Statement"), this); + debugMenu->addAction(debugsetnext); + debugsetnext->setShortcuts(QKeySequence::listFromString("F8")); + connect(debugsetnext, &QAction::triggered, [=]() + { + docs.setNextStatement(); + }); + auto debugresume = new QAction(tr("Resume"), this); + debugMenu->addAction(debugresume); + debugresume->setShortcuts(QKeySequence::listFromString("F5")); + connect(debugresume, &QAction::triggered, [=]() + { + if (!DebuggerSendCommand("qcresume\n")) + DebuggerStart(); //unable to send? assume its not running. + }); + auto debugover = new QAction(tr("Step Over"), this); + debugMenu->addAction(debugover); + debugover->setShortcuts(QKeySequence::listFromString("F10")); + connect(debugover, &QAction::triggered, [=]() + { + if (!DebuggerSendCommand("qcstep over\n")) + DebuggerStart(); + }); + auto debuginto = new QAction(tr("Step Into"), this); + debugMenu->addAction(debuginto); + debuginto->setShortcuts(QKeySequence::listFromString("F11")); + connect(debuginto, &QAction::triggered, [=]() + { + if (!DebuggerSendCommand("qcstep into\n")) + DebuggerStart(); + }); + auto debugout = new QAction(tr("Step Out"), this); + debugMenu->addAction(debugout); + debugout->setShortcuts(QKeySequence::listFromString("Shift+F11")); + connect(debugout, &QAction::triggered, [=]() + { + if (!DebuggerSendCommand("qcstep out\n")) + DebuggerStart(); + }); + auto debugbreak = new QAction(tr("Toggle Breakpoint"), this); + debugMenu->addAction(debugbreak); + debugbreak->setShortcuts(QKeySequence::listFromString("F9")); + connect(debugbreak, &QAction::triggered, [=]() + { + docs.toggleBreak(); + }); + } } public: @@ -1597,6 +1927,52 @@ public: } } *mainwnd; + +void WrappedQsciScintilla::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = createStandardContextMenu(); + static char blah[256]; + + if (*WordUnderCursor(blah, sizeof(blah), NULL, 0, -3)) + { + menu->addSeparator(); + connect(menu->addAction(tr("Go to definition")), &QAction::triggered, [=]() + { + GoToDefinition(blah); + }); + connect(menu->addAction(tr("Find Usages")), &QAction::triggered, [=]() + { + mainwnd->files.GrepAll(blah); + }); + } + + menu->addSeparator(); + connect(menu->addAction(tr("Toggle Breakpoint")), &QAction::triggered, [=]() + { + mainwnd->docs.toggleBreak(); + }); + if (qcdebugger) + { + connect(menu->addAction(tr("Set Next Statement")), &QAction::triggered, [=]() + { + mainwnd->docs.setNextStatement(); + }); + connect(menu->addAction(tr("Resume")), &QAction::triggered, [=]() + { + }); + } + else + { + connect(menu->addAction(tr("Start Debugging")), &QAction::triggered, [=]() + { + DebuggerStart(); + }); + } + + menu->exec(event->globalPos()); + delete menu; +} + //called when progssrcname has changed. //progssrcname should already have been set. void UpdateFileList(void) @@ -2043,7 +2419,6 @@ int GUIFileSize(const char *fname) -static QProcess *qcdebugger; static void DebuggerStop(void) { if (qcdebugger) diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index a306d2f8..c7a8d1e1 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -201,6 +201,40 @@ void GoToDefinition(const char *name) } } +pbool GenAutoCompleteList(char *prefix, char *buffer, int buffersize) +{ + QCC_def_t *def; + int prefixlen = strlen(prefix); + int usedbuffer = 0; + int l; + int fno; + for (fno = 0; fno < sourcefilesnumdefs; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope) + continue; //ignore locals, because we don't know where we are, and they're probably irrelevent. + + //make sure it has the right prefix + if (!strncmp(def->name, prefix, prefixlen)) + //but ignore it if its one of those special things that you're not meant to know about. + if (strcmp(def->name, "IMMEDIATE") && !strchr(def->name, ':') && !strchr(def->name, '.') && !strchr(def->name, '*') && !strchr(def->name, '[')) + { + l = strlen(def->name); + if (l && usedbuffer+2+l < buffersize) + { + if (usedbuffer) + buffer[usedbuffer++] = ' '; + memcpy(buffer+usedbuffer, def->name, l); + usedbuffer += l; + } + } + } + } + buffer[usedbuffer] = 0; + return usedbuffer>0; +} + static void GUI_WriteConfigLine(FILE *file, char *part1, char *part2, char *part3, char *desc) { int align = 0;