mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-15 00:41:32 +00:00
323 lines
7.5 KiB
C++
323 lines
7.5 KiB
C++
|
/*
|
||
|
** c_tabcomplete.cpp
|
||
|
** Tab completion code
|
||
|
**
|
||
|
**---------------------------------------------------------------------------
|
||
|
** Copyright 1998-2006 Randy Heit
|
||
|
** All rights reserved.
|
||
|
**
|
||
|
** Redistribution and use in source and binary forms, with or without
|
||
|
** modification, are permitted provided that the following conditions
|
||
|
** are met:
|
||
|
**
|
||
|
** 1. Redistributions of source code must retain the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer.
|
||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer in the
|
||
|
** documentation and/or other materials provided with the distribution.
|
||
|
** 3. The name of the author may not be used to endorse or promote products
|
||
|
** derived from this software without specific prior written permission.
|
||
|
**
|
||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
**---------------------------------------------------------------------------
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
#include "name.h"
|
||
|
#include "tarray.h"
|
||
|
#include "zstring.h"
|
||
|
#include "c_commandbuffer.h"
|
||
|
#include "c_cvars.h"
|
||
|
#include "c_tabcomplete.h"
|
||
|
#include "printf.h"
|
||
|
#include "c_dispatch.h"
|
||
|
#include "v_draw.h"
|
||
|
#include <string.h>
|
||
|
|
||
|
|
||
|
|
||
|
struct TabData
|
||
|
{
|
||
|
int UseCount;
|
||
|
FName TabName;
|
||
|
|
||
|
TabData()
|
||
|
: UseCount(0), TabName(NAME_None)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
TabData(const char *name)
|
||
|
: UseCount(1), TabName(name)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
TabData(const TabData &other) = default;
|
||
|
};
|
||
|
|
||
|
static TArray<TabData> TabCommands (TArray<TabData>::NoInit);
|
||
|
static int TabPos; // Last TabCommand tabbed to
|
||
|
static int TabStart; // First char in CmdLine to use for tab completion
|
||
|
static int TabSize; // Size of tab string
|
||
|
|
||
|
bool TabbedLast; // True if last key pressed was tab
|
||
|
bool TabbedList; // True if tab list was shown
|
||
|
CVAR(Bool, con_notablist, false, CVAR_ARCHIVE)
|
||
|
|
||
|
static bool FindTabCommand (const char *name, int *stoppos, int len)
|
||
|
{
|
||
|
FName aname(name);
|
||
|
unsigned int i;
|
||
|
int cval = 1;
|
||
|
|
||
|
for (i = 0; i < TabCommands.Size(); i++)
|
||
|
{
|
||
|
if (TabCommands[i].TabName == aname)
|
||
|
{
|
||
|
*stoppos = i;
|
||
|
return true;
|
||
|
}
|
||
|
cval = strnicmp (TabCommands[i].TabName.GetChars(), name, len);
|
||
|
if (cval >= 0)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*stoppos = i;
|
||
|
|
||
|
return (cval == 0);
|
||
|
}
|
||
|
|
||
|
void C_AddTabCommand (const char *name)
|
||
|
{
|
||
|
int pos;
|
||
|
|
||
|
if (FindTabCommand (name, &pos, INT_MAX))
|
||
|
{
|
||
|
TabCommands[pos].UseCount++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TabData tab(name);
|
||
|
TabCommands.Insert (pos, tab);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void C_RemoveTabCommand (const char *name)
|
||
|
{
|
||
|
if (TabCommands.Size() == 0)
|
||
|
{
|
||
|
// There are no tab commands that can be removed.
|
||
|
// This is important to skip construction of aname
|
||
|
// in case the NameManager has already been destroyed.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
FName aname(name, true);
|
||
|
|
||
|
if (aname == NAME_None)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
for (unsigned int i = 0; i < TabCommands.Size(); ++i)
|
||
|
{
|
||
|
if (TabCommands[i].TabName == aname)
|
||
|
{
|
||
|
if (--TabCommands[i].UseCount == 0)
|
||
|
{
|
||
|
TabCommands.Delete(i);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void C_ClearTabCommands ()
|
||
|
{
|
||
|
TabCommands.Clear();
|
||
|
}
|
||
|
|
||
|
static int FindDiffPoint (FName name1, const char *str2)
|
||
|
{
|
||
|
const char *str1 = name1.GetChars();
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; tolower(str1[i]) == tolower(str2[i]); i++)
|
||
|
if (str1[i] == 0 || str2[i] == 0)
|
||
|
break;
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
void C_TabComplete (bool goForward)
|
||
|
{
|
||
|
unsigned i;
|
||
|
int diffpoint;
|
||
|
|
||
|
auto CmdLineText = CmdLine.GetText();
|
||
|
if (!TabbedLast)
|
||
|
{
|
||
|
bool cancomplete;
|
||
|
|
||
|
|
||
|
// Skip any spaces at beginning of command line
|
||
|
for (i = 0; i < CmdLineText.Len(); ++i)
|
||
|
{
|
||
|
if (CmdLineText[i] != ' ')
|
||
|
break;
|
||
|
}
|
||
|
if (i == CmdLineText.Len())
|
||
|
{ // Line was nothing but spaces
|
||
|
return;
|
||
|
}
|
||
|
TabStart = i;
|
||
|
|
||
|
TabSize = (int)CmdLineText.Len() - TabStart;
|
||
|
|
||
|
if (!FindTabCommand(&CmdLineText[TabStart], &TabPos, TabSize))
|
||
|
return; // No initial matches
|
||
|
|
||
|
// Show a list of possible completions, if more than one.
|
||
|
if (TabbedList || con_notablist)
|
||
|
{
|
||
|
cancomplete = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cancomplete = C_TabCompleteList ();
|
||
|
TabbedList = true;
|
||
|
}
|
||
|
|
||
|
if (goForward)
|
||
|
{ // Position just before the list of completions so that when TabPos
|
||
|
// gets advanced below, it will be at the first one.
|
||
|
--TabPos;
|
||
|
}
|
||
|
else
|
||
|
{ // Find the last matching tab, then go one past it.
|
||
|
while (++TabPos < (int)TabCommands.Size())
|
||
|
{
|
||
|
if (FindDiffPoint(TabCommands[TabPos].TabName, &CmdLineText[TabStart]) < TabSize)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
TabbedLast = true;
|
||
|
if (!cancomplete)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((goForward && ++TabPos == (int)TabCommands.Size()) ||
|
||
|
(!goForward && --TabPos < 0))
|
||
|
{
|
||
|
TabbedLast = false;
|
||
|
CmdLineText.Truncate(TabSize);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
diffpoint = FindDiffPoint(TabCommands[TabPos].TabName, &CmdLineText[TabStart]);
|
||
|
|
||
|
if (diffpoint < TabSize)
|
||
|
{
|
||
|
// No more matches
|
||
|
TabbedLast = false;
|
||
|
CmdLineText.Truncate(TabSize - TabStart);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CmdLineText.Truncate(TabStart);
|
||
|
CmdLineText << TabCommands[TabPos].TabName.GetChars() << ' ';
|
||
|
}
|
||
|
}
|
||
|
CmdLine.SetString(CmdLineText);
|
||
|
CmdLine.MakeStartPosGood();
|
||
|
}
|
||
|
|
||
|
bool C_TabCompleteList ()
|
||
|
{
|
||
|
int nummatches, i;
|
||
|
size_t maxwidth;
|
||
|
int commonsize = INT_MAX;
|
||
|
|
||
|
nummatches = 0;
|
||
|
maxwidth = 0;
|
||
|
|
||
|
auto CmdLineText = CmdLine.GetText();
|
||
|
for (i = TabPos; i < (int)TabCommands.Size(); ++i)
|
||
|
{
|
||
|
if (FindDiffPoint (TabCommands[i].TabName, &CmdLineText[TabStart]) < TabSize)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (i > TabPos)
|
||
|
{
|
||
|
// This keeps track of the longest common prefix for all the possible
|
||
|
// completions, so we can fill in part of the command for the user if
|
||
|
// the longest common prefix is longer than what the user already typed.
|
||
|
int diffpt = FindDiffPoint (TabCommands[i-1].TabName, TabCommands[i].TabName.GetChars());
|
||
|
if (diffpt < commonsize)
|
||
|
{
|
||
|
commonsize = diffpt;
|
||
|
}
|
||
|
}
|
||
|
nummatches++;
|
||
|
maxwidth = std::max (maxwidth, strlen (TabCommands[i].TabName.GetChars()));
|
||
|
}
|
||
|
}
|
||
|
if (nummatches > 1)
|
||
|
{
|
||
|
size_t x = 0;
|
||
|
maxwidth += 3;
|
||
|
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLineText.GetChars());
|
||
|
for (i = TabPos; nummatches > 0; ++i, --nummatches)
|
||
|
{
|
||
|
// [Dusk] Print console commands blue, CVars green, aliases red.
|
||
|
const char* colorcode = "";
|
||
|
FConsoleCommand* ccmd;
|
||
|
if (FindCVar (TabCommands[i].TabName.GetChars(), NULL))
|
||
|
colorcode = TEXTCOLOR_GREEN;
|
||
|
else if ((ccmd = FConsoleCommand::FindByName (TabCommands[i].TabName.GetChars())) != NULL)
|
||
|
{
|
||
|
if (ccmd->IsAlias())
|
||
|
colorcode = TEXTCOLOR_RED;
|
||
|
else
|
||
|
colorcode = TEXTCOLOR_LIGHTBLUE;
|
||
|
}
|
||
|
|
||
|
Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars());
|
||
|
x += maxwidth;
|
||
|
if (x > CmdLine.ConCols / active_con_scale(twod) - maxwidth)
|
||
|
{
|
||
|
x = 0;
|
||
|
Printf ("\n");
|
||
|
}
|
||
|
}
|
||
|
if (x != 0)
|
||
|
{
|
||
|
Printf ("\n");
|
||
|
}
|
||
|
// Fill in the longest common prefix, if it's longer than what was typed.
|
||
|
if (TabSize != commonsize)
|
||
|
{
|
||
|
TabSize = commonsize;
|
||
|
CmdLineText.Truncate(TabStart);
|
||
|
CmdLineText.AppendCStrPart(TabCommands[TabPos].TabName.GetChars(), commonsize);
|
||
|
CmdLine.SetString(CmdLineText);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|