From 57093ed70ce195e97c3b9a9febe223a1269f465b Mon Sep 17 00:00:00 2001 From: ac-stef Date: Fri, 23 Jun 2017 12:48:17 +0200 Subject: [PATCH] rewrite doc command parser - properly break statements into words - pick statement where cursor is - detect unmatched braces + indicator - fix huge memory leak - add cs ident details - add debug view (broken into words) - menu entries for the settings docidentverbose 0|1|2|3 enables ident details or statement analyser DOCREF docidentverbose --- config/docs.cfg | 94 ++++---- config/menus_settings.cfg | 3 + source/src/command.cpp | 7 +- source/src/command.h | 1 + source/src/console.cpp | 3 +- source/src/docs.cpp | 468 ++++++++++++++++++-------------------- source/src/protos.h | 2 +- source/src/renderhud.cpp | 2 +- 8 files changed, 284 insertions(+), 296 deletions(-) diff --git a/config/docs.cfg b/config/docs.cfg index 43b39e7e..fa6474e1 100644 --- a/config/docs.cfg +++ b/config/docs.cfg @@ -3454,40 +3454,40 @@ docremark [It returns empty string, if no account exists or it is unusable (due docref [authsetup]; docsection [Game modes] docident [bdm] [Starts a map with the mode "Bot Deathmatch".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [bdm ac_complex] []; docident [blss] [Starts a map with the mode "Bot Last Swiss Standing".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [blss ac_complex] []; docident [bosok] [Starts a map with the mode "Bot One Shot, One Kill".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [bosok ac_complex] []; docident [bpf] [Starts a map with the mode "Bot Pistol Frenzy".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [bpf ac_complex] []; docident [btdm] [Starts a map with the mode "Bot Team Deathmatch".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [btdm ac_complex] []; docident [btosok] [Starts a map with the mode "Bot Team One Shot, One Kill".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [btosok ac_complex] []; docident [btsurv] [Starts a map with the mode "Bot Team Survivor".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [btsurv ac_complex] []; docident [coop] [Starts a map with the mode "Co-operative Editing".]; -docargument [M] [The name of the map you wish to edit] [] [1]; +docargument [M] [The name of the map you wish to edit] [] [0]; docremark [See the "Co-operative map editing" section on the "Tips, tricks and advice" chapter of the map editing guide for more information.]; docexample [coop ac_newmap] []; docident [ctf] [Starts a map with the mode "Capture the Flag".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [ctf ac_mines] []; docident [curmodeattr] [Checks the current game mode for certain attributes.]; docargument [A] [attribute name] [] [0]; @@ -3496,8 +3496,8 @@ docref [mode]; docref [gamemode]; docref [getmode]; docident [dm] [Starts a map with the mode "Deathmatch".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [dm ac_complex] []; docident [gamemode] [Returns the number of current game mode.]; docexample [echo $gamemode] [Output: 5]; @@ -3518,20 +3518,20 @@ docref [modeacronyms]; docref [gamemode]; docref [curmodeattr]; docident [htf] [Starts a map with the mode "Hunt the Flag".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [htf ac_mines] []; docident [ktf] [Starts a map with the mode "Keep the Flag".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [ktf ac_mines] []; docident [lms] [Starts a map with the mode "Survivor". Some players prefer the name "Last Man Standing" for this mode.]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [lms ac_complex] []; docident [lss] [Starts a map with the mode "Last Swiss Standing".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [lss ac_complex] []; docident [mode] [Sets the gameplay mode to N for the next map loaded.]; docargument [N] [Team Deathmatch] [0] [0]; @@ -3602,48 +3602,48 @@ docexample [echo (modenum btosok)] [Output: 21]; docref [mode]; docref [modeacronyms]; docident [osok] [Starts a map with the mode "One Shot, One Kill".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [osok ac_complex] []; docident [pf] [Starts a map with the mode "Pistol Frenzy".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [pf ac_complex] []; docident [showmodedescriptions] [Enables or disables the showing of game mode descriptions on the console after map starts.]; docargument [B] [0 off, 1 on] [min 0/max 1/default 1]; docref [mode]; docref [gamemodedesc]; docident [surv] [Starts a map with the mode "Survivor".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 10 minutes if T is 0 or not specified)] [1..60] [0]; docexample [surv ac_complex] []; docident [tdm] [Starts a map with the mode "Team Deathmatch".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [tdm ac_complex] []; docident [tktf] [Starts a map with the mode "Team Keep the Flag".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [tktf ac_mines] []; docident [tlss] [Starts a map with the mode "Team Last Swiss Standing".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [tlss ac_complex] []; docident [tosok] [Starts a map with the mode "Team One Shot, One Kill".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [tosok ac_complex] []; docident [tpf] [Starts a map with the mode "Team Pistol Frenzy".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [tpf ac_complex] []; docident [tsurv] [Starts a map with the mode "Team Survivor".]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [tsurv ac_complex] []; docident [vip] [Starts a map with the mode "Hunt the Flag". Some players prefer the name "VIP" for this mode.]; -docargument [M] [The name of the map you wish to play] [] [1]; -docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [1]; +docargument [M] [The name of the map you wish to play] [] [0]; +docargument [T] [The time limit, in minutes (default 15 minutes if T is 0 or not specified)] [1..60] [0]; docexample [vip ac_mines] []; docsection [Editing configs] docident [fog] [Sets the fog distance.]; diff --git a/config/menus_settings.cfg b/config/menus_settings.cfg index 7005de72..dba8a349 100644 --- a/config/menus_settings.cfg +++ b/config/menus_settings.cfg @@ -527,6 +527,9 @@ menuitemslider "Mini-map background style: " 0 2 "$showmapbackdrop" ["Fully Tra menuitemslider "Menu map file sort order: " 0 -1 "$menufilesort_cgz" (listoptions menufilesortorders) [menufilesort_cgz = $arg1] menuitemslider "Menu demo file sort order: " 0 -1 "$menufilesort_dmo" (listoptions menufilesortorders) [menufilesort_dmo = $arg1] menuitem "Edit hook script \i\1" [ showmenu "Edit cubescript hook" ] +menuitemcheckbox "Show command reference/documentation for console input: " "$docvisible" [ docvisible $arg1 ] +menuitemcheckbox "Show related commands: " "$docrefvisible" [ docrefvisible $arg1 ] +menuitemradio "Show docs detail: " 0 3 "$docidentverbose" [ default +ident verbose/debug +always] [ docidentverbose $arg1 ] //// Main > Settings > Misc > Autodownload config //// diff --git a/source/src/command.cpp b/source/src/command.cpp index 41efef89..fffb24e6 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -447,7 +447,7 @@ char *conc(const char **w, int n, bool space) return res; } -VARN(numargs, _numargs, 25, 0, 0); +VARN(numargs, _numargs, MAXWORDS, 0, 0); void intret(int v) { @@ -487,7 +487,6 @@ char *executeret(const char *p) // all evaluation happens here, if(!p || !p[0]) return NULL; if(executionstack.length() > CSLIMIT_RECURSION) { cslimiterr("recursion depth"); return NULL; } executionstack.add(p); - const int MAXWORDS = 25; // limit, remove char *w[MAXWORDS], emptychar = '\0'; char *retval = NULL; #define setretval(v) { char *rv = v; if(rv) retval = rv; } @@ -977,7 +976,7 @@ COMMANDF(exec, "v", (char **args, int numargs) { defformatstring(buf)("%d", numargs - 1); push("execnumargs", buf); - for(int i = 1; i < 24; i++) // MAXWORDS - 1 + for(int i = 1; i <= MAXWORDS - 2; i++) { formatstring(buf)("execarg%d", i); push(buf, i < numargs ? args[i] : ""); @@ -987,7 +986,7 @@ COMMANDF(exec, "v", (char **args, int numargs) const char *res = getalias("execresult"); if(res) result(res); pop("execresult"); - for(int i = 23; i > 0; i--) + for(int i = MAXWORDS - 2; i >= 1; i--) { formatstring(buf)("execarg%d", i); pop(buf); diff --git a/source/src/command.h b/source/src/command.h index 04711662..d7803e1c 100644 --- a/source/src/command.h +++ b/source/src/command.h @@ -107,4 +107,5 @@ enum { IEXC_CORE = 0, IEXC_CFG, IEXC_PROMPT, IEXC_MAPCFG, IEXC_MDLCFG, IEXC_NUM #define SVARFF(name, getb, checkb) extern char *name; void var_get##name() { getb; } void var_check##name() { checkb; } char *name = svariable(#name, "", &name, var_check##name, var_get##name, false) #define ATOI(s) strtol(s, NULL, 0) // supports hexadecimal numbers +#define MAXWORDS 25 diff --git a/source/src/console.cpp b/source/src/console.cpp index 35c9af98..02c3c5ff 100644 --- a/source/src/console.cpp +++ b/source/src/console.cpp @@ -608,8 +608,9 @@ void keypress(int code, bool isdown, int cooked, SDLMod mod) else exechook(HOOK_SP, "KEYRELEASE", "%d", code); } -char *getcurcommand() +char *getcurcommand(int *pos) { + if(pos && saycommandon) *pos = cmdline.pos; return saycommandon ? cmdline.buf : NULL; } diff --git a/source/src/docs.cpp b/source/src/docs.cpp index faf2bdf8..682cc066 100644 --- a/source/src/docs.cpp +++ b/source/src/docs.cpp @@ -32,19 +32,6 @@ struct docexample } }; -struct dockey -{ - char *alias, *name, *desc; - - dockey() : alias(NULL), name(NULL), desc(NULL) {} - ~dockey() - { - DELETEA(alias); - DELETEA(name); - DELETEA(desc); - } -}; - struct docident { char *name, *desc; @@ -52,13 +39,17 @@ struct docident vector remarks; vector references; vector examples; - vector keys; + vector keylines; + int a, b, c; + char *label, *related; - docident() : name(NULL), desc(NULL) {} + docident() : name(NULL), desc(NULL), label(NULL), related(NULL) {} ~docident() { DELETEA(name); DELETEA(desc); + DELETEA(label); + DELETEA(related); } }; @@ -145,10 +136,8 @@ COMMANDN(docexample, adddocexample, "ss"); void adddockey(char *alias, char *name, char *desc) { if(!lastident || !alias) return; - dockey &k = lastident->keys.add(); - k.alias = newstring(alias); - k.name = name && strlen(name) ? newstring(name) : NULL; - k.desc = desc && strlen(desc) ? newstring(desc) : NULL; + defformatstring(line)("~%-10s %s", *name ? name : alias, desc); + lastident->keylines.add(newstring(line)); } COMMANDN(dockey, adddockey, "sss"); @@ -400,260 +389,255 @@ COMMAND(docwritetodoref, "i"); VAR(docvisible, 0, 1, 1); VAR(docrefvisible, 0, 1, 1); VAR(docskip, 0, 0, 1000); +VAR(docidentverbose, 0, 1, 3); void toggledoc() { docvisible = !docvisible; } void scrolldoc(int i) { docskip += i; if(docskip < 0) docskip = 0; } -int numargs(char *args) +int docs_parsecmd(const char *p, const char *pos, const char *w[MAXWORDS], int wl[MAXWORDS], bool outer, int *unmatched) { - if(!args || !strlen(args)) return -1; - - int argidx = -1; - char *argstart = NULL; - - for(char *t = args; *t; t++) + vector brak; + for(;;) // loop statements { - if(!argstart && *t != ' ') { argstart = t; argidx++; } - else if(argstart && *t == ' ') if(t-1 >= args) + int nargs = MAXWORDS, curarg = -1; + loopi(MAXWORDS) { - switch(*argstart) - { - case '[': if(*(t-1) != ']') continue; break; - case '(': if(*(t-1) != ')') continue; break; - case '"': if(*(t-1) != '"') continue; break; - default: break; + w[i] = NULL; wl[i] = 0; + if(i > nargs) continue; + w[i] = (p += strspn(p, " \t")); // skip whitespace + if(p[0]=='/' && p[1]=='/') p += strcspn(p, "\n\r\0"); // skip comment + if(*p == '"') + { // find matching " + do + { + ++p; + p += strcspn(p, "\"\n\r"); + } while(*p == '\"' && p[-1] == '\\'); + p++; } - argstart = NULL; + else if(*p == '[' || *p == '(') + { // find matching ) or ] + bool quot = false; + char right = *p == '[' ? ']' : ')', left = *p++; + brak.add(p); + while(brak.length()) + { + p += strcspn(p, "([\"])"); + int c = *p++; + if(c == left && !quot) brak.add(p); + else if(c == '"') quot = !quot; + else if(c == right && !quot) brak.drop(); + else if(!c) + { // unmatched braces, interpret from there (in first pass) + *unmatched = right; + if(w[0] == brak.last() || brak.last() == p - 1 || outer) break; + p = brak.last(); + brak.setsize(0); + i = -1; + } + } + } + else p += strcspn(p, "; \t\n\r\0"); // skip regular word + if(i < 0) continue; + if(!(wl[i] = (int) (p - w[i]))) w[i] = NULL, nargs = i; + else if(w[i] <= pos) curarg = i - 1; } + p += strcspn(p, ";\n\r\0"); // skip statement delimiter + if(!*p++ || pos < p) return curarg; // was last statement, or the one we wanted } - return argidx; } void renderdoc(int x, int y, int doch) { if(!docvisible) return; + doch -= 3*FONTH + FONTH/2; - char *exp = getcurcommand(); - if(!exp || !*exp) return; - - int o = 0; //offset - int f = 0; //last found - - while (*exp) - {exp++; o++; if (*exp == ';' || (*exp == ' ' && f == o-1)) f = o;} exp--; - - if (f > 0) - { - for (int i = o - f - 1; i > 0; i--) exp--; - if (o > f + 1) exp++; - } - - else {for (int i = o; i > 1; i--) exp--;} - - char *openblock = strrchr(exp+1, '('); //find last open parenthesis - char *closeblock = strrchr(exp+1, ')'); //find last closed parenthesis - char *temp = NULL; - - if (openblock) - { - if (!closeblock || closeblock < openblock) //open block - temp = openblock + 1; - } - - if(!exp || (*exp != '/' && f == 0) || strlen(exp) < 2) return; - - char *c = exp+1; if (f > 0) c = exp; - char *d = NULL; if (temp) d = temp; - - size_t clen = strlen(c); - size_t dlen = 0; if (d) dlen = strlen(d); - - bool nc = false; //tests if text after open parenthesis is not a command + int cmdpos; + const char *exp = getcurcommand(&cmdpos); // get command buffer and cursor position + if(!exp || *exp != '/') return; + const char *pos = exp + (cmdpos < 0 ? strlen(exp) : cmdpos), *w[MAXWORDS]; + int wl[MAXWORDS], carg, unmatched = 0; docident *curident = NULL; - - for(size_t i = 0; i < clen; i++) // search first matching cmd doc by stripping arguments of exp from right to left + string buf; + loopi(2) // two scan passes, first one retries on unmatched braces { - char *end = c+clen-i; - if(!*end || *end == ' ') + carg = docs_parsecmd(exp + 1, pos, w, wl, i != 0, &unmatched); // break into words + if(!w[0]) return; + copystring(buf, w[0], wl[0] + 1); + curident = docidents.access(buf); // get doc entry + if(curident) break; + } + if(unmatched) draw_textf("\f3missing '%c'", x + 1111, y + doch + 2*FONTH, unmatched); + + ident *csident = idents->access(buf); // check for cs ident + + if(!curident && !csident && docidentverbose < 3) { docskip = 0; return; } + + vector doclines; + vector heaplines; + + if(curident) + { + if(!curident->label) { - string cmd; - string dmd; + formatstring(buf)("~%s", curident->name); // label + loopvj(curident->arguments) concatformatstring(buf, " %s", curident->arguments[j].token); + curident->label = newstring(buf); + } + doclines.add(curident->label); - copystring(cmd, c, clen-i+1); + doclines.add(NULL); + doclines.add(curident->desc); + doclines.add(NULL); - if (d && !nc && dlen > 1) + if(curident->arguments.length()) // args + { + if(carg >= curident->arguments.length() && curident->arguments.last().vararg) carg = curident->arguments.length() - 1; // vararg spans the line + + loopvj(curident->arguments) { - for(size_t j = 0; j < dlen; j++) //test text after parenthesis - { - char *dnd = d+dlen-j; - if(!*dnd || *dnd == ' ') - { - copystring(dmd, d, dlen-j+1); - curident = docidents.access(dmd); - } - if (j == dlen-1 && !curident) - nc = true; - } + docargument *a = &curident->arguments[j]; + formatstring(doclines.add(heaplines.add(newstringbuf())))("\f%c%-8s%s", j == carg ? '4' : '5', a->token, a->desc); + if(a->values) concatformatstring(heaplines.last(), " (%s)", a->values); } - else + doclines.add(NULL); + } + + if(curident->remarks.length()) // remarks + { + loopvj(curident->remarks) doclines.add(curident->remarks[j]); + doclines.add(NULL); + } + + if(curident->examples.length()) // examples + { + doclines.add(curident->examples.length() == 1 ? "Example:" : "Examples:"); + loopvj(curident->examples) { - nc = true; - curident = docidents.access(cmd); + doclines.add(curident->examples[j].code); + doclines.add(curident->examples[j].explanation); } + doclines.add(NULL); + } - if(curident) + if(curident->keylines.length()) // default keys + { + doclines.add(curident->keylines.length() == 1 ? "Default key:" : "Default keys:"); + loopvj(curident->keylines) doclines.add(curident->keylines[j]); // stored preformatted + doclines.add(NULL); + } + + if(docrefvisible && curident->references.length()) // references + { + if(!curident->related) { - vector doclines; - - char *label = newstringbuf(); // label - doclines.add(label); - formatstring(label)("~%s", curident->name); - loopvj(curident->arguments) - { - concatstring(label, " "); - concatstring(label, curident->arguments[j].token); - } - doclines.add(NULL); - - doclines.add(curident->desc); - doclines.add(NULL); - - if(curident->arguments.length() > 0) // args - { - extern textinputbuffer cmdline; - - if (d && dlen > 1) c = d; - char *args = strchr(c, ' '); - - int arg = -1; - - if(args) - { - args++; - if(cmdline.pos >= 0) - { - if(cmdline.pos >= args-c) - { - string a; - copystring(a, args, cmdline.pos-(args-c)+1); - args = a; - arg = numargs(args); - } - } - else arg = numargs(args); - - if(arg >= 0) // multipart idents need a fixed argument offset - { - char *c = cmd; - if (!nc) c = dmd; - while((c = strchr(c, ' ')) && c++) arg--; - } - - // fixes offset for var args - if(arg >= curident->arguments.length() && curident->arguments.last().vararg) arg = curident->arguments.length() - 1; - } - - loopvj(curident->arguments) - { - docargument *a = &curident->arguments[j]; - if(!a) continue; - formatstring(doclines.add(newstringbuf()))("\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc, - a->values ? "(" : "", a->values ? a->values : "", a->values ? ")" : ""); - } - - doclines.add(NULL); - } - - if(curident->remarks.length()) // remarks - { - loopvj(curident->remarks) doclines.add(curident->remarks[j]); - doclines.add(NULL); - } - - if(curident->examples.length()) // examples - { - doclines.add(curident->examples.length() == 1 ? "Example:" : "Examples:"); - loopvj(curident->examples) - { - doclines.add(curident->examples[j].code); - doclines.add(curident->examples[j].explanation); - } - doclines.add(NULL); - } - - if(curident->keys.length()) // default keys - { - doclines.add(curident->keys.length() == 1 ? "Default key:" : "Default keys:"); - loopvj(curident->keys) - { - dockey &k = curident->keys[j]; - defformatstring(line)("~%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : ""); - doclines.add(newstring(line)); - } - doclines.add(NULL); - } - - if(docrefvisible && curident->references.length()) // references - { - string refs = ""; - loopvj(curident->references) concatformatstring(refs, ", %s", curident->references[j]); - formatstring(doclines.add(newstringbuf()))("Related identifiers:%s", refs + 1); - } - - while(doclines.length() && !doclines.last()) doclines.pop(); - - doch -= 3*FONTH + FONTH/2; - - int offset = min(docskip, doclines.length()-1), maxl = offset, cury = 0; - for(int j = offset; j < doclines.length(); j++) - { - const char *str = doclines[j]; - int width = 0, height = FONTH; - if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3); - if(cury + height > doch) break; - cury += height; - maxl = j+1; - } - - if(offset > 0 && maxl >= doclines.length()) - { - for(int j = offset-1; j >= 0; j--) - { - const char *str = doclines[j]; - int width = 0, height = FONTH; - if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3); - if(cury + height > doch) break; - cury += height; - offset = j; - } - } - - cury = y; - for(int j = offset; j < maxl; j++) - { - const char *str = doclines[j]; - if(str) - { - const char *text = *str=='~' ? str+1 : str; - draw_text(text, x, cury, 0xFF, 0xFF, 0xFF, 0xFF, -1, str==text ? VIRTW*4/3 : -1); - int width, height; - text_bounds(text, width, height, str==text ? VIRTW*4/3 : -1); - cury += height; - if(str!=text) delete[] str; - } - else cury += FONTH; - } - - if(docskip > offset) docskip = offset; - if(maxl < doclines.length()) draw_text("\f4more (F3)", x, y+doch); // footer - if(offset > 0) draw_text("\f4less (F2)", x, y+doch+FONTH); - draw_text("\f4disable doc reference (F1)", x, y+doch+2*FONTH); - return; + string refs = ""; + loopvj(curident->references) concatformatstring(refs, ", %s", curident->references[j]); + formatstring(buf)("Related identifiers:%s", refs + 1); + curident->related = newstring(buf); } - else docskip = 0; + doclines.add(curident->related); + doclines.add(NULL); } } + + if(csident && docidentverbose) // cs ident details + { + doclines.add("\f4Ident info:"); + formatstring(buf)("\fs\f1%s\fr: ", csident->name); + switch(csident->type) + { + case ID_VAR: + if(csident->maxval < csident->minval) concatformatstring(buf, "integer variable, current %d, read-only", *csident->storage.i); + else concatformatstring(buf, "integer variable, current %d, min %d, max %d, default %d", *csident->storage.i, csident->minval, csident->maxval, csident->defaultval); + break; + case ID_FVAR: + if(csident->maxvalf < csident->minvalf) concatformatstring(buf, "float variable, current %s, read-only", floatstr(*csident->storage.f)); + else concatformatstring(buf, "float variable, current %s, min %s, max %s, default %s", floatstr(*csident->storage.f), floatstr(csident->minvalf), floatstr(csident->maxvalf), floatstr(csident->defaultvalf)); + break; + case ID_SVAR: + concatstring(buf, "string variable"); + break; + case ID_COMMAND: + concatformatstring(buf, "builtin command%s", strchr("sif", *csident->sig) ? ", arguments:" : ""); + loopi(strlen(csident->sig)) switch(csident->sig[i]) + { + case 's': concatstring(buf, i == carg ? " \fs\f4string\fr": " string"); break; + case 'i': concatstring(buf, i == carg ? " \fs\f4int\fr" : " int"); break; + case 'f': concatstring(buf, i == carg ? " \fs\f4float\fr" : " float"); break; + case 'd': concatstring(buf, ", keybind-only"); break; + case 'v': concatstring(buf, ", argument list varies"); break; + case 'c': concatstring(buf, ", concatenates arguments with spaces"); break; + case 'w': concatstring(buf, ", concatenates arguments without spaces"); break; + } + break; + case ID_ALIAS: + concatformatstring(buf, "alias, %s", csident->istemp ? "temp" : (csident->isconst ? "const" : (csident->persist ? "persistent" : "non-persistent"))); + break; + } + doclines.add(heaplines.add(newstring(buf))); + doclines.add(NULL); + } + + if(docidentverbose >= 2) + { + doclines.add("\f4Cubescript statement broken into words:"); + int n; + loopi(MAXWORDS) if(w[i]) + { + if(i) formatstring(buf)("\fs\f4arg%d: \fr%n%s", i, &n, w[i]); + else formatstring(buf)("\fs\f4cmd: \fr%n%s", &n, w[i]); + doclines.add(heaplines.add(newstring(buf, n + wl[i]))); + } + } + + while(doclines.length() && !doclines.last()) doclines.pop(); + + int offset = min(docskip, doclines.length()-1), maxl = offset, cury = 0; + for(int j = offset; j < doclines.length(); j++) + { + const char *str = doclines[j]; + int width = 0, height = FONTH; + if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3); + if(cury + height > doch) break; + cury += height; + maxl = j+1; + } + + if(offset > 0 && maxl >= doclines.length()) + { + for(int j = offset-1; j >= 0; j--) + { + const char *str = doclines[j]; + int width = 0, height = FONTH; + if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3); + if(cury + height > doch) break; + cury += height; + offset = j; + } + } + + cury = y; + for(int j = offset; j < maxl; j++) + { + const char *str = doclines[j]; + if(str) + { + const char *text = *str=='~' ? str+1 : str; + draw_text(text, x, cury, 0xFF, 0xFF, 0xFF, 0xFF, -1, str==text ? VIRTW*4/3 : -1); + int width, height; + text_bounds(text, width, height, str==text ? VIRTW*4/3 : -1); + cury += height; + } + else cury += FONTH; + } + heaplines.deletearrays(); + + if(docskip > offset) docskip = offset; + if(maxl < doclines.length()) draw_text("\f4more (F3)", x, y+doch); // footer + if(offset > 0) draw_text("\f4less (F2)", x, y+doch+FONTH); + draw_text("\f4disable doc reference (F1)", x, y+doch+2*FONTH); } void *docmenu = NULL; diff --git a/source/src/protos.h b/source/src/protos.h index d0165971..04730379 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -127,7 +127,7 @@ extern vector *bootclientlog; extern void keypress(int code, bool isdown, int cooked, SDLMod mod = KMOD_NONE); extern int rendercommand(int x, int y, int w); extern void renderconsole(); -extern char *getcurcommand(); +extern char *getcurcommand(int *pos); extern char *addreleaseaction(const char *s); extern void savehistory(); extern void loadhistory(); diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index 09420a97..56d48617 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -835,7 +835,7 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat playerent *targetplayer = playerincrosshair(); bool menu = menuvisible(); - bool command = getcurcommand() ? true : false; + bool command = getcurcommand(NULL) ? true : false; bool reloading = lastmillis < p->weaponsel->reloading + p->weaponsel->info.reloadtime; if(p->state==CS_ALIVE || p->state==CS_EDITING) {