From 5afb0a02c5afef137a7f03ff1f03b3c58bfd2b4a Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Sat, 23 Apr 2016 01:43:29 +0300 Subject: [PATCH] console: command autocompletion --- console.d | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- render.d | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 109 insertions(+), 8 deletions(-) diff --git a/console.d b/console.d index 7aa35cb..924813a 100644 --- a/console.d +++ b/console.d @@ -667,14 +667,31 @@ version(contest_vars) unittest { } +void addName (string name) { + if (name.length == 0) return; + if (name !in cmdlist) { + import std.algorithm : sort; + //import std.range : array; + cmdlistSorted ~= name; + cmdlistSorted.sort; + } +} + + public void conRegVar(alias fn, T) (T aminv, T amaxv, string aname, string ahelp=null) if (isIntegral!(typeof(fn)) && isIntegral!T) { if (aname.length == 0) aname = (&fn).stringof[2..$]; // HACK - if (aname.length > 0) cmdlist[aname] = new ConVar!(typeof(fn))(&fn, cast(typeof(fn))aminv, cast(typeof(fn))amaxv, aname, ahelp); + if (aname.length > 0) { + addName(aname); + cmdlist[aname] = new ConVar!(typeof(fn))(&fn, cast(typeof(fn))aminv, cast(typeof(fn))amaxv, aname, ahelp); + } } public void conRegVar(alias fn) (string aname, string ahelp=null) if (!isCallable!(typeof(fn))) { if (aname.length == 0) aname = (&fn).stringof[2..$]; // HACK - if (aname.length > 0) cmdlist[aname] = new ConVar!(typeof(fn))(&fn, aname, ahelp); + if (aname.length > 0) { + addName(aname); + cmdlist[aname] = new ConVar!(typeof(fn))(&fn, aname, ahelp); + } } @@ -750,12 +767,36 @@ public void conRegFunc(alias fn) (string aname, string ahelp=null) if (isCallabl static if (is(typeof(&fn))) { if (aname.length == 0) aname = (&fn).stringof[2..$]; // HACK } - if (aname.length > 0) cmdlist[aname] = new ConFunc(aname, ahelp); + if (aname.length > 0) { + addName(aname); + cmdlist[aname] = new ConFunc(aname, ahelp); + } } // ////////////////////////////////////////////////////////////////////////// // __gshared ConCommand[string] cmdlist; +__gshared string[] cmdlistSorted; + + +// ////////////////////////////////////////////////////////////////////////// // +public bool conHasCommand (const(char)[] name) { pragma(inline, true); return ((name in cmdlist) !is null); } + +public auto conByCommand () { + static struct Range { + private: + usize idx; + + public: + @property bool empty() () { pragma(inline, true); return (idx >= cmdlistSorted.length); } + @property string front() () { pragma(inline, true); return (idx < cmdlistSorted.length ? cmdlistSorted.ptr[idx] : null); } + @property bool frontIsVar() () { pragma(inline, true); return (idx < cmdlistSorted.length ? (cast(ConVarBase)cmdlist[cmdlistSorted.ptr[idx]] !is null) : false); } + @property bool frontIsFunc() () { pragma(inline, true); return (idx < cmdlistSorted.length ? (cast(ConFuncBase)cmdlist[cmdlistSorted.ptr[idx]] !is null) : false); } + void popFront () { pragma(inline, true); if (idx < cmdlistSorted.length) ++idx; } + } + Range res; + return res; +} // ////////////////////////////////////////////////////////////////////////// // @@ -960,6 +1001,7 @@ public class ConCommandEcho : ConCommand { shared static this () { + addName("echo"); cmdlist["echo"] = new ConCommandEcho(); } @@ -1056,3 +1098,15 @@ version(contest_echo) unittest { conwriteln("[", s, "]"); } } + + +version(contest_cmdlist) unittest { + auto cl = conByCommand; + while (!cl.empty) { + if (cl.frontIsVar) conwrite("VAR "); + else if (cl.frontIsFunc) conwrite("FUNC "); + else conwrite("UNK "); + conwriteln("[", cl.front, "]"); + cl.popFront(); + } +} diff --git a/render.d b/render.d index 27ae875..7487c42 100644 --- a/render.d +++ b/render.d @@ -134,13 +134,63 @@ void concmdDoAll () { void concliChar (char ch) { + __gshared int prevWasEmptyAndTab = 0; + concmdbufLock.lock(); scope(exit) concmdbufLock.unlock(); - conLastChange = 0; + //conLastChange = 0; + + // autocomplete + if (ch == 9) { + if (conclilen == 0) { + if (++prevWasEmptyAndTab < 2) return; + } else { + prevWasEmptyAndTab = 0; + } + if (conclilen > 0) { + string minPfx = null; + // find longest command + foreach (auto name; conByCommand) { + if (name.length >= conclilen && name.length > minPfx.length && name[0..conclilen] == concli[0..conclilen]) minPfx = name; + } + //conwriteln("longest command: [", minPfx, "]"); + // find longest prefix + foreach (auto name; conByCommand) { + if (name.length < conclilen) continue; + if (name[0..conclilen] != concli[0..conclilen]) continue; + usize pos = 0; + while (pos < name.length && pos < minPfx.length && minPfx.ptr[pos] == name.ptr[pos]) ++pos; + if (pos < minPfx.length) minPfx = minPfx[0..pos]; + } + if (minPfx.length > concli.length) minPfx = minPfx[0..concli.length]; + //conwriteln("longest prefix : [", minPfx, "]"); + if (minPfx.length >= conclilen) { + // wow! + concli[0..minPfx.length] = minPfx[]; + conclilen = cast(uint)minPfx.length; + if (conclilen < concli.length && conHasCommand(minPfx)) concli.ptr[conclilen++] = ' '; + conLastChange = 0; + return; + } + } + // nope, print all available commands + foreach (auto name; conByCommand) { + if (conclilen > 0) { + if (name.length < conclilen) continue; + if (name[0..conclilen] != concli[0..conclilen]) continue; + } + conwriteln(name); + } + return; + } + // process other keys + prevWasEmptyAndTab = 0; + // remove last char if (ch == 8) { if (conclilen > 0) { conLastChange = 0; --conclilen; } return; } + // execute command if (ch == 13) { if (conclilen > 0) { conLastChange = 0; @@ -151,10 +201,7 @@ void concliChar (char ch) { } // ^Y if (ch == 25) { - if (conclilen > 0) { - conLastChange = 0; - conclilen = 0; - } + if (conclilen > 0) { conLastChange = 0; conclilen = 0; } return; } if (ch < ' ' || ch > 127) return; -- 2.11.4.GIT